@parhelia/core 0.1.12554 → 0.1.12555

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 (575) 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 +98 -187
  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 +21 -21
  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 +2 -3
  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 +483 -2377
  120. package/dist/editor/ai/AgentTerminal.js.map +1 -1
  121. package/dist/editor/ai/AgentTerminalStatusBar.d.ts +3 -8
  122. package/dist/editor/ai/AgentTerminalStatusBar.js +56 -460
  123. package/dist/editor/ai/AgentTerminalStatusBar.js.map +1 -1
  124. package/dist/editor/ai/Agents.js +113 -150
  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 +23 -238
  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/GuidanceOverlay.js +11 -17
  133. package/dist/editor/ai/GuidanceOverlay.js.map +1 -1
  134. package/dist/editor/ai/HelpTerminal.d.ts +5 -0
  135. package/dist/editor/ai/HelpTerminal.js +166 -0
  136. package/dist/editor/ai/HelpTerminal.js.map +1 -0
  137. package/dist/editor/ai/InlineAiDialog.d.ts +1 -1
  138. package/dist/editor/ai/InlineAiDialog.js +192 -514
  139. package/dist/editor/ai/InlineAiDialog.js.map +1 -1
  140. package/dist/editor/ai/InlineAiTrigger.js +12 -115
  141. package/dist/editor/ai/InlineAiTrigger.js.map +1 -1
  142. package/dist/editor/ai/MediaImage.js +8 -40
  143. package/dist/editor/ai/MediaImage.js.map +1 -1
  144. package/dist/editor/ai/SpawnedAgentsPanel.js +12 -10
  145. package/dist/editor/ai/SpawnedAgentsPanel.js.map +1 -1
  146. package/dist/editor/ai/ToolCallDisplay.d.ts +2 -22
  147. package/dist/editor/ai/ToolCallDisplay.js +147 -518
  148. package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
  149. package/dist/editor/ai/dialogs/AgentDialogHandler.d.ts +8 -1
  150. package/dist/editor/ai/dialogs/AgentDialogHandler.js +42 -379
  151. package/dist/editor/ai/dialogs/AgentDialogHandler.js.map +1 -1
  152. package/dist/editor/ai/dialogs/QuestionnaireInline.d.ts +1 -5
  153. package/dist/editor/ai/dialogs/QuestionnaireInline.js +60 -628
  154. package/dist/editor/ai/dialogs/QuestionnaireInline.js.map +1 -1
  155. package/dist/editor/ai/dialogs/agentDialogTypes.d.ts +0 -115
  156. package/dist/editor/ai/dialogs/agentDialogTypes.js +0 -2
  157. package/dist/editor/ai/dialogs/agentDialogTypes.js.map +1 -1
  158. package/dist/editor/ai/types.d.ts +1 -3
  159. package/dist/editor/ai/useAgentStatus.d.ts +1 -2
  160. package/dist/editor/ai/useAgentStatus.js +99 -86
  161. package/dist/editor/ai/useAgentStatus.js.map +1 -1
  162. package/dist/editor/ai/useInlineAiPosition.js +5 -45
  163. package/dist/editor/ai/useInlineAiPosition.js.map +1 -1
  164. package/dist/editor/client/AboutDialog.js +2 -4
  165. package/dist/editor/client/AboutDialog.js.map +1 -1
  166. package/dist/editor/client/EditorShell.d.ts +1 -4
  167. package/dist/editor/client/EditorShell.js +230 -730
  168. package/dist/editor/client/EditorShell.js.map +1 -1
  169. package/dist/editor/client/editContext.d.ts +19 -33
  170. package/dist/editor/client/editContext.js.map +1 -1
  171. package/dist/editor/client/helpers.js +0 -6
  172. package/dist/editor/client/helpers.js.map +1 -1
  173. package/dist/editor/client/hooks/useEditorUrlSync.js +2 -1
  174. package/dist/editor/client/hooks/useEditorUrlSync.js.map +1 -1
  175. package/dist/editor/client/hooks/useEditorWebSocket.d.ts +0 -10
  176. package/dist/editor/client/hooks/useEditorWebSocket.js +14 -209
  177. package/dist/editor/client/hooks/useEditorWebSocket.js.map +1 -1
  178. package/dist/editor/client/hooks/useQuota.d.ts +0 -8
  179. package/dist/editor/client/hooks/useQuota.js.map +1 -1
  180. package/dist/editor/client/hooks/useSocketMessageHandler.js +7 -68
  181. package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
  182. package/dist/editor/client/itemsRepository.js +6 -10
  183. package/dist/editor/client/itemsRepository.js.map +1 -1
  184. package/dist/editor/client/operations.d.ts +3 -6
  185. package/dist/editor/client/operations.js +30 -208
  186. package/dist/editor/client/operations.js.map +1 -1
  187. package/dist/editor/client/pageModelBuilder.js +31 -4
  188. package/dist/editor/client/pageModelBuilder.js.map +1 -1
  189. package/dist/editor/client/ui/DevModeIndicator.js +2 -2
  190. package/dist/editor/client/ui/DevModeIndicator.js.map +1 -1
  191. package/dist/editor/client/ui/EditorChrome.d.ts +6 -0
  192. package/dist/editor/client/ui/EditorChrome.js +72 -55
  193. package/dist/editor/client/ui/EditorChrome.js.map +1 -1
  194. package/dist/editor/client/ui/FullscreenControls.js +3 -5
  195. package/dist/editor/client/ui/FullscreenControls.js.map +1 -1
  196. package/dist/editor/commands/commands.d.ts +1 -11
  197. package/dist/editor/commands/commands.js +1 -12
  198. package/dist/editor/commands/commands.js.map +1 -1
  199. package/dist/editor/commands/componentCommands.js +55 -109
  200. package/dist/editor/commands/componentCommands.js.map +1 -1
  201. package/dist/editor/commands/customCommandConverter.d.ts +1 -8
  202. package/dist/editor/commands/customCommandConverter.js +5 -35
  203. package/dist/editor/commands/customCommandConverter.js.map +1 -1
  204. package/dist/editor/commands/handlers/agentHandler.js +1 -2
  205. package/dist/editor/commands/handlers/agentHandler.js.map +1 -1
  206. package/dist/editor/commands/itemCommands.d.ts +0 -3
  207. package/dist/editor/commands/itemCommands.js +10 -93
  208. package/dist/editor/commands/itemCommands.js.map +1 -1
  209. package/dist/editor/commands/undo.d.ts +15 -9
  210. package/dist/editor/commands/undo.js +0 -24
  211. package/dist/editor/commands/undo.js.map +1 -1
  212. package/dist/editor/context-menu/InsertMenu.js +39 -83
  213. package/dist/editor/context-menu/InsertMenu.js.map +1 -1
  214. package/dist/editor/field-types/MultiLineText.js +1 -1
  215. package/dist/editor/field-types/MultiLineText.js.map +1 -1
  216. package/dist/editor/field-types/RawEditor.js +1 -1
  217. package/dist/editor/field-types/ReactQuill.d.ts +125 -0
  218. package/dist/editor/field-types/ReactQuill.js +385 -0
  219. package/dist/editor/field-types/ReactQuill.js.map +1 -0
  220. package/dist/editor/field-types/RichTextEditor.js +5 -13
  221. package/dist/editor/field-types/RichTextEditor.js.map +1 -1
  222. package/dist/editor/field-types/RichTextEditorComponent.js +3 -37
  223. package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
  224. package/dist/editor/field-types/SingleLineText.js +1 -1
  225. package/dist/editor/field-types/TreeListEditor.js +2 -3
  226. package/dist/editor/field-types/TreeListEditor.js.map +1 -1
  227. package/dist/editor/field-types/richtext/components/ReactSlate.css +5 -23
  228. package/dist/editor/field-types/richtext/components/ReactSlate.d.ts +0 -2
  229. package/dist/editor/field-types/richtext/components/ReactSlate.js +4 -28
  230. package/dist/editor/field-types/richtext/components/ReactSlate.js.map +1 -1
  231. package/dist/editor/field-types/richtext/components/ToolbarButton.js +2 -4
  232. package/dist/editor/field-types/richtext/components/ToolbarButton.js.map +1 -1
  233. package/dist/editor/field-types/richtext/contextMenuFactory.d.ts +0 -13
  234. package/dist/editor/field-types/richtext/contextMenuFactory.js +24 -181
  235. package/dist/editor/field-types/richtext/contextMenuFactory.js.map +1 -1
  236. package/dist/editor/field-types/richtext/types.d.ts +0 -2
  237. package/dist/editor/field-types/richtext/types.js.map +1 -1
  238. package/dist/editor/field-types/richtext/utils/plugins.js +0 -4
  239. package/dist/editor/field-types/richtext/utils/plugins.js.map +1 -1
  240. package/dist/editor/field-types/textContextMenuFactory.js +2 -3
  241. package/dist/editor/field-types/textContextMenuFactory.js.map +1 -1
  242. package/dist/editor/media-selector/AiImageSearchPrompt.js +2 -4
  243. package/dist/editor/media-selector/AiImageSearchPrompt.js.map +1 -1
  244. package/dist/editor/media-selector/MediaFolderBrowser.js +1 -1
  245. package/dist/editor/media-selector/MediaFolderBrowser.js.map +1 -1
  246. package/dist/editor/media-selector/MediaSelector.js +1 -7
  247. package/dist/editor/media-selector/MediaSelector.js.map +1 -1
  248. package/dist/editor/media-selector/TreeSelector.js +35 -40
  249. package/dist/editor/media-selector/TreeSelector.js.map +1 -1
  250. package/dist/editor/menubar/ActiveUsers.js +1 -1
  251. package/dist/editor/menubar/ActiveUsers.js.map +1 -1
  252. package/dist/editor/menubar/GenericToolbar.js +2 -4
  253. package/dist/editor/menubar/GenericToolbar.js.map +1 -1
  254. package/dist/editor/menubar/ItemLanguageVersion.js +2 -2
  255. package/dist/editor/menubar/ItemLanguageVersion.js.map +1 -1
  256. package/dist/editor/menubar/PageSelector.js +147 -26
  257. package/dist/editor/menubar/PageSelector.js.map +1 -1
  258. package/dist/editor/menubar/Separator.js +1 -1
  259. package/dist/editor/menubar/VersionSelector.js +4 -2
  260. package/dist/editor/menubar/VersionSelector.js.map +1 -1
  261. package/dist/editor/menubar/WorkflowButton.js +12 -39
  262. package/dist/editor/menubar/WorkflowButton.js.map +1 -1
  263. package/dist/editor/menubar/toolbar-sections/CustomCommandsToolbar.js +38 -16
  264. package/dist/editor/menubar/toolbar-sections/CustomCommandsToolbar.js.map +1 -1
  265. package/dist/editor/menubar/toolbar-sections/EditControls.js +3 -3
  266. package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -1
  267. package/dist/editor/menubar/toolbar-sections/HelpButton.js +0 -1
  268. package/dist/editor/menubar/toolbar-sections/HelpButton.js.map +1 -1
  269. package/dist/editor/menubar/toolbar-sections/ManualBrowser.d.ts +10 -6
  270. package/dist/editor/menubar/toolbar-sections/ManualBrowser.js +220 -597
  271. package/dist/editor/menubar/toolbar-sections/ManualBrowser.js.map +1 -1
  272. package/dist/editor/menubar/toolbar-sections/UtilityControls.js +2 -13
  273. package/dist/editor/menubar/toolbar-sections/UtilityControls.js.map +1 -1
  274. package/dist/editor/page-editor-chrome/CommentHighlighting.js +1 -42
  275. package/dist/editor/page-editor-chrome/CommentHighlighting.js.map +1 -1
  276. package/dist/editor/page-editor-chrome/FrameMenu.js +1 -1
  277. package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
  278. package/dist/editor/page-editor-chrome/InlineEditor.js +48 -97
  279. package/dist/editor/page-editor-chrome/InlineEditor.js.map +1 -1
  280. package/dist/editor/page-editor-chrome/PlaceholderDropZone.js +17 -38
  281. package/dist/editor/page-editor-chrome/PlaceholderDropZone.js.map +1 -1
  282. package/dist/editor/page-editor-chrome/PlaceholderDropZones.js +11 -17
  283. package/dist/editor/page-editor-chrome/PlaceholderDropZones.js.map +1 -1
  284. package/dist/editor/page-editor-chrome/useInlineAICompletion.js +301 -301
  285. package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +1 -1
  286. package/dist/editor/page-viewer/DeviceToolbar.js +1 -1
  287. package/dist/editor/page-viewer/DeviceToolbar.js.map +1 -1
  288. package/dist/editor/page-viewer/EditorForm.js +11 -69
  289. package/dist/editor/page-viewer/EditorForm.js.map +1 -1
  290. package/dist/editor/page-viewer/MiniMap.d.ts +4 -2
  291. package/dist/editor/page-viewer/MiniMap.js +28 -91
  292. package/dist/editor/page-viewer/MiniMap.js.map +1 -1
  293. package/dist/editor/page-viewer/PageViewer.d.ts +1 -3
  294. package/dist/editor/page-viewer/PageViewer.js +19 -92
  295. package/dist/editor/page-viewer/PageViewer.js.map +1 -1
  296. package/dist/editor/page-viewer/PageViewerFrame.d.ts +1 -2
  297. package/dist/editor/page-viewer/PageViewerFrame.js +115 -348
  298. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  299. package/dist/editor/page-viewer/pageModelSkeletonBuilder.js +49 -114
  300. package/dist/editor/page-viewer/pageModelSkeletonBuilder.js.map +1 -1
  301. package/dist/editor/page-viewer/pageViewContext.d.ts +0 -1
  302. package/dist/editor/page-viewer/pageViewContext.js +14 -51
  303. package/dist/editor/page-viewer/pageViewContext.js.map +1 -1
  304. package/dist/editor/pageModel.d.ts +1 -14
  305. package/dist/editor/reviews/Comment.js +12 -26
  306. package/dist/editor/reviews/Comment.js.map +1 -1
  307. package/dist/editor/reviews/CommentDisplayPopover.js +5 -7
  308. package/dist/editor/reviews/CommentDisplayPopover.js.map +1 -1
  309. package/dist/editor/reviews/CommentView.js +4 -19
  310. package/dist/editor/reviews/CommentView.js.map +1 -1
  311. package/dist/editor/reviews/Comments.js +72 -89
  312. package/dist/editor/reviews/Comments.js.map +1 -1
  313. package/dist/editor/reviews/CreateReviewDialog.js +177 -281
  314. package/dist/editor/reviews/CreateReviewDialog.js.map +1 -1
  315. package/dist/editor/reviews/DecisionsMatrix.js +25 -96
  316. package/dist/editor/reviews/DecisionsMatrix.js.map +1 -1
  317. package/dist/editor/reviews/DiffView.js +14 -7
  318. package/dist/editor/reviews/DiffView.js.map +1 -1
  319. package/dist/editor/reviews/EditReviewSettingsDialog.js +4 -6
  320. package/dist/editor/reviews/EditReviewSettingsDialog.js.map +1 -1
  321. package/dist/editor/reviews/MultiReviewManager.js +3 -25
  322. package/dist/editor/reviews/MultiReviewManager.js.map +1 -1
  323. package/dist/editor/reviews/PagesPanel.js +15 -31
  324. package/dist/editor/reviews/PagesPanel.js.map +1 -1
  325. package/dist/editor/reviews/PreviewInfo.js +4 -1
  326. package/dist/editor/reviews/PreviewInfo.js.map +1 -1
  327. package/dist/editor/reviews/ReviewCard.js +7 -13
  328. package/dist/editor/reviews/ReviewCard.js.map +1 -1
  329. package/dist/editor/reviews/ReviewDetail.js +2 -3
  330. package/dist/editor/reviews/ReviewDetail.js.map +1 -1
  331. package/dist/editor/reviews/ReviewsList.js +3 -7
  332. package/dist/editor/reviews/ReviewsList.js.map +1 -1
  333. package/dist/editor/reviews/SuggestedEdit.js +3 -34
  334. package/dist/editor/reviews/SuggestedEdit.js.map +1 -1
  335. package/dist/editor/reviews/SuggestionDisplayPopover.js +5 -31
  336. package/dist/editor/reviews/SuggestionDisplayPopover.js.map +1 -1
  337. package/dist/editor/reviews/commentAi.js +6 -25
  338. package/dist/editor/reviews/commentAi.js.map +1 -1
  339. package/dist/editor/reviews/reviewCommands.js +1 -4
  340. package/dist/editor/reviews/reviewCommands.js.map +1 -1
  341. package/dist/editor/reviews/useMultiReview.js +2 -2
  342. package/dist/editor/reviews/useMultiReview.js.map +1 -1
  343. package/dist/editor/reviews/useReviews.d.ts +2 -2
  344. package/dist/editor/reviews/useReviews.js +30 -12
  345. package/dist/editor/reviews/useReviews.js.map +1 -1
  346. package/dist/editor/services/agentService.d.ts +5 -229
  347. package/dist/editor/services/agentService.js +39 -292
  348. package/dist/editor/services/agentService.js.map +1 -1
  349. package/dist/editor/services/aiService.d.ts +1 -57
  350. package/dist/editor/services/aiService.js +6 -79
  351. package/dist/editor/services/aiService.js.map +1 -1
  352. package/dist/editor/services/contentService.d.ts +3 -6
  353. package/dist/editor/services/contentService.js +12 -13
  354. package/dist/editor/services/contentService.js.map +1 -1
  355. package/dist/editor/services/editService.d.ts +1 -52
  356. package/dist/editor/services/editService.js +2 -94
  357. package/dist/editor/services/editService.js.map +1 -1
  358. package/dist/editor/services/indexService.js +1 -1
  359. package/dist/editor/services/indexService.js.map +1 -1
  360. package/dist/editor/services/reviewsService.d.ts +6 -3
  361. package/dist/editor/services/reviewsService.js +11 -2
  362. package/dist/editor/services/reviewsService.js.map +1 -1
  363. package/dist/editor/services/serviceHelper.d.ts +1 -2
  364. package/dist/editor/services/serviceHelper.js +20 -112
  365. package/dist/editor/services/serviceHelper.js.map +1 -1
  366. package/dist/editor/services/systemService.d.ts +1 -2
  367. package/dist/editor/services/systemService.js +0 -3
  368. package/dist/editor/services/systemService.js.map +1 -1
  369. package/dist/editor/services-server/api.d.ts +2 -1
  370. package/dist/editor/services-server/api.js +6 -11
  371. package/dist/editor/services-server/api.js.map +1 -1
  372. package/dist/editor/services-server/graphQL.d.ts +29 -0
  373. package/dist/editor/services-server/graphQL.js +53 -0
  374. package/dist/editor/services-server/graphQL.js.map +1 -0
  375. package/dist/editor/settings/About.js +3 -317
  376. package/dist/editor/settings/About.js.map +1 -1
  377. package/dist/editor/settings/AllAgentsPanel.d.ts +5 -0
  378. package/dist/editor/settings/AllAgentsPanel.js +139 -0
  379. package/dist/editor/settings/AllAgentsPanel.js.map +1 -0
  380. package/dist/editor/settings/LatestFeedback.d.ts +1 -0
  381. package/dist/editor/settings/LatestFeedback.js +136 -0
  382. package/dist/editor/settings/LatestFeedback.js.map +1 -0
  383. package/dist/editor/settings/QuotaInfo.js +4 -210
  384. package/dist/editor/settings/QuotaInfo.js.map +1 -1
  385. package/dist/editor/settings/SettingsView.js +23 -25
  386. package/dist/editor/settings/SettingsView.js.map +1 -1
  387. package/dist/editor/settings/Setup.d.ts +1 -0
  388. package/dist/editor/settings/Setup.js +211 -0
  389. package/dist/editor/settings/Setup.js.map +1 -0
  390. package/dist/editor/settings/Status.js +6 -7
  391. package/dist/editor/settings/Status.js.map +1 -1
  392. package/dist/editor/settings/index/useIndexStatus.js +22 -20
  393. package/dist/editor/settings/index/useIndexStatus.js.map +1 -1
  394. package/dist/editor/settings/panels/AgentsPanel.d.ts +4 -0
  395. package/dist/editor/settings/panels/AgentsPanel.js +121 -95
  396. package/dist/editor/settings/panels/AgentsPanel.js.map +1 -1
  397. package/dist/editor/settings/panels/DatabasePanel.d.ts +6 -0
  398. package/dist/editor/settings/panels/DatabasePanel.js +50 -0
  399. package/dist/editor/settings/panels/DatabasePanel.js.map +1 -0
  400. package/dist/editor/settings/panels/ModelsPanel.js +108 -329
  401. package/dist/editor/settings/panels/ModelsPanel.js.map +1 -1
  402. package/dist/editor/settings/panels/ProvidersPanel.d.ts +1 -1
  403. package/dist/editor/settings/panels/ProvidersPanel.js +59 -86
  404. package/dist/editor/settings/panels/ProvidersPanel.js.map +1 -1
  405. package/dist/editor/settings/panels/SearchConfigPanel.js +4 -4
  406. package/dist/editor/settings/panels/SearchConfigPanel.js.map +1 -1
  407. package/dist/editor/settings/panels/index.d.ts +2 -3
  408. package/dist/editor/settings/panels/index.js +2 -3
  409. package/dist/editor/settings/panels/index.js.map +1 -1
  410. package/dist/editor/settings/setup-steps/AiSetupStep/EmbeddingsModelSection.d.ts +2 -0
  411. package/dist/editor/settings/setup-steps/AiSetupStep/EmbeddingsModelSection.js +195 -0
  412. package/dist/editor/settings/setup-steps/AiSetupStep/EmbeddingsModelSection.js.map +1 -0
  413. package/dist/editor/settings/setup-steps/AiSetupStep/index.d.ts +2 -0
  414. package/dist/editor/settings/setup-steps/AiSetupStep/index.js +21 -0
  415. package/dist/editor/settings/setup-steps/AiSetupStep/index.js.map +1 -0
  416. package/dist/editor/settings/setup-steps/AiSetupStep/provider/ProviderSection.d.ts +1 -0
  417. package/dist/editor/settings/setup-steps/AiSetupStep/provider/ProviderSection.js +233 -0
  418. package/dist/editor/settings/setup-steps/AiSetupStep/provider/ProviderSection.js.map +1 -0
  419. package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersList.d.ts +15 -0
  420. package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersList.js +14 -0
  421. package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersList.js.map +1 -0
  422. package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.d.ts +1 -0
  423. package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.js +94 -0
  424. package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.js.map +1 -0
  425. package/dist/editor/settings/setup-steps/AiSetupStep/types.d.ts +1 -0
  426. package/dist/editor/settings/setup-steps/AiSetupStep/types.js +2 -0
  427. package/dist/editor/settings/setup-steps/AiSetupStep/types.js.map +1 -0
  428. package/dist/editor/settings/setup-steps/AiSetupStep/utils.d.ts +5 -0
  429. package/dist/editor/settings/setup-steps/AiSetupStep/utils.js +44 -0
  430. package/dist/editor/settings/setup-steps/AiSetupStep/utils.js.map +1 -0
  431. package/dist/editor/settings/setup-steps/IndexSetupStep.d.ts +2 -0
  432. package/dist/editor/settings/setup-steps/IndexSetupStep.js +36 -0
  433. package/dist/editor/settings/setup-steps/IndexSetupStep.js.map +1 -0
  434. package/dist/editor/settings/setup-steps/SettingsSetupStep.d.ts +2 -0
  435. package/dist/editor/settings/setup-steps/SettingsSetupStep.js +111 -0
  436. package/dist/editor/settings/setup-steps/SettingsSetupStep.js.map +1 -0
  437. package/dist/editor/settings/setup-steps/SetupOverview.d.ts +14 -0
  438. package/dist/editor/settings/setup-steps/SetupOverview.js +38 -0
  439. package/dist/editor/settings/setup-steps/SetupOverview.js.map +1 -0
  440. package/dist/editor/settings/status/coreStatusChecks.js +19 -124
  441. package/dist/editor/settings/status/coreStatusChecks.js.map +1 -1
  442. package/dist/editor/settings/status/useStartupChecks.d.ts +1 -3
  443. package/dist/editor/settings/status/useStartupChecks.js +5 -9
  444. package/dist/editor/settings/status/useStartupChecks.js.map +1 -1
  445. package/dist/editor/setup-wizard/steps/CompleteStep.d.ts +1 -2
  446. package/dist/editor/setup-wizard/steps/CompleteStep.js +1 -2
  447. package/dist/editor/setup-wizard/steps/CompleteStep.js.map +1 -1
  448. package/dist/editor/sidebar/ComponentPalette.js +1 -2
  449. package/dist/editor/sidebar/ComponentPalette.js.map +1 -1
  450. package/dist/editor/sidebar/ComponentTree.d.ts +1 -8
  451. package/dist/editor/sidebar/ComponentTree.js +69 -216
  452. package/dist/editor/sidebar/ComponentTree.js.map +1 -1
  453. package/dist/editor/sidebar/Debug.d.ts +1 -0
  454. package/dist/editor/sidebar/Debug.js +70 -0
  455. package/dist/editor/sidebar/Debug.js.map +1 -0
  456. package/dist/editor/sidebar/EditHistory.js +46 -22
  457. package/dist/editor/sidebar/EditHistory.js.map +1 -1
  458. package/dist/editor/sidebar/Favorites.js +8 -4
  459. package/dist/editor/sidebar/Favorites.js.map +1 -1
  460. package/dist/editor/sidebar/GraphQL.d.ts +2 -0
  461. package/dist/editor/sidebar/GraphQL.js +234 -0
  462. package/dist/editor/sidebar/GraphQL.js.map +1 -0
  463. package/dist/editor/sidebar/LeftToolbar.d.ts +1 -0
  464. package/dist/editor/sidebar/LeftToolbar.js +12 -0
  465. package/dist/editor/sidebar/LeftToolbar.js.map +1 -0
  466. package/dist/editor/sidebar/MainContentTree.js +3 -4
  467. package/dist/editor/sidebar/MainContentTree.js.map +1 -1
  468. package/dist/editor/sidebar/NavigationSidebar.d.ts +4 -0
  469. package/dist/editor/sidebar/NavigationSidebar.js +254 -0
  470. package/dist/editor/sidebar/NavigationSidebar.js.map +1 -0
  471. package/dist/editor/sidebar/OperationItem.js +7 -21
  472. package/dist/editor/sidebar/OperationItem.js.map +1 -1
  473. package/dist/editor/sidebar/SidebarPanel.d.ts +1 -3
  474. package/dist/editor/sidebar/SidebarPanel.js +12 -44
  475. package/dist/editor/sidebar/SidebarPanel.js.map +1 -1
  476. package/dist/editor/sidebar/SidebarStack.d.ts +1 -2
  477. package/dist/editor/sidebar/SidebarStack.js +3 -4
  478. package/dist/editor/sidebar/SidebarStack.js.map +1 -1
  479. package/dist/editor/sidebar/Validation.js +12 -22
  480. package/dist/editor/sidebar/Validation.js.map +1 -1
  481. package/dist/editor/sidebar/Workbox.js +3 -53
  482. package/dist/editor/sidebar/Workbox.js.map +1 -1
  483. package/dist/editor/sidebar/WorkspaceRail.d.ts +1 -0
  484. package/dist/editor/sidebar/WorkspaceRail.js +167 -56
  485. package/dist/editor/sidebar/WorkspaceRail.js.map +1 -1
  486. package/dist/editor/tree-indicators/GutterColumns.d.ts +1 -3
  487. package/dist/editor/tree-indicators/GutterColumns.js +5 -26
  488. package/dist/editor/tree-indicators/GutterColumns.js.map +1 -1
  489. package/dist/editor/tree-indicators/GutterContext.d.ts +0 -4
  490. package/dist/editor/tree-indicators/GutterContext.js +0 -23
  491. package/dist/editor/tree-indicators/GutterContext.js.map +1 -1
  492. package/dist/editor/tree-indicators/GutterSelector.d.ts +5 -0
  493. package/dist/editor/tree-indicators/GutterSelector.js +91 -0
  494. package/dist/editor/tree-indicators/GutterSelector.js.map +1 -0
  495. package/dist/editor/tree-indicators/index.d.ts +1 -0
  496. package/dist/editor/tree-indicators/index.js +1 -0
  497. package/dist/editor/tree-indicators/index.js.map +1 -1
  498. package/dist/editor/tree-indicators/types.d.ts +1 -12
  499. package/dist/editor/ui/CopyMoveTargetSelectorDialog.js +1 -1
  500. package/dist/editor/ui/CopyMoveTargetSelectorDialog.js.map +1 -1
  501. package/dist/editor/ui/Icons.js +1 -1
  502. package/dist/editor/ui/Icons.js.map +1 -1
  503. package/dist/editor/ui/ItemNameDialogNew.d.ts +0 -2
  504. package/dist/editor/ui/ItemNameDialogNew.js +17 -33
  505. package/dist/editor/ui/ItemNameDialogNew.js.map +1 -1
  506. package/dist/editor/ui/ItemSearch.js +11 -7
  507. package/dist/editor/ui/ItemSearch.js.map +1 -1
  508. package/dist/editor/ui/SimpleIconButton.js +1 -1
  509. package/dist/editor/ui/SimpleIconButton.js.map +1 -1
  510. package/dist/editor/ui/SimpleTabs.d.ts +0 -1
  511. package/dist/editor/ui/SimpleTabs.js +25 -45
  512. package/dist/editor/ui/SimpleTabs.js.map +1 -1
  513. package/dist/editor/ui/Splitter.d.ts +0 -1
  514. package/dist/editor/ui/Splitter.js +86 -102
  515. package/dist/editor/ui/Splitter.js.map +1 -1
  516. package/dist/editor/ui/TemplateSelectorDialog.js +4 -4
  517. package/dist/editor/ui/TemplateSelectorDialog.js.map +1 -1
  518. package/dist/editor/ui/TreeListSelector.d.ts +1 -6
  519. package/dist/editor/ui/TreeListSelector.js +2 -2
  520. package/dist/editor/ui/TreeListSelector.js.map +1 -1
  521. package/dist/editor/utils/keyboardNavigation.d.ts +20 -6
  522. package/dist/editor/utils/keyboardNavigation.js +140 -48
  523. package/dist/editor/utils/keyboardNavigation.js.map +1 -1
  524. package/dist/editor/utils.js +9 -19
  525. package/dist/editor/utils.js.map +1 -1
  526. package/dist/editor/views/CompareView.d.ts +1 -3
  527. package/dist/editor/views/CompareView.js +5 -7
  528. package/dist/editor/views/CompareView.js.map +1 -1
  529. package/dist/editor/views/EditView.js +1 -1
  530. package/dist/editor/views/EditView.js.map +1 -1
  531. package/dist/editor/views/EditorSlot.js +34 -27
  532. package/dist/editor/views/EditorSlot.js.map +1 -1
  533. package/dist/editor/views/ItemEditor.js +3 -7
  534. package/dist/editor/views/ItemEditor.js.map +1 -1
  535. package/dist/editor/views/MediaFolderEditView.js +1 -1
  536. package/dist/editor/views/MediaFolderEditView.js.map +1 -1
  537. package/dist/editor/views/ParheliaView.js +6 -5
  538. package/dist/editor/views/ParheliaView.js.map +1 -1
  539. package/dist/editor/views/SingleEditView.d.ts +1 -2
  540. package/dist/editor/views/SingleEditView.js +8 -10
  541. package/dist/editor/views/SingleEditView.js.map +1 -1
  542. package/dist/editor/views/editorSlotContext.js +6 -35
  543. package/dist/editor/views/editorSlotContext.js.map +1 -1
  544. package/dist/index.d.ts +2 -16
  545. package/dist/index.js +0 -11
  546. package/dist/index.js.map +1 -1
  547. package/dist/revision.d.ts +2 -2
  548. package/dist/revision.js +2 -2
  549. package/dist/setup/services/setupWizardService.d.ts +13 -40
  550. package/dist/setup/services/setupWizardService.js +17 -32
  551. package/dist/setup/services/setupWizardService.js.map +1 -1
  552. package/dist/setup/wizard/steps/AddModelDialog.js +3 -12
  553. package/dist/setup/wizard/steps/AddModelDialog.js.map +1 -1
  554. package/dist/setup/wizard/steps/ImportModelDialog.js +22 -39
  555. package/dist/setup/wizard/steps/ImportModelDialog.js.map +1 -1
  556. package/dist/splash-screen/ModernSplashScreen.js +32 -112
  557. package/dist/splash-screen/ModernSplashScreen.js.map +1 -1
  558. package/dist/splash-screen/NewPage.js +50 -33
  559. package/dist/splash-screen/NewPage.js.map +1 -1
  560. package/dist/splash-screen/OpenPage.js +6 -2
  561. package/dist/splash-screen/OpenPage.js.map +1 -1
  562. package/dist/splash-screen/ParheliaAssistantChat.js +29 -12
  563. package/dist/splash-screen/ParheliaAssistantChat.js.map +1 -1
  564. package/dist/splash-screen/ParheliaLogo.js +37 -87
  565. package/dist/splash-screen/ParheliaLogo.js.map +1 -1
  566. package/dist/splash-screen/RecentPages.js +3 -3
  567. package/dist/splash-screen/RecentPages.js.map +1 -1
  568. package/dist/tour/Tour.d.ts +1 -2
  569. package/dist/tour/Tour.js +75 -256
  570. package/dist/tour/Tour.js.map +1 -1
  571. package/dist/tour/default-tour.js +96 -222
  572. package/dist/tour/default-tour.js.map +1 -1
  573. package/dist/types.d.ts +29 -63
  574. package/package.json +15 -19
  575. package/styles.css +10 -14
@@ -1,10 +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 { Send, AlertCircle, Loader2, User, Wand2, Square, Mic, MicOff, ChevronDown, ChevronUp, ListTodo, ArrowLeft, DollarSign, ExternalLink, Settings2, Target, X, Plus, } from "lucide-react";
4
- import { getAgent, startAgent, claimAgentBrowser, assignAgentSkill, persistDraftAgent, updateAgentSettings, updateAgentCostLimit, updateAgentContext, getAgentSkillCatalog, getAgentAvailableTools, getAgentOperationAllowances, getAgentTriggerSubscriptions, cancelAgent, canonicalizeAgentMetadata, getPendingPrompts, releaseAgentBrowser, revokeAgentSkill, } from "../services/agentService";
5
- 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";
6
5
  import { useEditContext, useFieldsEditContext } from "../client/editContext";
7
- import { localStorageService } from "../services/localStorageService";
8
6
  import { Textarea } from "../../components/ui/textarea";
9
7
  import { Button } from "../../components/ui/button";
10
8
  import { PlaceholderInput, } from "../../components/ui/PlaceholderInput";
@@ -16,210 +14,19 @@ import { SpawnedAgentsPanel } from "./SpawnedAgentsPanel";
16
14
  import { getComponentById } from "../componentTreeHelper";
17
15
  import { AgentGreeting } from "./AgentGreeting";
18
16
  import { getAgentHistory } from "../services/editService";
19
- import { QuestionnaireInline } from "./dialogs/QuestionnaireInline";
20
- import { getBrowserCaptureClaim, setBrowserCaptureClaim, } from "./dialogs/browserBoundCapture";
21
- import { DIALOG_TYPES, } from "./dialogs/agentDialogTypes";
22
17
  import { Popover, PopoverContent, PopoverTrigger, } from "../../components/ui/popover";
23
18
  import { SecretAgentIcon } from "../ui/Icons";
24
19
  import { formatTime, formatDateTime } from "../utils";
25
20
  import { cn } from "../../lib/utils";
26
- import { sanitizeSvg } from "../../lib/sanitize";
27
21
  import { Select } from "../../components/ui/select";
28
22
  import { AgentTerminalStatusBar } from "./AgentTerminalStatusBar";
23
+ import { useMediaQuery } from "../client/hooks/useMediaQuery";
29
24
  import { SimpleTabs } from "../ui/SimpleTabs";
30
- import { Splitter } from "../ui/Splitter";
31
- import { ScrollingContentTree } from "../ScrollingContentTree";
32
- import { MarkdownDisplay, } from "../../components/MarkdownDisplay";
33
- const userMessageMarkdownComponents = {
34
- h1: (props) => (_jsx("h1", { ...props, className: "mb-2 text-sm leading-5 font-semibold text-gray-900" })),
35
- h2: (props) => (_jsx("h2", { ...props, className: "mb-1.5 text-[13px] leading-5 font-semibold text-gray-900" })),
36
- h3: (props) => (_jsx("h3", { ...props, className: "mb-1 text-[12px] leading-5 font-semibold text-gray-900" })),
37
- h4: (props) => (_jsx("h4", { ...props, className: "mb-1 text-[12px] leading-5 font-medium text-gray-800" })),
38
- p: (props) => (_jsx("p", { ...props, className: "my-1 text-[12px] leading-5 text-gray-700" })),
39
- ul: (props) => (_jsx("ul", { ...props, className: "my-2 ml-5 list-disc space-y-1 text-[12px] leading-5 text-gray-700" })),
40
- ol: (props) => (_jsx("ol", { ...props, className: "my-2 ml-5 list-decimal space-y-1 text-[12px] leading-5 text-gray-700" })),
41
- li: (props) => (_jsx("li", { ...props, className: "text-[12px] leading-5 text-gray-700" })),
42
- pre: (props) => (_jsx("pre", { ...props, className: "my-2 overflow-auto rounded-md bg-slate-100 px-3 py-2 text-[11px] leading-4 text-slate-700" })),
43
- code: ({ inline, className, ...props }) => inline ? (_jsx("code", { ...props, className: "rounded bg-slate-100 px-1 py-0.5 text-[11px] text-slate-700" })) : (_jsx("code", { ...props, className: className })),
44
- };
45
- function buildPlaceholderAgentDetails(agentStub) {
46
- const now = new Date().toISOString();
47
- const updated = agentStub.updatedDate || now;
48
- // AgentDetails has required fields, but some workspaces only pass an Agent stub initially.
49
- // This placeholder keeps streaming/tool-call UI working until `getAgent()` returns full details.
50
- return {
51
- ...agentStub,
52
- name: agentStub.name || "Agent",
53
- userId: agentStub.userId || "",
54
- updatedDate: updated,
55
- profileName: agentStub.profileName || "",
56
- model: agentStub.model || "",
57
- createdDate: agentStub.createdDate || updated,
58
- totalTokensUsed: 0,
59
- totalInputTokens: 0,
60
- totalOutputTokens: 0,
61
- totalCachedInputTokens: 0,
62
- totalInputTokenCost: 0,
63
- totalOutputTokenCost: 0,
64
- totalCachedInputTokenCost: 0,
65
- totalImageCost: 0,
66
- totalCost: 0,
67
- currency: agentStub.currency || "USD",
68
- messageCount: agentStub.messageCount || 0,
69
- };
70
- }
71
- function normalizeDialogAgentId(value) {
72
- return value?.trim().toLowerCase() || "";
73
- }
74
- function formatAllowanceSource(source) {
75
- const normalized = source?.trim();
76
- if (!normalized)
77
- return null;
78
- if (normalized === "user")
79
- return "User granted";
80
- if (normalized.startsWith("preconfigured:profile:"))
81
- return "Profile";
82
- if (normalized.startsWith("preconfigured:skill:"))
83
- return "Skill";
84
- if (normalized.startsWith("system:")) {
85
- return normalized.slice("system:".length).replace(/[-_]+/g, " ").trim();
86
- }
87
- return normalized;
88
- }
89
- function formatAllowanceLabel(allowance) {
90
- return `${allowance.operationType || "*"}${"itemPath" in allowance
91
- ? ` ${allowance.itemPath}`
92
- : ` ${allowance.normalizedPath}`}`;
93
- }
94
- function getAgentRunMessageAgentId(payload) {
95
- const agentId = payload?.agentId;
96
- return typeof agentId === "string" ? normalizeDialogAgentId(agentId) : null;
97
- }
98
- function getAgentRunMessageSeq(payload) {
99
- const seq = payload?.seq;
100
- return typeof seq === "number" ? seq : null;
101
- }
102
- function getAgentRunMessageDetail(type, payload) {
103
- if (type === "agent:run:delta") {
104
- return payload?.type || null;
105
- }
106
- if (type === "agent:run:status") {
107
- return payload?.data?.state || payload?.data?.status || null;
108
- }
109
- if (type === "agent:run:error") {
110
- return payload?.error || null;
111
- }
112
- if (type === "agent:run:complete") {
113
- return payload?.finalStatus || null;
114
- }
115
- if (type === "agent:run:start") {
116
- return payload?.agentName || null;
117
- }
118
- return null;
119
- }
120
- function isHeartbeatRunEventMessage(message) {
121
- if (!message)
122
- return false;
123
- if (message.type === "agent:run:delta") {
124
- const deltaType = message.payload?.type;
125
- return String(deltaType || "").toLowerCase() === "heartbeat";
126
- }
127
- if (message.type === "agent:run:status") {
128
- const state = message.payload?.data?.state || message.payload?.data?.status;
129
- return String(state || "").toLowerCase() === "heartbeat";
130
- }
131
- return false;
132
- }
133
- function getVisibleDialogRegistry() {
134
- const registry = globalThis.__agentDialogVisibleCallbacks;
135
- return registry && typeof registry === "object" ? registry : {};
136
- }
137
- function decodeHtmlEntities(value) {
138
- if (typeof document === "undefined") {
139
- return value;
140
- }
141
- const textarea = document.createElement("textarea");
142
- textarea.innerHTML = value;
143
- return textarea.value;
144
- }
145
- function toUserFacingAgentErrorMessage(value) {
146
- if (!value)
147
- return "";
148
- const normalizedValue = decodeHtmlEntities(value.replace(/<br\s*\/?>/gi, "\n")).trim();
149
- if (!normalizedValue)
150
- return "";
151
- const trimmed = normalizedValue.trim();
152
- const maybeJson = trimmed.startsWith("{") ||
153
- trimmed.startsWith("[") ||
154
- trimmed.startsWith('"');
155
- if (maybeJson) {
156
- try {
157
- const parsed = JSON.parse(trimmed);
158
- const structuredMessage = typeof parsed === "string"
159
- ? parsed
160
- : typeof parsed?.error === "object" && parsed?.error
161
- ? (() => {
162
- const errObj = parsed.error;
163
- // Extract detailed message from metadata.raw (OpenRouter-style nested errors)
164
- if (typeof errObj.metadata?.raw ===
165
- "string") {
166
- return String(errObj.metadata.raw);
167
- }
168
- // Fall back to error.message
169
- if (typeof errObj.message === "string") {
170
- return errObj.message;
171
- }
172
- return "";
173
- })()
174
- : typeof parsed?.error === "string"
175
- ? parsed.error
176
- : typeof parsed?.message === "string"
177
- ? parsed.message
178
- : typeof parsed?.detail === "string"
179
- ? parsed.detail
180
- : typeof parsed?.error_description === "string"
181
- ? parsed.error_description
182
- : "";
183
- if (structuredMessage.trim()) {
184
- value = structuredMessage;
185
- }
186
- }
187
- catch {
188
- // Fall through to plain-text cleanup when the provider error isn't valid JSON.
189
- }
190
- }
191
- const firstLine = value
192
- .replace(/<br\s*\/?>/gi, "\n")
193
- .split(/\r?\n/)
194
- .map((line) => line.trim())
195
- .find(Boolean);
196
- const cleaned = decodeHtmlEntities(firstLine || value)
197
- .replace(/^Failed to start agent:\s*/i, "")
198
- .replace(/^Unknown error\s*/i, "")
199
- .replace(/^Error:\s*/i, "")
200
- .replace(/^Error\s+/i, "")
201
- .replace(/\s+/g, " ")
202
- .trim();
203
- return cleaned;
204
- }
205
- function isAgentErrorStatusValue(status) {
206
- return status === "error";
207
- }
208
25
  // Simple user message component
209
26
  const UserMessage = ({ message }) => {
210
- const content = message.content || "";
211
- const [isTriggerExpanded, setIsTriggerExpanded] = useState(false);
212
- // Trigger-sourced prompts are prefixed by backend as "[Trigger: {name}]: {content}"
213
- const triggerPattern = /^\[Trigger: ([^\]]+)\]:\s*(.*)$/s;
214
- const triggerMatch = content.match(triggerPattern);
215
- const triggerName = triggerMatch?.[1]?.trim() || "";
216
- const triggerContent = triggerMatch?.[2] || "";
217
- const isTriggerMessage = triggerName.length > 0;
218
- if (isTriggerMessage) {
219
- 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 }) }))] }) }));
220
- }
221
27
  // Parse source agent name from content if it starts with "[From ...]:"
222
28
  // Backend formats messages from other agents as "[From {sourceAgentName}]: {content}"
29
+ const content = message.content || "";
223
30
  const fromPattern = /^\[From ([^\]]+)\]:\s*(.*)$/s;
224
31
  const match = content.match(fromPattern);
225
32
  let sourceAgentName;
@@ -244,10 +51,7 @@ const UserMessage = ({ message }) => {
244
51
  message.sourceAgent?.name;
245
52
  }
246
53
  const displayName = sourceAgentName ? `[From ${sourceAgentName}]` : "You";
247
- 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 }) })] })] }));
248
- };
249
- const HeartbeatMessage = ({ message }) => {
250
- 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 })] })] }));
251
55
  };
252
56
  // Helper to extract todos from potentially incomplete JSON during streaming
253
57
  const extractPartialTodos = (jsonText) => {
@@ -481,6 +285,14 @@ const extractTodosFromMessages = (messages) => {
481
285
  todoMap.set(key, todo);
482
286
  }
483
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
+ }
484
296
  return result;
485
297
  };
486
298
  // TodoListPanel component
@@ -488,9 +300,12 @@ const TodoListPanel = ({ messages, agentMetadata, }) => {
488
300
  const [isExpanded, setIsExpanded] = useState(true);
489
301
  // First try to get todos from agent metadata (real-time updates)
490
302
  // Server sends additionalData.todoList directly via contextChanged status
303
+ // Also check top-level todoList for backward compatibility with stored contexts
491
304
  const metadataTodos = (() => {
492
305
  try {
493
- 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;
494
309
  if (todoList?.items && Array.isArray(todoList.items)) {
495
310
  const rawItems = todoList.items
496
311
  .map((item, idx) => ({
@@ -618,17 +433,6 @@ const groupConsecutiveMessages = (agentMessages) => {
618
433
  // Add user message
619
434
  groups.push({ type: "user", messages: [message] });
620
435
  }
621
- else if (message.messageType === "heartbeat" ||
622
- message.role === "system") {
623
- if (currentAssistantGroup.length > 0) {
624
- groups.push({
625
- type: "assistant-group",
626
- messages: currentAssistantGroup,
627
- });
628
- currentAssistantGroup = [];
629
- }
630
- groups.push({ type: "heartbeat", messages: [message] });
631
- }
632
436
  else if (message.role === "assistant") {
633
437
  // Add to current assistant group
634
438
  currentAssistantGroup.push(message);
@@ -696,7 +500,6 @@ const calculateTotalTokens = (messages) => {
696
500
  outputCost: acc.outputCost + (message.outputTokenCost || 0),
697
501
  cachedCost: acc.cachedCost + (message.cachedInputTokenCost || 0),
698
502
  cacheWriteCost: acc.cacheWriteCost,
699
- imageCost: acc.imageCost,
700
503
  totalCost: acc.totalCost + (message.totalCost || 0),
701
504
  };
702
505
  }, {
@@ -708,7 +511,6 @@ const calculateTotalTokens = (messages) => {
708
511
  outputCost: 0,
709
512
  cachedCost: 0,
710
513
  cacheWriteCost: 0,
711
- imageCost: 0,
712
514
  totalCost: 0,
713
515
  });
714
516
  return totals;
@@ -719,84 +521,6 @@ const getOperationsForMessageGroup = (messages, agentOperations) => {
719
521
  const matched = agentOperations.filter((op) => op.toolCallId && toolCallIds.has(op.toolCallId));
720
522
  return matched;
721
523
  };
722
- const stringifyToolField = (value) => {
723
- if (value === undefined || value === null)
724
- return undefined;
725
- if (typeof value === "string") {
726
- return value.trim().length > 0 ? value : undefined;
727
- }
728
- try {
729
- return JSON.stringify(value);
730
- }
731
- catch {
732
- return String(value);
733
- }
734
- };
735
- const parseToolResultValue = (value) => {
736
- if (value === undefined || value === null) {
737
- return undefined;
738
- }
739
- if (typeof value === "object") {
740
- return value;
741
- }
742
- if (typeof value !== "string") {
743
- return String(value);
744
- }
745
- const trimmed = value.trim();
746
- if (!trimmed) {
747
- return undefined;
748
- }
749
- try {
750
- let parsed = JSON.parse(trimmed);
751
- if (typeof parsed === "string" &&
752
- (parsed.startsWith("{") || parsed.startsWith("["))) {
753
- parsed = JSON.parse(parsed);
754
- }
755
- if (parsed && typeof parsed === "object") {
756
- return parsed;
757
- }
758
- }
759
- catch {
760
- // Fall back to the original string when the payload is plain text.
761
- }
762
- return value;
763
- };
764
- const getFirstToolCallEnvelope = (data) => {
765
- if (!data || typeof data !== "object")
766
- return undefined;
767
- const direct = data.toolCall || data.tool_call;
768
- if (direct)
769
- return direct;
770
- const arrayCandidates = [data.tool_calls, data.toolCalls];
771
- for (const candidate of arrayCandidates) {
772
- if (Array.isArray(candidate) && candidate.length > 0) {
773
- return candidate[0];
774
- }
775
- }
776
- return undefined;
777
- };
778
- const extractToolCallFields = (data) => {
779
- const envelope = getFirstToolCallEnvelope(data);
780
- const functionPayload = data?.function || envelope?.function;
781
- const functionName = data?.functionName ||
782
- data?.name ||
783
- functionPayload?.name ||
784
- envelope?.functionName ||
785
- envelope?.name ||
786
- "unknown";
787
- const toolCallId = data?.toolCallId || data?.id || envelope?.id;
788
- const functionArguments = stringifyToolField(data?.functionArguments) ||
789
- stringifyToolField(data?.arguments) ||
790
- stringifyToolField(functionPayload?.arguments) ||
791
- stringifyToolField(envelope?.functionArguments) ||
792
- stringifyToolField(envelope?.arguments) ||
793
- "{}";
794
- return {
795
- toolCallId,
796
- functionName,
797
- functionArguments,
798
- };
799
- };
800
524
  // Convert agent messages to AI terminal format for a response group
801
525
  const convertAgentMessagesToAiFormat = (agentMessages) => {
802
526
  return agentMessages.map((agentMessage) => {
@@ -817,39 +541,28 @@ const convertAgentMessagesToAiFormat = (agentMessages) => {
817
541
  role: agentMessage.role,
818
542
  createdDate: agentMessage.createdDate,
819
543
  tool_calls: agentMessage.toolCalls
820
- ? agentMessage.toolCalls.map((toolCall) => {
821
- const isPruned = !!toolCall.isPruned ||
822
- /^PRUNED$/i.test(toolCall.functionError || "");
823
- const displayResult = parseToolResultValue(toolCall.functionResultRichContent) ?? toolCall.functionResult;
824
- return {
825
- id: toolCall.toolCallId,
826
- displayName: toolCall.functionName,
827
- function: {
828
- name: toolCall.functionName,
829
- arguments: toolCall.functionArguments,
830
- result: displayResult,
831
- error: toolCall.functionError,
832
- },
833
- // Pass through approval info if present on the tool call
834
- requiresApproval: toolCall.requiresApproval,
835
- // Pass through prune metadata so the terminal can render a neutral state
836
- isPruned,
837
- prunedAt: toolCall.prunedAt,
838
- // Pass through isCompleted so ToolCallDisplay knows when to hide spinner
839
- isCompleted: toolCall.isCompleted,
840
- // Tool call is streaming if message is not completed and tool call has no result yet
841
- isStreaming: !agentMessage.isCompleted &&
842
- !toolCall.isCompleted &&
843
- !displayResult &&
844
- !toolCall.functionError &&
845
- !isPruned,
846
- // Pass through message IDs for approval/rejection events
847
- messageId: toolCall.messageId,
848
- dbMessageId: toolCall.dbMessageId,
849
- responseTimeMs: toolCall.responseTimeMs,
850
- createdDate: toolCall.createdDate,
851
- };
852
- })
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
+ }))
853
566
  : [],
854
567
  };
855
568
  if (agentMessage.toolCallId) {
@@ -861,10 +574,10 @@ const convertAgentMessagesToAiFormat = (agentMessages) => {
861
574
  // interface AgentTerminalProps {
862
575
  // agentStub: Agent;
863
576
  // }
864
- 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, }) {
865
578
  const editContext = useEditContext();
866
579
  const fieldsContext = useFieldsEditContext();
867
- const [agent, setAgent] = useState(() => buildPlaceholderAgentDetails(agentStub));
580
+ const [agent, setAgent] = useState(undefined);
868
581
  const [messages, setMessages] = useState([]);
869
582
  const [agentOperations, setAgentOperations] = useState([]);
870
583
  const [prompt, setPrompt] = useState("");
@@ -875,35 +588,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
875
588
  const [activePlaceholderInput, setActivePlaceholderInput] = useState(null);
876
589
  const [allPlaceholdersFilled, setAllPlaceholdersFilled] = useState(false);
877
590
  const [agentMetadata, setAgentMetadata] = useState(null);
878
- const [isBrowserClaimMutationPending, setIsBrowserClaimMutationPending] = useState(false);
879
- const [pendingBrowserCaptureDialogType, setPendingBrowserCaptureDialogType] = useState(null);
880
- // Ensure we always have an agent object for streaming handlers, even before `getAgent()` resolves.
881
- // This prevents early tool calls (e.g., ask-questionnaire) from being dropped in compact/workspace UIs.
882
- useEffect(() => {
883
- setAgent((prev) => {
884
- if (prev?.id === agentStub.id)
885
- return prev;
886
- return buildPlaceholderAgentDetails(agentStub);
887
- });
888
- }, [agentStub.id]);
889
- const observedMessageIdsRef = useRef(new Set());
890
- useEffect(() => {
891
- observedMessageIdsRef.current = new Set();
892
- }, [agentStub.id]);
893
- useEffect(() => {
894
- if (!onMessage)
895
- return;
896
- for (const message of messages) {
897
- if (!message?.id)
898
- continue;
899
- if (!message.isCompleted)
900
- continue;
901
- if (observedMessageIdsRef.current.has(message.id))
902
- continue;
903
- observedMessageIdsRef.current.add(message.id);
904
- onMessage(message);
905
- }
906
- }, [messages, onMessage]);
907
591
  // Generate a stable clientSessionId per component instance for stream deduplication
908
592
  const clientSessionIdRef = useRef(null);
909
593
  if (!clientSessionIdRef.current) {
@@ -915,7 +599,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
915
599
  const [isListening, setIsListening] = useState(false);
916
600
  const recognitionRef = useRef(null);
917
601
  const prevPlaceholderRef = useRef(null);
918
- const promptBeforeVoiceRef = useRef("");
919
602
  // Voice button press-and-hold tracking
920
603
  const voicePressStartRef = useRef(null);
921
604
  const voiceHoldTimerRef = useRef(null);
@@ -925,70 +608,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
925
608
  const [showCompressionPopover, setShowCompressionPopover] = useState(false);
926
609
  // Agent inline dialog state (for component type selector, etc.)
927
610
  const [activeInlineDialog, setActiveInlineDialog] = useState(null);
928
- const dialogTerminalInstanceIdRef = useRef("");
929
- if (!dialogTerminalInstanceIdRef.current) {
930
- dialogTerminalInstanceIdRef.current = crypto.randomUUID();
931
- }
932
- const activeInlineDialogRef = useRef(activeInlineDialog);
933
- const isQuestionnaireDialogOpen = activeInlineDialog?.request.dialogType === "questionnaire";
934
- const orphanTimeoutRef = useRef(null);
935
- useEffect(() => {
936
- activeInlineDialogRef.current = activeInlineDialog;
937
- const visibleRegistry = { ...getVisibleDialogRegistry() };
938
- const callbackId = activeInlineDialog?.request.callbackId || null;
939
- const terminalInstanceId = dialogTerminalInstanceIdRef.current;
940
- const agentKeys = [
941
- normalizeDialogAgentId(agentStubIdRefForDialogs.current),
942
- normalizeDialogAgentId(agentIdRefForDialogs.current),
943
- ].filter(Boolean);
944
- agentKeys.forEach((key) => {
945
- if (callbackId) {
946
- visibleRegistry[key] = {
947
- callbackId,
948
- terminalInstanceId,
949
- };
950
- }
951
- else {
952
- delete visibleRegistry[key];
953
- }
954
- });
955
- globalThis.__agentDialogVisibleCallbacks = visibleRegistry;
956
- }, [activeInlineDialog]);
957
- useEffect(() => {
958
- onQuestionnaireOpenChange?.(isQuestionnaireDialogOpen);
959
- }, [isQuestionnaireDialogOpen, onQuestionnaireOpenChange]);
960
- useLayoutEffect(() => {
961
- if (displayMode !== "summary" || !isQuestionnaireDialogOpen) {
962
- return;
963
- }
964
- const scrollSummaryTerminalToTop = () => {
965
- const container = messagesContainerRef.current;
966
- if (!container) {
967
- return;
968
- }
969
- container.scrollTop = 0;
970
- };
971
- scrollSummaryTerminalToTop();
972
- const frame1 = requestAnimationFrame(() => {
973
- scrollSummaryTerminalToTop();
974
- });
975
- let frame3 = 0;
976
- const frame2 = requestAnimationFrame(() => {
977
- frame3 = requestAnimationFrame(() => {
978
- scrollSummaryTerminalToTop();
979
- });
980
- });
981
- return () => {
982
- cancelAnimationFrame(frame1);
983
- cancelAnimationFrame(frame2);
984
- cancelAnimationFrame(frame3);
985
- };
986
- }, [displayMode, isQuestionnaireDialogOpen]);
987
- useEffect(() => {
988
- return () => {
989
- onQuestionnaireOpenChange?.(false);
990
- };
991
- }, [onQuestionnaireOpenChange]);
992
611
  const isWaitingRef = useRef(false);
993
612
  useEffect(() => {
994
613
  isWaitingRef.current = isWaitingForResponse;
@@ -999,49 +618,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
999
618
  const isStoppingRef = useRef(false);
1000
619
  // Server-driven state: true when agent is actively processing (set by WebSocket messages)
1001
620
  const [isAgentThinking, setIsAgentThinking] = useState(false);
1002
- useEffect(() => {
1003
- if (!initialMetadata)
1004
- return;
1005
- setAgentMetadata((prev) => sanitizeAgentMetadata({
1006
- ...(prev || {}),
1007
- ...initialMetadata,
1008
- additionalData: {
1009
- ...(prev?.additionalData || {}),
1010
- ...(initialMetadata?.additionalData || {}),
1011
- },
1012
- }));
1013
- }, [initialMetadata]);
1014
621
  const hasActiveStreaming = useCallback(() => {
1015
622
  const current = messagesRef.current || [];
1016
623
  return current.some((m) => !m.isCompleted && m.messageType === "streaming");
1017
624
  }, []);
1018
- const currentAgentId = agent?.id || agentStub.id;
1019
- const recentAgentRunEvents = useMemo(() => {
1020
- const normalizedAgentId = normalizeDialogAgentId(currentAgentId);
1021
- if (!normalizedAgentId) {
1022
- return [];
1023
- }
1024
- if (!editContext) {
1025
- return [];
1026
- }
1027
- return (editContext.webSocketMessages || [])
1028
- .filter((message) => {
1029
- if (!message?.type?.startsWith("agent:run:")) {
1030
- return false;
1031
- }
1032
- if (isHeartbeatRunEventMessage(message)) {
1033
- return false;
1034
- }
1035
- return getAgentRunMessageAgentId(message.payload) === normalizedAgentId;
1036
- })
1037
- .slice(-8)
1038
- .map((message) => ({
1039
- timestamp: message.timestamp,
1040
- type: message.type,
1041
- seq: getAgentRunMessageSeq(message.payload),
1042
- detail: getAgentRunMessageDetail(message.type, message.payload),
1043
- }));
1044
- }, [currentAgentId, editContext?.webSocketMessages]);
1045
625
  // Collect all pending tool calls for batch approval functionality
1046
626
  const allPendingApprovals = useMemo(() => {
1047
627
  const pending = [];
@@ -1083,82 +663,41 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1083
663
  }, [allPendingApprovals]);
1084
664
  // Handle mode switch to autonomous
1085
665
  const handleSwitchToAutonomous = useCallback(() => {
1086
- const nextMode = "autonomous";
1087
- setMode(nextMode);
1088
- setAgentMetadata((prev) => ({
1089
- ...(prev ?? {}),
1090
- mode: nextMode,
1091
- }));
1092
- setAgent((prev) => {
1093
- if (!prev)
1094
- return prev;
1095
- return {
1096
- ...prev,
1097
- mode: nextMode,
1098
- };
1099
- });
1100
- }, [setAgent, setAgentMetadata]);
666
+ setMode("autonomous");
667
+ }, []);
1101
668
  const [resolvedPageName, setResolvedPageName] = useState(undefined);
1102
669
  const [resolvedComponentName, setResolvedComponentName] = useState(undefined);
1103
670
  const [resolvedFieldName, setResolvedFieldName] = useState(undefined);
1104
671
  const [promptHistory, setPromptHistory] = useState(() => {
1105
- return (localStorageService.getItem("editor.agent.promptHistory") || []);
672
+ if (typeof window !== "undefined") {
673
+ return JSON.parse(localStorage.getItem("editor.agent.promptHistory") || "[]");
674
+ }
675
+ return [];
1106
676
  });
1107
677
  const [currentHistoryIndex, setCurrentHistoryIndex] = useState(-1);
1108
678
  const [showPredefined, setShowPredefined] = useState(false);
1109
679
  const [activeProfile, setActiveProfile] = useState(undefined);
1110
680
  const [selectedModelId, setSelectedModelId] = useState(undefined);
1111
- const normalizeAgentMode = (value) => {
1112
- if (value === "autonomous" ||
1113
- value === "read-only" ||
1114
- value === "supervised") {
1115
- return value;
1116
- }
1117
- return null;
1118
- };
1119
681
  const [mode, setMode] = useState("supervised");
1120
682
  const [queuedPrompts, setQueuedPrompts] = useState([]);
1121
- const [expandedQueuedTriggerIds, setExpandedQueuedTriggerIds] = useState({});
683
+ const isMobile = useMediaQuery("(max-width: 768px)");
1122
684
  const [contextPanelsActiveTab, setContextPanelsActiveTab] = useState(0);
1123
685
  const [hiddenContextPanelTabIds, setHiddenContextPanelTabIds] = useState(new Set());
1124
686
  const [showCostAndAgent, setShowCostAndAgent] = useState(false);
1125
- const [showAgentSettings, setShowAgentSettings] = useState(false);
1126
- const [showSkillPicker, setShowSkillPicker] = useState(false);
1127
- const [availableSkills, setAvailableSkills] = useState([]);
1128
- const [skillRootIds, setSkillRootIds] = useState([]);
1129
- const [selectableTemplateIds, setSelectableTemplateIds] = useState([]);
1130
- const [skillsLoading, setSkillsLoading] = useState(false);
1131
- const [skillsError, setSkillsError] = useState(null);
1132
- const [skillActionError, setSkillActionError] = useState(null);
1133
- const [triggerSubscriptions, setTriggerSubscriptions] = useState([]);
1134
- const [availableTools, setAvailableTools] = useState([]);
1135
- const [operationAllowances, setOperationAllowances] = useState({
1136
- sitecore: [],
1137
- filesystem: [],
1138
- });
1139
- const [triggerSubscriptionsLoading, setTriggerSubscriptionsLoading] = useState(false);
1140
- const [availableToolsLoading, setAvailableToolsLoading] = useState(false);
1141
- const [operationAllowancesLoading, setOperationAllowancesLoading] = useState(false);
1142
- const [triggerSubscriptionsError, setTriggerSubscriptionsError] = useState(null);
1143
- const [availableToolsError, setAvailableToolsError] = useState(null);
1144
- const [operationAllowancesError, setOperationAllowancesError] = useState(null);
1145
- const [toolsSectionExpanded, setToolsSectionExpanded] = useState(false);
1146
- const [allowancesSectionExpanded, setAllowancesSectionExpanded] = useState(false);
1147
- const [subscribedTriggersSectionExpanded, setSubscribedTriggersSectionExpanded,] = useState(false);
1148
- const isPersistedAgent = !!agent?.userId;
1149
- const isLocalOnlyDraftAgent = agent?.status === "new" && !isPersistedAgent;
1150
687
  const hasSpawnedAgents = useMemo(() => {
1151
688
  if (!agentMetadata)
1152
689
  return false;
1153
- const childAgents = agentMetadata?.childAgents;
690
+ const childAgents = agentMetadata?.ChildAgents ||
691
+ agentMetadata?.childAgents;
1154
692
  if (!Array.isArray(childAgents) || childAgents.length === 0)
1155
693
  return false;
1156
- 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));
1157
695
  }, [agentMetadata]);
1158
696
  const hasTodoContent = useMemo(() => {
1159
697
  const metadataTodos = (() => {
1160
698
  try {
1161
- const todoList = agentMetadata?.additionalData?.todoList;
699
+ const todoList = agentMetadata?.additionalData?.todoList ||
700
+ agentMetadata?.todoList;
1162
701
  if (todoList?.items && Array.isArray(todoList.items)) {
1163
702
  const raw = todoList.items.filter((item) => item?.title || item?.text || item?.label || item?.task);
1164
703
  return raw.length > 0;
@@ -1206,9 +745,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1206
745
  .then((result) => {
1207
746
  if (cancelled)
1208
747
  return;
1209
- if (result.type === "success" &&
1210
- result.data &&
1211
- result.data.length > 0) {
748
+ if (result.type === "success" && result.data && result.data.length > 0) {
1212
749
  setHasHistoryContent(true);
1213
750
  }
1214
751
  else {
@@ -1239,38 +776,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1239
776
  });
1240
777
  return () => unsubscribe();
1241
778
  }, [agent?.id, editContext?.addSocketMessageListener]);
1242
- useEffect(() => {
1243
- let active = true;
1244
- const loadSkills = async () => {
1245
- try {
1246
- setSkillsLoading(true);
1247
- setSkillsError(null);
1248
- const catalog = await getAgentSkillCatalog(false);
1249
- if (active) {
1250
- setAvailableSkills(catalog.skills.filter((s) => !s.disabled));
1251
- setSkillRootIds(catalog.rootIds);
1252
- setSelectableTemplateIds(catalog.selectableTemplateIds);
1253
- }
1254
- }
1255
- catch (e) {
1256
- if (active) {
1257
- setSkillsError(e?.message || "Failed to load skills");
1258
- setAvailableSkills([]);
1259
- setSkillRootIds([]);
1260
- setSelectableTemplateIds([]);
1261
- }
1262
- }
1263
- finally {
1264
- if (active) {
1265
- setSkillsLoading(false);
1266
- }
1267
- }
1268
- };
1269
- void loadSkills();
1270
- return () => {
1271
- active = false;
1272
- };
1273
- }, []);
1274
779
  const modeOptions = useMemo(() => [
1275
780
  {
1276
781
  value: "supervised",
@@ -1305,239 +810,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1305
810
  value: m.id,
1306
811
  label: m.name,
1307
812
  })) || []).sort((a, b) => a.label.localeCompare(b.label)), [activeProfile]);
1308
- const metadataSelectedSkillIds = useMemo(() => {
1309
- const rawSkillIds = agentMetadata?.additionalData?.skillIds ?? [];
1310
- if (!Array.isArray(rawSkillIds)) {
1311
- return [];
1312
- }
1313
- return rawSkillIds
1314
- .map((x) => String(x || "").trim())
1315
- .filter((x) => x.length > 0);
1316
- }, [agentMetadata]);
1317
- const backendAssignedSkillIds = useMemo(() => {
1318
- const rawSkillIds = agent?.assignedSkillIds ?? [];
1319
- if (!Array.isArray(rawSkillIds)) {
1320
- return [];
1321
- }
1322
- return rawSkillIds
1323
- .map((x) => String(x || "").trim())
1324
- .filter((x) => x.length > 0);
1325
- }, [agent]);
1326
- const preloadedSkillIds = useMemo(() => {
1327
- const rawSkillIds = activeProfile?.preloadSkills ?? [];
1328
- if (!Array.isArray(rawSkillIds)) {
1329
- return [];
1330
- }
1331
- return rawSkillIds
1332
- .map((skill) => String(skill?.id || "").trim())
1333
- .filter((id) => id.length > 0);
1334
- }, [activeProfile?.preloadSkills]);
1335
- const autoAssignedSkillIds = useMemo(() => {
1336
- const preloadedIdSet = new Set(preloadedSkillIds.map((id) => id.toLowerCase()));
1337
- const all = isLocalOnlyDraftAgent
1338
- ? preloadedSkillIds
1339
- : backendAssignedSkillIds.filter((id) => preloadedIdSet.has(id.toLowerCase()));
1340
- const seen = new Set();
1341
- const unique = [];
1342
- for (const id of all) {
1343
- const key = id.toLowerCase();
1344
- if (seen.has(key))
1345
- continue;
1346
- seen.add(key);
1347
- unique.push(id);
1348
- }
1349
- return unique;
1350
- }, [backendAssignedSkillIds, isLocalOnlyDraftAgent, preloadedSkillIds]);
1351
- const selectedSkillIds = useMemo(() => {
1352
- const all = [
1353
- ...autoAssignedSkillIds,
1354
- ...backendAssignedSkillIds,
1355
- ...metadataSelectedSkillIds,
1356
- ];
1357
- const seen = new Set();
1358
- const unique = [];
1359
- for (const id of all) {
1360
- const key = id.toLowerCase();
1361
- if (seen.has(key))
1362
- continue;
1363
- seen.add(key);
1364
- unique.push(id);
1365
- }
1366
- return unique;
1367
- }, [autoAssignedSkillIds, backendAssignedSkillIds, metadataSelectedSkillIds]);
1368
- useEffect(() => {
1369
- let active = true;
1370
- if (!showAgentSettings) {
1371
- return () => {
1372
- active = false;
1373
- };
1374
- }
1375
- if (!agent?.id || isLocalOnlyDraftAgent) {
1376
- setTriggerSubscriptions([]);
1377
- setTriggerSubscriptionsLoading(false);
1378
- setTriggerSubscriptionsError(null);
1379
- setAvailableTools([]);
1380
- setAvailableToolsLoading(false);
1381
- setAvailableToolsError(null);
1382
- setOperationAllowances({ sitecore: [], filesystem: [] });
1383
- setOperationAllowancesLoading(false);
1384
- setOperationAllowancesError(null);
1385
- return () => {
1386
- active = false;
1387
- };
1388
- }
1389
- const loadTriggerSubscriptions = async () => {
1390
- try {
1391
- setTriggerSubscriptionsLoading(true);
1392
- setTriggerSubscriptionsError(null);
1393
- const subscriptions = await getAgentTriggerSubscriptions(agent.id);
1394
- if (active) {
1395
- setTriggerSubscriptions(subscriptions);
1396
- }
1397
- }
1398
- catch (e) {
1399
- if (active) {
1400
- setTriggerSubscriptionsError(e?.message || "Failed to load trigger subscriptions");
1401
- }
1402
- }
1403
- finally {
1404
- if (active) {
1405
- setTriggerSubscriptionsLoading(false);
1406
- }
1407
- }
1408
- };
1409
- const loadOperationAllowances = async () => {
1410
- try {
1411
- setOperationAllowancesLoading(true);
1412
- setOperationAllowancesError(null);
1413
- const allowances = await getAgentOperationAllowances(agent.id);
1414
- if (active) {
1415
- setOperationAllowances(allowances);
1416
- }
1417
- }
1418
- catch (e) {
1419
- if (active) {
1420
- setOperationAllowancesError(e?.message || "Failed to load allowances");
1421
- }
1422
- }
1423
- finally {
1424
- if (active) {
1425
- setOperationAllowancesLoading(false);
1426
- }
1427
- }
1428
- };
1429
- const loadAvailableTools = async () => {
1430
- try {
1431
- setAvailableToolsLoading(true);
1432
- setAvailableToolsError(null);
1433
- const tools = await getAgentAvailableTools(agent.id);
1434
- if (active) {
1435
- setAvailableTools(tools);
1436
- }
1437
- }
1438
- catch (e) {
1439
- if (active) {
1440
- setAvailableToolsError(e?.message || "Failed to load available tools");
1441
- }
1442
- }
1443
- finally {
1444
- if (active) {
1445
- setAvailableToolsLoading(false);
1446
- }
1447
- }
1448
- };
1449
- void loadTriggerSubscriptions();
1450
- void loadAvailableTools();
1451
- void loadOperationAllowances();
1452
- return () => {
1453
- active = false;
1454
- };
1455
- }, [
1456
- showAgentSettings,
1457
- agent?.id,
1458
- agent?.status,
1459
- agent?.userId,
1460
- agent?.profileId,
1461
- mode,
1462
- selectedSkillIds,
1463
- ]);
1464
- const activeTriggerSubscriptions = useMemo(() => triggerSubscriptions.filter((sub) => sub.isActive), [triggerSubscriptions]);
1465
- const allowanceGroups = useMemo(() => [
1466
- {
1467
- key: "sitecore",
1468
- label: "Sitecore",
1469
- rows: operationAllowances.sitecore,
1470
- },
1471
- {
1472
- key: "filesystem",
1473
- label: "Filesystem",
1474
- rows: operationAllowances.filesystem,
1475
- },
1476
- ], [operationAllowances]);
1477
- const hasAnyAllowances = useMemo(() => operationAllowances.sitecore.length > 0 ||
1478
- operationAllowances.filesystem.length > 0, [operationAllowances]);
1479
- const allowancesTotalCount = useMemo(() => operationAllowances.sitecore.length +
1480
- operationAllowances.filesystem.length, [operationAllowances]);
1481
- const listedProfileSkillIdSet = useMemo(() => {
1482
- const ids = [
1483
- ...(activeProfile?.availableSkills ?? []),
1484
- ...(activeProfile?.allowedSkills ?? []),
1485
- ]
1486
- .map((skill) => String(skill?.id || "").toLowerCase())
1487
- .filter((id) => id.length > 0);
1488
- return new Set(ids);
1489
- }, [activeProfile?.availableSkills, activeProfile?.allowedSkills]);
1490
- const profileFilteredSkills = useMemo(() => {
1491
- if (listedProfileSkillIdSet.size === 0) {
1492
- return [];
1493
- }
1494
- return availableSkills.filter((skill) => listedProfileSkillIdSet.has(skill.id.toLowerCase()));
1495
- }, [availableSkills, listedProfileSkillIdSet]);
1496
- const profileFilteredSkillIdSet = useMemo(() => new Set(profileFilteredSkills.map((s) => s.id.toLowerCase())), [profileFilteredSkills]);
1497
- const manuallyAssignableSkillIdSet = useMemo(() => new Set((activeProfile?.allowedSkills ?? [])
1498
- .map((skill) => String(skill?.id || "").toLowerCase())
1499
- .filter((id) => id.length > 0)), [activeProfile?.allowedSkills]);
1500
- const selectableTemplateIdSet = useMemo(() => new Set(selectableTemplateIds.map((id) => id.toLowerCase())), [selectableTemplateIds]);
1501
- const selectedSkills = useMemo(() => selectedSkillIds
1502
- .map((id) => availableSkills.find((s) => s.id.toLowerCase() === id.toLowerCase()))
1503
- .filter((s) => !!s), [availableSkills, selectedSkillIds]);
1504
- const selectedSkillSet = useMemo(() => new Set(selectedSkillIds.map((id) => id.toLowerCase())), [selectedSkillIds]);
1505
- const autoAssignedSkillSet = useMemo(() => new Set(autoAssignedSkillIds.map((id) => id.toLowerCase())), [autoAssignedSkillIds]);
1506
- const previewAvailableTools = useMemo(() => {
1507
- const baseToolNames = mode === "read-only"
1508
- ? (activeProfile?.readOnlyToolNames ?? [])
1509
- : (activeProfile?.allowedToolNames ?? []);
1510
- const toolNames = new Set();
1511
- for (const toolName of baseToolNames) {
1512
- const normalized = String(toolName || "").trim();
1513
- if (normalized) {
1514
- toolNames.add(normalized);
1515
- }
1516
- }
1517
- for (const skill of selectedSkills) {
1518
- for (const toolName of skill.additionalAllowedTools ?? []) {
1519
- const normalized = String(toolName || "").trim();
1520
- if (normalized) {
1521
- toolNames.add(normalized);
1522
- }
1523
- }
1524
- }
1525
- return Array.from(toolNames).sort((a, b) => a.localeCompare(b));
1526
- }, [
1527
- activeProfile?.allowedToolNames,
1528
- activeProfile?.readOnlyToolNames,
1529
- mode,
1530
- selectedSkills,
1531
- ]);
1532
- const displayedAvailableTools = isLocalOnlyDraftAgent
1533
- ? previewAvailableTools
1534
- : availableTools;
1535
- const displayedAvailableToolsLoading = isLocalOnlyDraftAgent
1536
- ? false
1537
- : availableToolsLoading;
1538
- const displayedAvailableToolsError = isLocalOnlyDraftAgent
1539
- ? null
1540
- : availableToolsError;
1541
813
  // Remove deprecated cost limit fields from metadata to avoid confusion with agent/profile settings
1542
814
  const sanitizeAgentMetadata = useCallback((meta) => {
1543
815
  try {
@@ -1562,239 +834,28 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1562
834
  return meta;
1563
835
  }
1564
836
  }, []);
1565
- const getSkillActionErrorMessage = useCallback((error) => {
1566
- const message = error instanceof Error && error.message.trim()
1567
- ? error.message.trim()
1568
- : "Failed to update skill";
1569
- if (message.includes("Skill is not available for this agent profile")) {
1570
- return "This skill cannot be added for the current agent profile.";
1571
- }
1572
- return message;
1573
- }, []);
1574
- const clearLegacySelectedSkillsFromMetadata = useCallback(async () => {
1575
- const legacySkillIds = metadataSelectedSkillIds;
1576
- if (!agent?.id || legacySkillIds.length === 0) {
1577
- return;
1578
- }
1579
- const current = agentMetadata || {};
1580
- const currentAdditionalData = current.additionalData ||
1581
- {};
1582
- const nextAdditionalData = Object.fromEntries(Object.entries(currentAdditionalData).filter(([key]) => key.toLowerCase() !== "skillids"));
1583
- const next = {
1584
- ...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,
1585
849
  };
1586
- if (Object.keys(nextAdditionalData).length > 0) {
1587
- next.additionalData = nextAdditionalData;
1588
- }
1589
- else {
1590
- delete next.additionalData;
1591
- }
1592
- try {
1593
- await updateAgentContext(agent.id, next);
1594
- setAgentMetadata(next);
1595
- setAgent((prev) => prev ? { ...prev, agentContext: JSON.stringify(next) } : prev);
1596
- }
1597
- catch (e) {
1598
- console.error("Failed to clear legacy selected skills", e);
1599
- }
1600
- }, [
1601
- agent?.id,
1602
- agentMetadata,
1603
- metadataSelectedSkillIds,
1604
- setAgent,
1605
- setAgentMetadata,
1606
- ]);
1607
- const parsePersistedAgentContext = (contextJson) => {
1608
- try {
1609
- if (!contextJson)
1610
- return null;
1611
- const parsedContext = JSON.parse(contextJson);
1612
- if (!parsedContext || typeof parsedContext !== "object") {
1613
- return null;
1614
- }
1615
- return sanitizeAgentMetadata(parsedContext);
1616
- }
1617
- catch {
1618
- return null;
1619
- }
1620
- };
1621
- const buildDraftPersistenceContext = () => {
1622
- let effectiveContext = agentMetadata;
1623
- try {
1624
- const normalizedAgentProfileId = agent?.profileId?.toLowerCase();
1625
- const profile = activeProfile ||
1626
- profiles.find((p) => p.id?.toLowerCase() === normalizedAgentProfileId);
1627
- const isLiveMode = profile?.editorContextMode === "live";
1628
- if (isLiveMode && typeof buildCurrentContext === "function") {
1629
- const freshContext = buildCurrentContext();
1630
- if (freshContext) {
1631
- effectiveContext = {
1632
- ...(agentMetadata || {}),
1633
- items: freshContext.items,
1634
- currentItemId: freshContext.currentItemId,
1635
- components: freshContext.components,
1636
- field: freshContext.field,
1637
- activeWorkspace: freshContext.activeWorkspace,
1638
- hasPageLoaded: freshContext.hasPageLoaded,
1639
- openSidebars: freshContext.openSidebars,
1640
- };
1641
- }
1642
- }
1643
- }
1644
- catch (e) {
1645
- console.warn("[AgentTerminal] Failed to compute draft context:", e);
1646
- }
1647
- return sanitizeAgentMetadata(effectiveContext || null);
1648
- };
1649
- const ensureDraftAgentPersisted = async () => {
1650
- if (!agent?.id) {
1651
- throw new Error("Agent not ready. Please try again.");
1652
- }
1653
- if (!isLocalOnlyDraftAgent) {
1654
- return agent;
1655
- }
1656
- const requestSettings = getPendingRequestSettings();
1657
- if (!requestSettings.profileId) {
1658
- throw new Error("Select an agent profile before adding a skill.");
1659
- }
1660
- const effectiveContext = buildDraftPersistenceContext();
1661
- const persistedAgent = await persistDraftAgent({
1662
- agentId: agent.id,
1663
- sessionId: editContext?.sessionId || undefined,
1664
- profileId: requestSettings.profileId,
1665
- mode: requestSettings.mode,
1666
- model: requestSettings.modelId || undefined,
1667
- name: agent.name,
1668
- context: effectiveContext,
1669
- });
1670
- pendingSettingsRef.current = null;
1671
- const persistedMetadata = parsePersistedAgentContext(persistedAgent.agentContext) ??
1672
- effectiveContext;
1673
- setAgentMetadata(persistedMetadata ? { ...persistedMetadata } : null);
1674
- setAgent((prev) => {
1675
- const next = {
1676
- ...(prev || {}),
1677
- ...persistedAgent,
1678
- };
1679
- if (prev?.messages?.length && !persistedAgent.messages?.length) {
1680
- next.messages = prev.messages;
1681
- }
1682
- return next;
1683
- });
1684
- onAgentUpdate?.(persistedAgent);
1685
- return persistedAgent;
1686
- };
1687
- const handleAddSkill = useCallback(async (skillId) => {
1688
- if (selectedSkillSet.has(skillId.toLowerCase()))
1689
- return;
1690
- if (!agent?.id)
1691
- return;
1692
- try {
1693
- setSkillActionError(null);
1694
- const persistedAgent = await ensureDraftAgentPersisted();
1695
- await assignAgentSkill({ agentId: persistedAgent.id, skillId });
1696
- setAgent((prev) => {
1697
- if (!prev)
1698
- return prev;
1699
- const currentAssigned = Array.isArray(prev.assignedSkillIds)
1700
- ? prev.assignedSkillIds
1701
- : [];
1702
- if (currentAssigned.some((existingId) => String(existingId).toLowerCase() === skillId.toLowerCase())) {
1703
- return prev;
1704
- }
1705
- return {
1706
- ...prev,
1707
- assignedSkillIds: [...currentAssigned, skillId],
1708
- };
1709
- });
1710
- await clearLegacySelectedSkillsFromMetadata();
1711
- return true;
1712
- }
1713
- catch (e) {
1714
- setSkillActionError(getSkillActionErrorMessage(e));
1715
- return false;
1716
- }
1717
- }, [
1718
- agent?.id,
1719
- assignAgentSkill,
1720
- clearLegacySelectedSkillsFromMetadata,
1721
- ensureDraftAgentPersisted,
1722
- getSkillActionErrorMessage,
1723
- setAgent,
1724
- selectedSkillSet,
1725
- ]);
1726
- const handleRemoveSkill = useCallback(async (skillId) => {
1727
- if (!agent?.id)
1728
- return;
1729
- try {
1730
- setSkillActionError(null);
1731
- await revokeAgentSkill({ agentId: agent.id, skillId });
1732
- setAgent((prev) => {
1733
- if (!prev)
1734
- return prev;
1735
- const currentAssigned = Array.isArray(prev.assignedSkillIds)
1736
- ? prev.assignedSkillIds
1737
- : [];
1738
- return {
1739
- ...prev,
1740
- assignedSkillIds: currentAssigned.filter((existingId) => String(existingId).toLowerCase() !== skillId.toLowerCase()),
1741
- };
1742
- });
1743
- await clearLegacySelectedSkillsFromMetadata();
1744
- }
1745
- catch (e) {
1746
- setSkillActionError(getSkillActionErrorMessage(e));
1747
- }
1748
- }, [
1749
- agent?.id,
1750
- clearLegacySelectedSkillsFromMetadata,
1751
- getSkillActionErrorMessage,
1752
- revokeAgentSkill,
1753
- setAgent,
1754
- ]);
1755
- const handleOpenProfileSettings = useCallback(async () => {
1756
- if (!editContext || !activeProfile?.id)
1757
- return;
1758
- const lang = editContext.currentItemDescriptor?.language || "en";
1759
- await editContext.loadItem({
1760
- id: activeProfile.id,
1761
- language: lang,
1762
- version: 0,
1763
- });
1764
- editContext.switchWorkspace("editor");
1765
- }, [editContext, activeProfile?.id]);
1766
- const handleEditProfileSideBySide = useCallback(async () => {
1767
- if (!editContext || !activeProfile?.id)
1768
- return;
1769
- const lang = editContext.currentItemDescriptor?.language || "en";
1770
- const profileItem = {
1771
- id: activeProfile.id,
1772
- language: lang,
1773
- version: 0,
850
+ }
851
+ catch {
852
+ deterministicFlags = {
853
+ deterministic: false,
854
+ seed: undefined,
1774
855
  };
1775
- if (editContext.workspaceId === "agents") {
1776
- editContext.setShowAgentsWorkspaceEditor(true);
1777
- await editContext.loadItem(profileItem);
1778
- return;
1779
- }
1780
- await editContext.loadItem(profileItem, {
1781
- openInNewSlot: true,
1782
- });
1783
- editContext.switchWorkspace("editor");
1784
- }, [editContext, activeProfile?.id]);
1785
- const handleOpenSkillItem = useCallback(async (skillId) => {
1786
- if (!editContext || !skillId)
1787
- return;
1788
- const lang = editContext.currentItemDescriptor?.language || "en";
1789
- await editContext.loadItem({
1790
- id: skillId,
1791
- language: lang,
1792
- version: 0,
1793
- });
1794
- editContext.switchWorkspace("editor");
1795
- }, [editContext]);
856
+ }
1796
857
  useEffect(() => {
1797
- localStorageService.setItem("editor.agent.promptHistory", promptHistory);
858
+ localStorage.setItem("editor.agent.promptHistory", JSON.stringify(promptHistory));
1798
859
  }, [promptHistory]);
1799
860
  useEffect(() => {
1800
861
  // Keep messagesRef synchronized with messages state
@@ -1806,32 +867,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1806
867
  const [liveTotals, setLiveTotals] = useState(null);
1807
868
  // Context window status from backend (extracted from delta cost object)
1808
869
  const [contextWindowStatus, setContextWindowStatus] = useState(null);
1809
- const effectiveModelName = useMemo(() => {
1810
- const fromContextWindow = contextWindowStatus?.model?.trim();
1811
- if (fromContextWindow)
1812
- return fromContextWindow;
1813
- const fromAgent = agent?.model?.trim();
1814
- if (fromAgent)
1815
- return fromAgent;
1816
- const models = activeProfile?.models || [];
1817
- const selectedModelName = selectedModelId
1818
- ? models.find((m) => m.id === selectedModelId)?.name?.trim()
1819
- : undefined;
1820
- if (selectedModelName)
1821
- return selectedModelName;
1822
- const defaultModelName = activeProfile?.defaultModelId
1823
- ? models.find((m) => m.id === activeProfile.defaultModelId)?.name?.trim()
1824
- : undefined;
1825
- if (defaultModelName)
1826
- return defaultModelName;
1827
- return models[0]?.name?.trim() || undefined;
1828
- }, [
1829
- contextWindowStatus?.model,
1830
- agent?.model,
1831
- activeProfile?.models,
1832
- activeProfile?.defaultModelId,
1833
- selectedModelId,
1834
- ]);
1835
870
  // Flag to track when we should create a new message
1836
871
  const shouldCreateNewMessage = useRef(false);
1837
872
  // Keep a ref to the current messages for immediate access
@@ -1842,13 +877,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1842
877
  const placeholderInputRef = useRef(null);
1843
878
  const promptPlaceholderInputRef = useRef(null);
1844
879
  const messagesContainerRef = useRef(null);
1845
- const inlineDialogContainerRef = useRef(null);
1846
880
  const [shouldAutoScroll, setShouldAutoScroll] = useState(true);
1847
881
  // WebSocket subscription state for agent streaming
1848
882
  const seenMessageIdsRef = useRef(new Set());
1849
883
  const lastSeqRef = useRef(0);
1850
884
  const subscribedAgentIdRef = useRef(null);
1851
- // 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)
1852
886
  const pendingSettingsRef = useRef(null);
1853
887
  // Track whether textarea should maintain focus during re-renders
1854
888
  const shouldMaintainFocusRef = useRef(false);
@@ -1870,11 +904,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1870
904
  setIsVoiceSupported(!!SR);
1871
905
  if (SR === undefined) {
1872
906
  setIsVoiceDisabled(true);
1873
- localStorageService.setString(VOICE_DISABLED_KEY, "true");
907
+ localStorage.setItem(VOICE_DISABLED_KEY, "true");
1874
908
  return;
1875
909
  }
1876
910
  else {
1877
- const wasDisabled = localStorageService.getString(VOICE_DISABLED_KEY) === "true";
911
+ const wasDisabled = localStorage.getItem(VOICE_DISABLED_KEY) === "true";
1878
912
  setIsVoiceDisabled(wasDisabled);
1879
913
  }
1880
914
  }
@@ -1885,12 +919,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1885
919
  // Auto-focus terminal input on mount
1886
920
  useEffect(() => {
1887
921
  if (textareaRef.current) {
1888
- try {
1889
- textareaRef.current.focus({ preventScroll: true });
1890
- }
1891
- catch {
1892
- textareaRef.current.focus();
1893
- }
922
+ textareaRef.current.focus();
1894
923
  }
1895
924
  }, []);
1896
925
  // Start voice recognition
@@ -1909,29 +938,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1909
938
  r.lang = editContext?.currentItemDescriptor?.language || "en-US";
1910
939
  r.continuous = true;
1911
940
  r.interimResults = true;
1912
- promptBeforeVoiceRef.current = prompt;
1913
941
  r.onstart = () => {
1914
942
  setIsListening(true);
1915
943
  prevPlaceholderRef.current = inputPlaceholder;
1916
944
  setInputPlaceholder("Listening...");
1917
945
  };
1918
- // Desktop Chrome: single result at index 0, transitions interim→final once.
1919
- // Mobile Chrome: new result index per event, ALL immediately isFinal,
1920
- // each containing the full cumulative transcript. We always take the last
1921
- // non-empty final result and REPLACE the prompt to handle both patterns.
1922
946
  r.onresult = (event) => {
1923
- let lastFinalTranscript = "";
947
+ let finalText = "";
1924
948
  let interimText = "";
1925
- for (let i = event.results.length - 1; i >= 0; i--) {
949
+ for (let i = event.resultIndex; i < event.results.length; i++) {
1926
950
  const res = event.results[i];
1927
- if (res.isFinal &&
1928
- !lastFinalTranscript &&
1929
- (res[0]?.transcript || "").trim()) {
1930
- lastFinalTranscript = res[0].transcript;
1931
- }
1932
- if (!res.isFinal) {
1933
- interimText = (res[0]?.transcript || "") + interimText;
1934
- }
951
+ if (res.isFinal)
952
+ finalText += res[0]?.transcript || "";
953
+ else
954
+ interimText += res[0]?.transcript || "";
1935
955
  }
1936
956
  if (interimText) {
1937
957
  setInputPlaceholder(`Listening... ${interimText.trim()}`);
@@ -1939,11 +959,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1939
959
  else {
1940
960
  setInputPlaceholder("Listening...");
1941
961
  }
1942
- const base = promptBeforeVoiceRef.current;
1943
- const separator = base && !base.endsWith(" ") ? " " : "";
1944
- const recognized = lastFinalTranscript.trim();
1945
- if (recognized) {
1946
- 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
+ });
1947
967
  if (textareaRef.current) {
1948
968
  try {
1949
969
  const v = textareaRef.current.value || "";
@@ -1959,7 +979,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1959
979
  // network error also comes from incompatible chromium client
1960
980
  if (e?.error === "network") {
1961
981
  try {
1962
- localStorageService.setString(VOICE_DISABLED_KEY, "true");
982
+ localStorage.setItem(VOICE_DISABLED_KEY, "true");
1963
983
  setIsVoiceDisabled(true);
1964
984
  }
1965
985
  catch { }
@@ -2128,18 +1148,25 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2128
1148
  // Shared stream message handlers with messageId support
2129
1149
  const createNewStreamMessage = useCallback((messageId, agentData) => {
2130
1150
  const currentAgent = agentData || agent;
2131
- 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
+ }
2132
1159
  // Reduced: avoid verbose logging during streaming
2133
1160
  return {
2134
1161
  id: messageId,
2135
- agentId: effectiveAgentId,
1162
+ agentId: currentAgent.id,
2136
1163
  messageIndex: -1,
2137
1164
  role: "assistant",
2138
1165
  content: "",
2139
1166
  name: "agent",
2140
1167
  messageType: "streaming",
2141
1168
  isCompleted: false,
2142
- model: currentAgent?.model || "",
1169
+ model: currentAgent.model || "",
2143
1170
  tokensUsed: 0,
2144
1171
  inputTokens: 0,
2145
1172
  outputTokens: 0,
@@ -2148,71 +1175,16 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2148
1175
  outputTokenCost: 0,
2149
1176
  cachedInputTokenCost: 0,
2150
1177
  totalCost: 0,
2151
- currency: currentAgent?.currency || "USD",
1178
+ currency: currentAgent.currency || "USD",
2152
1179
  createdDate: new Date().toISOString(),
2153
1180
  toolCalls: [],
2154
1181
  };
2155
- }, [agent, agentStub.id]);
2156
- const stripHeartbeatMessages = useCallback((currentMessages) => currentMessages.filter((message) => message.messageType !== "heartbeat"), []);
2157
- const handleHeartbeatMessage = useCallback((message) => {
2158
- const heartbeatText = (message.data?.message ||
2159
- message.data?.Message ||
2160
- "Still working on your request. This is taking a little longer than usual.").trim() ||
2161
- "Still working on your request. This is taking a little longer than usual.";
2162
- const createdDate = message.timestamp || new Date().toISOString();
2163
- const heartbeatId = `heartbeat-${agent?.id || agentStub.id}`;
2164
- setMessages((prev) => {
2165
- const withoutHeartbeats = stripHeartbeatMessages(prev);
2166
- const updated = [
2167
- ...withoutHeartbeats,
2168
- {
2169
- id: heartbeatId,
2170
- agentId: agent?.id || agentStub.id,
2171
- messageIndex: -1,
2172
- role: "system",
2173
- content: heartbeatText,
2174
- name: "system",
2175
- messageType: "heartbeat",
2176
- isCompleted: true,
2177
- model: "",
2178
- tokensUsed: 0,
2179
- inputTokens: 0,
2180
- outputTokens: 0,
2181
- cachedInputTokens: 0,
2182
- inputTokenCost: 0,
2183
- outputTokenCost: 0,
2184
- cachedInputTokenCost: 0,
2185
- totalCost: 0,
2186
- currency: "USD",
2187
- createdDate,
2188
- toolCalls: [],
2189
- },
2190
- ];
2191
- messagesRef.current = updated;
2192
- return updated;
2193
- });
2194
- }, [agent?.id, agentStub.id, stripHeartbeatMessages]);
2195
- const clearHeartbeatMessages = useCallback(() => {
2196
- setMessages((prev) => {
2197
- const updated = stripHeartbeatMessages(prev);
2198
- if (updated.length === prev.length) {
2199
- return prev;
2200
- }
2201
- messagesRef.current = updated;
2202
- return updated;
2203
- });
2204
- }, [stripHeartbeatMessages]);
1182
+ }, [agent]);
2205
1183
  const handleContentChunk = useCallback((message, agentData) => {
2206
1184
  // Get messageId from data, or generate one from agent ID (for backward compatibility)
2207
1185
  // If no messageId is provided, we'll use the last assistant message or create a new one
2208
1186
  let messageId = message.data?.messageId;
2209
1187
  if (!messageId && agentData?.id) {
2210
- console.warn("[AgentTerminal] Content chunk missing messageId; falling back to local resolution", {
2211
- agentId: agentData.id,
2212
- isIncremental: message.data?.isIncremental,
2213
- previousContentLength: message.data?.previousContentLength,
2214
- totalContentLength: message.data?.totalContentLength,
2215
- });
2216
1188
  // For backward compatibility: if no messageId, find or create the current streaming message
2217
1189
  // This handles cases where the backend doesn't send messageId
2218
1190
  const currentMessages = messagesRef.current;
@@ -2226,7 +1198,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2226
1198
  // If the agent isn't currently running (e.g., we switched tabs after the run
2227
1199
  // completed), skip creating a new streaming message to avoid duplicates.
2228
1200
  const currentAgentStatus = (agentData || agent)?.status;
2229
- const isAgentRunning = currentAgentStatus === "running";
1201
+ const isAgentRunning = currentAgentStatus === "running" || currentAgentStatus === 1;
2230
1202
  if (!isAgentRunning) {
2231
1203
  return;
2232
1204
  }
@@ -2259,8 +1231,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2259
1231
  outputCost: Number(cost.output) || 0,
2260
1232
  cachedCost: Number(cost.cached) || 0,
2261
1233
  cacheWriteCost: Number(cost.cacheWrite) || 0,
2262
- imageCost: Number(cost.imageCost ?? cost.totalImageCost) ||
2263
- 0,
2264
1234
  totalCost: Number(cost.total) || 0,
2265
1235
  currency: "USD",
2266
1236
  };
@@ -2284,20 +1254,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2284
1254
  setIsWaitingForResponse(false);
2285
1255
  shouldCreateNewMessage.current = false;
2286
1256
  }
2287
- // Extract context window info from cost object.
2288
- // Note: contextUsed can legitimately be 0 (especially early in OpenAI streams),
2289
- // so we must check for null/undefined instead of truthiness.
2290
- const contextWindowRaw = cost.contextWindow;
2291
- const contextUsedRaw = cost.contextUsed;
2292
- if (contextWindowRaw !== undefined &&
2293
- contextWindowRaw !== null &&
2294
- contextUsedRaw !== undefined &&
2295
- contextUsedRaw !== null) {
2296
- const contextWindowValue = Number(contextWindowRaw);
2297
- const contextUsedValue = Number(contextUsedRaw);
2298
- if (contextWindowValue > 0 &&
2299
- Number.isFinite(contextUsedValue) &&
2300
- 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) {
2301
1262
  setContextWindowStatus({
2302
1263
  contextWindowTokens: contextWindowValue,
2303
1264
  estimatedInputTokens: contextUsedValue,
@@ -2313,15 +1274,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2313
1274
  const existingMessageIndex = prev.findIndex((msg) => msg.id === messageId);
2314
1275
  if (existingMessageIndex === -1) {
2315
1276
  // Message doesn't exist - create new streaming message
2316
- const previousContentLength = message.data?.previousContentLength || 0;
2317
- if (message.data?.isIncremental && previousContentLength > 0) {
2318
- console.warn("[AgentTerminal] Incremental chunk arrived before its base message existed", {
2319
- messageId,
2320
- previousContentLength,
2321
- totalContentLength: message.data?.totalContentLength,
2322
- deltaLength: (message.data?.deltaContent || "").length,
2323
- });
2324
- }
2325
1277
  const newStreamMessage = createNewStreamMessage(messageId, agentData);
2326
1278
  // Set the content for the new message
2327
1279
  const updatedNewMessage = { ...newStreamMessage };
@@ -2344,21 +1296,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2344
1296
  return prev;
2345
1297
  // Check if existing content is already longer than what we're trying to stream
2346
1298
  const currentContentLength = existingMessage.content?.length || 0;
2347
- const previousContentLength = message.data?.previousContentLength || 0;
2348
1299
  const totalContentLength = message.data?.totalContentLength || 0;
2349
- if (message.data?.isIncremental &&
2350
- previousContentLength !== currentContentLength &&
2351
- (previousContentLength > 0 || currentContentLength > 0)) {
2352
- console.warn("[AgentTerminal] Content chunk length mismatch", {
2353
- messageId,
2354
- previousContentLength,
2355
- currentContentLength,
2356
- totalContentLength,
2357
- deltaLength: (message.data?.deltaContent || "").length,
2358
- });
2359
- }
2360
- if (message.data?.isIncremental &&
2361
- currentContentLength >= totalContentLength &&
1300
+ if (currentContentLength >= totalContentLength &&
2362
1301
  totalContentLength > 0) {
2363
1302
  return prev;
2364
1303
  }
@@ -2382,15 +1321,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2382
1321
  });
2383
1322
  }, [createNewStreamMessage, agent]);
2384
1323
  const handleToolCall = useCallback((message, agentData) => {
2385
- const extractedToolCall = extractToolCallFields(message.data);
2386
- const toolCallId = extractedToolCall.toolCallId || crypto.randomUUID();
1324
+ const toolCallId = message.data?.toolCallId || message.data?.id || crypto.randomUUID();
2387
1325
  // Prefer provided messageId, otherwise fall back to the last streaming assistant message
2388
1326
  let toolCallMessageId = message.data?.messageId;
2389
1327
  if (!toolCallMessageId) {
2390
- console.warn("[AgentTerminal] Tool call missing messageId; falling back", {
2391
- toolCallId,
2392
- toolName: message.data?.name || message.data?.displayName,
2393
- });
2394
1328
  const current = messagesRef.current;
2395
1329
  const lastStreaming = [...current]
2396
1330
  .reverse()
@@ -2398,11 +1332,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2398
1332
  if (lastStreaming?.id) {
2399
1333
  toolCallMessageId = lastStreaming.id;
2400
1334
  }
2401
- else {
2402
- // Tool calls can arrive before any assistant content chunk (common for dialog tools like ask-questionnaire).
2403
- // Create a synthetic streaming message so the UI can render the tool call immediately.
2404
- toolCallMessageId = crypto.randomUUID();
2405
- }
2406
1335
  }
2407
1336
  // Find or create the target message for this tool call
2408
1337
  if (toolCallMessageId) {
@@ -2430,25 +1359,24 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2430
1359
  }
2431
1360
  // Add tool call to the message in the array
2432
1361
  if (toolCallMessageId && message.data && toolCallId) {
2433
- const toolCallError = message.data.functionError || message.data.error || "";
2434
- const isPruned = !!message.data?.isPruned || /^PRUNED$/i.test(String(toolCallError));
2435
- const toolCallCreatedDate = message.data.createdDate ||
2436
- message.timestamp ||
2437
- new Date().toISOString();
1362
+ const functionName = message.data.functionName ||
1363
+ message.data.name ||
1364
+ message.data.function?.name ||
1365
+ "unknown";
2438
1366
  const toolCall = {
2439
1367
  id: toolCallId,
2440
1368
  messageId: toolCallMessageId,
2441
1369
  dbMessageId: message.data.messageId, // Database message ID for approval/rejection
2442
1370
  toolCallId: toolCallId,
2443
- functionName: extractedToolCall.functionName,
2444
- functionArguments: extractedToolCall.functionArguments,
1371
+ functionName: functionName,
1372
+ functionArguments: message.data.functionArguments ||
1373
+ message.data.arguments ||
1374
+ JSON.stringify(message.data.function?.arguments || {}),
2445
1375
  functionResult: message.data.functionResult || message.data.result || "",
2446
- functionResultRichContent: message.data.richContent || undefined,
2447
- functionError: toolCallError,
2448
- isPruned,
1376
+ functionError: message.data.functionError || message.data.error || "",
2449
1377
  isCompleted: false,
2450
1378
  responseTimeMs: message.data.responseTimeMs,
2451
- createdDate: toolCallCreatedDate,
1379
+ createdDate: new Date().toISOString(),
2452
1380
  requiresApproval: message.data?.requiresApproval,
2453
1381
  };
2454
1382
  // Check for existing tool call - search across ALL messages by toolCallId first
@@ -2487,21 +1415,14 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2487
1415
  // Check if the new data has more information than what we have
2488
1416
  const newArgs = toolCall.functionArguments;
2489
1417
  const existingArgs = existingToolCall.functionArguments;
2490
- const newArgsText = stringifyToolField(newArgs) || "";
2491
- const existingArgsText = stringifyToolField(existingArgs) || "";
2492
- const hasMoreCompleteArgs = (newArgsText.length > existingArgsText.length &&
2493
- newArgsText !== existingArgsText) ||
2494
- (existingArgsText === "{}" && newArgsText !== "{}");
1418
+ const hasMoreCompleteArgs = newArgs && newArgs.length > (existingArgs?.length || 0);
2495
1419
  const hasNewResult = toolCall.functionResult && !existingToolCall.functionResult;
2496
- const hasNewRichContent = toolCall.functionResultRichContent &&
2497
- !existingToolCall.functionResultRichContent;
2498
1420
  const hasNewError = toolCall.functionError && !existingToolCall.functionError;
2499
1421
  const hasNewApprovalInfo = toolCall.requiresApproval && !existingToolCall.requiresApproval;
2500
1422
  const hasNewDbMessageId = toolCall.dbMessageId && !existingToolCall.dbMessageId;
2501
1423
  // Only update if there's meaningful new data
2502
1424
  if (hasMoreCompleteArgs ||
2503
1425
  hasNewResult ||
2504
- hasNewRichContent ||
2505
1426
  hasNewError ||
2506
1427
  hasNewApprovalInfo ||
2507
1428
  hasNewDbMessageId) {
@@ -2518,11 +1439,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2518
1439
  updatedToolCalls[idx] = {
2519
1440
  ...existing,
2520
1441
  functionArguments: hasMoreCompleteArgs
2521
- ? newArgsText
2522
- : existingArgsText || existing.functionArguments,
1442
+ ? newArgs
1443
+ : existing.functionArguments,
2523
1444
  functionResult: toolCall.functionResult || existing.functionResult,
2524
- functionResultRichContent: toolCall.functionResultRichContent ||
2525
- existing.functionResultRichContent,
2526
1445
  functionError: toolCall.functionError || existing.functionError,
2527
1446
  requiresApproval: toolCall.requiresApproval || existing.requiresApproval,
2528
1447
  };
@@ -2557,15 +1476,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2557
1476
  }
2558
1477
  }, [createNewStreamMessage]);
2559
1478
  const handleToolResult = useCallback((message, agentData) => {
2560
- const extractedToolCall = extractToolCallFields(message.data);
2561
- const resultToolCallId = extractedToolCall.toolCallId || crypto.randomUUID();
1479
+ const resultToolCallId = message.data?.toolCallId || message.data?.id || crypto.randomUUID();
2562
1480
  // Prefer provided messageId, otherwise fall back to the last streaming assistant message
2563
1481
  let resultMessageId = message.data?.messageId;
2564
1482
  if (!resultMessageId) {
2565
- console.warn("[AgentTerminal] Tool result missing messageId; falling back", {
2566
- toolCallId: resultToolCallId,
2567
- toolName: message.data?.functionName || message.data?.displayName,
2568
- });
2569
1483
  const current = messagesRef.current;
2570
1484
  const lastStreaming = [...current]
2571
1485
  .reverse()
@@ -2587,7 +1501,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2587
1501
  outputCost: Number(cost.output) || 0,
2588
1502
  cachedCost: Number(cost.cached) || 0,
2589
1503
  cacheWriteCost: Number(cost.cacheWrite) || 0,
2590
- imageCost: Number(cost.imageCost) || 0,
2591
1504
  totalCost: Number(cost.total) || 0,
2592
1505
  currency: "USD",
2593
1506
  };
@@ -2599,20 +1512,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2599
1512
  if (anyNonZero) {
2600
1513
  setLiveTotals(nextTotals);
2601
1514
  }
2602
- // Extract context window info from cost object.
2603
- // Note: contextUsed can legitimately be 0 (especially early in OpenAI streams),
2604
- // so we must check for null/undefined instead of truthiness.
2605
- const contextWindowRaw = cost.contextWindow;
2606
- const contextUsedRaw = cost.contextUsed;
2607
- if (contextWindowRaw !== undefined &&
2608
- contextWindowRaw !== null &&
2609
- contextUsedRaw !== undefined &&
2610
- contextUsedRaw !== null) {
2611
- const contextWindowValue = Number(contextWindowRaw);
2612
- const contextUsedValue = Number(contextUsedRaw);
2613
- if (contextWindowValue > 0 &&
2614
- Number.isFinite(contextUsedValue) &&
2615
- 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) {
2616
1520
  setContextWindowStatus({
2617
1521
  contextWindowTokens: contextWindowValue,
2618
1522
  estimatedInputTokens: contextUsedValue,
@@ -2636,7 +1540,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2636
1540
  outputCost: Number(data.totalOutputTokenCost) || 0,
2637
1541
  cachedCost: Number(data.totalCachedTokenCost) || 0,
2638
1542
  cacheWriteCost: Number(data.totalCacheWriteTokenCost) || 0,
2639
- imageCost: Number(data.totalImageCost) || 0,
2640
1543
  totalCost: Number(data.totalCost) || 0,
2641
1544
  currency: data.currency || "USD",
2642
1545
  };
@@ -2669,22 +1572,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2669
1572
  const existingToolCall = updatedMessage.toolCalls[toolCallIndex];
2670
1573
  if (existingToolCall && message.data) {
2671
1574
  const updatedToolCalls = [...updatedMessage.toolCalls];
2672
- const nextArgsText = stringifyToolField(extractedToolCall.functionArguments) || "";
2673
- const existingArgsText = stringifyToolField(existingToolCall.functionArguments) || "";
2674
- const hasMoreCompleteArgs = (nextArgsText.length > existingArgsText.length &&
2675
- nextArgsText !== existingArgsText) ||
2676
- (existingArgsText === "{}" && nextArgsText !== "{}");
2677
1575
  const toolCall = {
2678
1576
  id: existingToolCall.id,
2679
1577
  messageId: existingToolCall.messageId,
2680
1578
  toolCallId: existingToolCall.toolCallId,
2681
1579
  functionName: existingToolCall.functionName,
2682
- functionArguments: hasMoreCompleteArgs
2683
- ? nextArgsText
2684
- : existingToolCall.functionArguments,
1580
+ functionArguments: existingToolCall.functionArguments,
2685
1581
  functionResult: message.data.functionResult || message.data.result || "",
2686
- functionResultRichContent: message.data.richContent ||
2687
- existingToolCall.functionResultRichContent,
2688
1582
  functionError: message.data.functionError || message.data.error || "",
2689
1583
  isCompleted: true,
2690
1584
  responseTimeMs: message.data.responseTimeMs,
@@ -2701,21 +1595,23 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2701
1595
  }
2702
1596
  else if (message.data && resultToolCallId && resultMessageId) {
2703
1597
  // Create new tool call if it doesn't exist
2704
- const toolCallCreatedDate = message.data.createdDate ||
2705
- message.timestamp ||
2706
- new Date().toISOString();
1598
+ const functionName = message.data.functionName ||
1599
+ message.data.name ||
1600
+ message.data.function?.name ||
1601
+ "unknown";
2707
1602
  const toolCall = {
2708
1603
  id: resultToolCallId,
2709
1604
  messageId: resultMessageId,
2710
1605
  toolCallId: resultToolCallId,
2711
- functionName: extractedToolCall.functionName,
2712
- functionArguments: extractedToolCall.functionArguments,
1606
+ functionName: functionName,
1607
+ functionArguments: message.data.functionArguments ||
1608
+ message.data.arguments ||
1609
+ JSON.stringify(message.data.function?.arguments || {}),
2713
1610
  functionResult: message.data.functionResult || message.data.result || "",
2714
- functionResultRichContent: message.data.richContent || undefined,
2715
1611
  functionError: message.data.functionError || message.data.error || "",
2716
1612
  isCompleted: true,
2717
1613
  responseTimeMs: message.data.responseTimeMs,
2718
- createdDate: toolCallCreatedDate,
1614
+ createdDate: new Date().toISOString(),
2719
1615
  };
2720
1616
  updatedMessage.toolCalls = [...updatedMessage.toolCalls, toolCall];
2721
1617
  }
@@ -2806,9 +1702,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2806
1702
  // The agent might have been persisted after sending a prompt
2807
1703
  // Only treat as "new" if backend returns 404
2808
1704
  const hasExistingMessages = messagesRef.current.length > 0;
2809
- if (agentStub.status === "new" &&
2810
- !agentStub.userId &&
2811
- !hasExistingMessages) {
1705
+ if (agentStub.status === "new" && !hasExistingMessages) {
2812
1706
  // Only initialize as new if we have no messages yet (initial mount)
2813
1707
  // Derive initial profile from provided metadata if present
2814
1708
  const initialProfileIdFromMeta = (() => {
@@ -2848,7 +1742,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2848
1742
  totalInputTokenCost: 0,
2849
1743
  totalOutputTokenCost: 0,
2850
1744
  totalCachedInputTokenCost: 0,
2851
- totalImageCost: 0,
2852
1745
  totalCost: 0,
2853
1746
  messageCount: 0,
2854
1747
  });
@@ -2891,7 +1784,48 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2891
1784
  }
2892
1785
  })();
2893
1786
  // Create context with top-level properties (what ContextInfoBar expects)
2894
- 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;
2895
1829
  let nextMetadata = null;
2896
1830
  if (initialMetadata) {
2897
1831
  // Merge initial metadata with local context (using top-level structure)
@@ -2967,16 +1901,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2967
1901
  seenMessageIdsRef.current.add(msg.id.toLowerCase());
2968
1902
  }
2969
1903
  });
2970
- // Keep local streaming if the agent is still active (running/waiting); otherwise discard locals.
2971
- // This is important for dialog-style tools (e.g., ask-questionnaire) where the agent may be
2972
- // "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
2973
1905
  const isRunning = agentData.status === "running" || agentData.status === 1;
2974
- const isWaiting = agentData.status === "waitingForApproval" ||
2975
- agentData.status === 2 ||
2976
- agentData.status === "waitingForInput" ||
2977
- agentData.status === "costLimitReached" ||
2978
- agentData.status === 7;
2979
- const keepLocalStreaming = isRunning || isWaiting;
2980
1906
  // Filter local messages to only include streaming/incomplete messages that aren't in DB
2981
1907
  // This prevents duplicates when reloading - DB messages are source of truth for completed messages
2982
1908
  const localStreaming = isRunning
@@ -2994,19 +1920,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2994
1920
  // Don't keep completed messages from local state - DB is source of truth
2995
1921
  return false;
2996
1922
  })
2997
- : keepLocalStreaming
2998
- ? messagesRef.current.filter((localMsg) => {
2999
- if (!localMsg.id)
3000
- return false;
3001
- if (!localMsg.isCompleted &&
3002
- localMsg.messageType === "streaming") {
3003
- const existsInDb = dbMessages.some((dbMsg) => dbMsg.id &&
3004
- dbMsg.id.toLowerCase() === localMsg.id.toLowerCase());
3005
- return !existsInDb;
3006
- }
3007
- return false;
3008
- })
3009
- : [];
1923
+ : [];
3010
1924
  const merged = mergeMessagesById(dbMessages, localStreaming);
3011
1925
  messagesRef.current = merged;
3012
1926
  setMessages(merged);
@@ -3017,7 +1931,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3017
1931
  const runningNow = agentData.status === "running" || agentData.status === 1;
3018
1932
  const waitingForApprovalNow = agentData.status === "waitingForApproval" ||
3019
1933
  agentData.status === 2;
3020
- const waitingForInputNow = agentData.status === "waitingForInput";
3021
1934
  const hasStreamingNow = merged.some((m) => !m.isCompleted && m.messageType === "streaming");
3022
1935
  if (runningNow || hasStreamingNow) {
3023
1936
  setIsWaitingForResponse(true);
@@ -3025,11 +1938,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3025
1938
  // Agent is actively running, show thinking dots
3026
1939
  setIsAgentThinking(true);
3027
1940
  }
3028
- else if (waitingForApprovalNow || waitingForInputNow) {
1941
+ else if (waitingForApprovalNow) {
3029
1942
  setIsWaitingForResponse(false);
3030
1943
  isWaitingRef.current = false;
3031
1944
  setIsConnecting(false);
3032
- // Agent is waiting for user input/approval, hide thinking dots
1945
+ // Agent is waiting for user approval, hide thinking dots
3033
1946
  setIsAgentThinking(false);
3034
1947
  }
3035
1948
  else {
@@ -3078,8 +1991,19 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3078
1991
  if (!contextJson)
3079
1992
  return null;
3080
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
3081
1997
  if (parsedContext && typeof parsedContext === "object") {
3082
- 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;
3083
2007
  }
3084
2008
  return null;
3085
2009
  }
@@ -3165,7 +2089,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3165
2089
  return;
3166
2090
  }
3167
2091
  // Check if cost limit exceeded based on status or cost values
3168
- const statusIndicatesLimit = agent.status === "costLimitReached";
2092
+ const statusIndicatesLimit = agent.status === "costLimitReached" || agent.status === 7;
3169
2093
  // Use liveTotals.totalCost as fallback if agent.totalCost is missing or 0
3170
2094
  const effectiveTotalCost = agent.totalCost || liveTotals?.totalCost || 0;
3171
2095
  const costExceedsLimit = agent.costLimit &&
@@ -3250,9 +2174,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3250
2174
  // Handle agent:profile:switched (profile changed via switch-profile function)
3251
2175
  if (messageType === "agent:profile:switched") {
3252
2176
  const payload = message.payload || {};
3253
- const switchedAgentId = payload.agentId;
3254
- const newProfileId = payload.newProfileId;
3255
- 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;
3256
2180
  if (switchedAgentId === agent.id && newProfileId) {
3257
2181
  // Update the agent's profile
3258
2182
  setAgent((prev) => {
@@ -3293,10 +2217,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3293
2217
  return;
3294
2218
  }
3295
2219
  // For other agent messages, check if this is for our agent
3296
- const agentId = message.payload?.agentId;
3297
- if (agentId !== agent.id) {
2220
+ const agentId = message.payload?.agentId || message.payload?.AgentId;
2221
+ if (agentId !== agent.id)
3298
2222
  return;
3299
- }
3300
2223
  // Handle agent:run:start
3301
2224
  if (messageType === "agent:run:start") {
3302
2225
  // If a stop operation is in progress, ignore this message to prevent
@@ -3400,9 +2323,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3400
2323
  if (messageType === "agent:prompt:queued") {
3401
2324
  const { queueEntry } = message.payload;
3402
2325
  if (queueEntry) {
3403
- if (shouldSuppressQueuedPrompt(queueEntry)) {
3404
- return;
3405
- }
3406
2326
  // Add the new prompt to the queued prompts list
3407
2327
  setQueuedPrompts((prev) => {
3408
2328
  // Check if prompt already exists (deduplication)
@@ -3431,29 +2351,17 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3431
2351
  return;
3432
2352
  }
3433
2353
  const { seq, type, data, cost } = message.payload;
3434
- if (type === "ToolCall" || type === "toolCall") {
3435
- }
3436
2354
  // Always allow ContextUpdate messages (metadata updates) regardless of agent status
3437
2355
  const isContextUpdate = type === "ContextUpdate" || type === "contextUpdate";
3438
- const isHeartbeat = type === "Heartbeat" || type === "heartbeat";
3439
- const shouldClearHeartbeat = type === "ContentChunk" ||
3440
- type === "contentChunk" ||
3441
- type === "ToolCall" ||
3442
- type === "toolCall" ||
3443
- type === "ToolResult" ||
3444
- type === "toolResult";
3445
2356
  // Allow deltas if the agent is running OR if we already have an active streaming message
3446
2357
  // OR if the server provided a messageId for targeted updates.
3447
2358
  // This avoids dropping early deltas that may arrive before the 'running' status update.
3448
2359
  // ContextUpdate messages are always allowed as they're metadata updates, not content.
3449
2360
  const isRunning = agent.status === "running" || agent.status === 1;
3450
2361
  const hasStreaming = messagesRef.current.some((m) => !m.isCompleted && m.messageType === "streaming");
3451
- const hasMessageId = !!message?.payload?.data?.messageId;
3452
- if (!isContextUpdate &&
3453
- !isHeartbeat &&
3454
- !isRunning &&
3455
- !hasStreaming &&
3456
- !hasMessageId) {
2362
+ const hasMessageId = !!message?.payload?.data?.messageId ||
2363
+ !!message?.payload?.data?.MessageId;
2364
+ if (!isContextUpdate && !isRunning && !hasStreaming && !hasMessageId) {
3457
2365
  return; // ignore only if we cannot safely target an existing streaming message
3458
2366
  }
3459
2367
  // Deduplicate by sequence
@@ -3470,9 +2378,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3470
2378
  cost,
3471
2379
  timestamp: new Date().toISOString(),
3472
2380
  };
3473
- if (shouldClearHeartbeat) {
3474
- clearHeartbeatMessages();
3475
- }
3476
2381
  if (type === "ContentChunk" || type === "contentChunk") {
3477
2382
  handleContentChunk(agentStreamMessage, agent);
3478
2383
  }
@@ -3482,9 +2387,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3482
2387
  else if (type === "ToolResult" || type === "toolResult") {
3483
2388
  handleToolResult(agentStreamMessage, agent);
3484
2389
  }
3485
- else if (type === "Heartbeat" || type === "heartbeat") {
3486
- handleHeartbeatMessage(agentStreamMessage);
3487
- }
3488
2390
  else if (type === "ContextUpdate" || type === "contextUpdate") {
3489
2391
  // Handle context updates from streaming - data contains additionalData.todoList and ChildAgents
3490
2392
  const contextData = data;
@@ -3494,8 +2396,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3494
2396
  const current = (prev || {});
3495
2397
  const updated = { ...current };
3496
2398
  // Merge additionalData if present (deep merge to preserve existing values)
3497
- if (contextData.additionalData) {
3498
- const sourceAdditionalData = contextData.additionalData || {};
2399
+ if (contextData.additionalData || contextData.AdditionalData) {
2400
+ const sourceAdditionalData = contextData.additionalData ||
2401
+ contextData.AdditionalData ||
2402
+ {};
3499
2403
  updated.additionalData = {
3500
2404
  ...(current.additionalData || {}),
3501
2405
  ...sourceAdditionalData,
@@ -3503,10 +2407,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3503
2407
  }
3504
2408
  // Merge ChildAgents if present (for spawned agents) - replace entire array
3505
2409
  // Backend sends the complete updated list, not just additions
3506
- if (contextData.childAgents) {
3507
- const childAgents = contextData.childAgents;
2410
+ if (contextData.ChildAgents || contextData.childAgents) {
2411
+ const childAgents = contextData.ChildAgents || contextData.childAgents;
3508
2412
  if (Array.isArray(childAgents)) {
3509
- updated.childAgents = childAgents;
2413
+ updated.ChildAgents = childAgents;
3510
2414
  }
3511
2415
  }
3512
2416
  return updated;
@@ -3532,18 +2436,15 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3532
2436
  // Route based on statusData.state
3533
2437
  try {
3534
2438
  // Normalize various status shapes and handle Cancelled uniformly
3535
- const normalizedStatus = parseAgentStatus(statusData?.state) ||
3536
- parseAgentStatus(statusData?.status);
3537
- if (normalizedStatus === "idle") {
2439
+ const normalizedStatus = statusData?.state ||
2440
+ statusData?.Status ||
2441
+ statusData?.status;
2442
+ if (normalizedStatus === "Cancelled" ||
2443
+ normalizedStatus === "canceled") {
3538
2444
  // Stop indicators and mark any in-progress streaming messages as completed
3539
- clearHeartbeatMessages();
3540
- setAgent((prev) => (prev ? { ...prev, status: "idle" } : prev));
3541
2445
  setIsWaitingForResponse(false);
3542
2446
  isWaitingRef.current = false;
3543
2447
  setIsConnecting(false);
3544
- setIsSubmitting(false);
3545
- shouldCreateNewMessage.current = false;
3546
- setIsAgentThinking(false);
3547
2448
  setMessages((prev) => {
3548
2449
  const updated = prev.map((msg) => !msg.isCompleted && msg.messageType === "streaming"
3549
2450
  ? {
@@ -3586,7 +2487,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3586
2487
  outputCost: Number(totals.totalOutputTokenCost) || 0,
3587
2488
  cachedCost: Number(totals.totalCachedInputTokenCost) || 0,
3588
2489
  cacheWriteCost: Number(totals.totalCacheWriteTokenCost) || 0,
3589
- imageCost: Number(totals.totalImageCost) || 0,
3590
2490
  totalCost: totalCost,
3591
2491
  currency: totals.currency,
3592
2492
  };
@@ -3598,26 +2498,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3598
2498
  if (anyNonZero) {
3599
2499
  setLiveTotals(nextTotals);
3600
2500
  }
3601
- // Fallback context usage update for providers that do not include
3602
- // context usage in every stream delta (for example some OpenAI streams).
3603
- // Prefer explicit status values when present; otherwise derive from totals.
3604
- const contextWindowValue = Number(statusData?.contextWindow ??
3605
- agent?.contextWindowTokens ??
3606
- 0);
3607
- const contextUsedValue = Number(statusData?.contextUsed ??
3608
- (Number(totals.totalInputTokens) || 0) +
3609
- (Number(totals.totalCachedInputTokens) || 0) +
3610
- (Number(totals.totalCacheWriteTokens) || 0));
3611
- if (contextWindowValue > 0 &&
3612
- Number.isFinite(contextUsedValue) &&
3613
- contextUsedValue >= 0) {
3614
- setContextWindowStatus({
3615
- contextWindowTokens: contextWindowValue,
3616
- estimatedInputTokens: contextUsedValue,
3617
- contextUsedPercent: (contextUsedValue / contextWindowValue) * 100,
3618
- model: agent?.model || "",
3619
- });
3620
- }
3621
2501
  // If server provides costLimit along with totals, persist it locally
3622
2502
  if (statusCostLimit) {
3623
2503
  setAgent((prev) => prev &&
@@ -3639,7 +2519,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3639
2519
  return;
3640
2520
  }
3641
2521
  if (statusData?.state === "ToolApprovalsRequired") {
3642
- setPendingBrowserCaptureDialogType(null);
3643
2522
  const msgId = statusData.messageId;
3644
2523
  const ids = statusData.toolCallIds || [];
3645
2524
  if (msgId && Array.isArray(ids) && ids.length > 0) {
@@ -3671,40 +2550,16 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3671
2550
  setIsWaitingForResponse(false);
3672
2551
  return;
3673
2552
  }
3674
- // Handle waiting states explicitly
3675
- if (normalizedStatus === "waitingForApproval") {
3676
- setPendingBrowserCaptureDialogType(null);
2553
+ // Handle "WaitingForApproval" state explicitly
2554
+ if (statusData?.state === "WaitingForApproval" ||
2555
+ statusData?.state === "waitingForApproval") {
3677
2556
  setAgent((prev) => prev ? { ...prev, status: "waitingForApproval" } : prev);
3678
2557
  setIsConnecting(false);
3679
2558
  setIsWaitingForResponse(false);
3680
2559
  setIsAgentThinking(false);
3681
2560
  return;
3682
2561
  }
3683
- if (normalizedStatus === "waitingForInput") {
3684
- const dialogType = typeof statusData?.dialogType === "string"
3685
- ? statusData.dialogType
3686
- : null;
3687
- const isBrowserCaptureWait = dialogType === DIALOG_TYPES.CAPTURE_PAGE_DOM ||
3688
- dialogType === DIALOG_TYPES.CAPTURE_PAGE_SCREENSHOT;
3689
- setPendingBrowserCaptureDialogType(isBrowserCaptureWait ? dialogType : null);
3690
- setAgent((prev) => prev
3691
- ? {
3692
- ...prev,
3693
- status: "waitingForInput",
3694
- statusMessage: isBrowserCaptureWait &&
3695
- typeof statusData?.title === "string" &&
3696
- statusData.title.trim()
3697
- ? statusData.title.trim()
3698
- : prev.statusMessage,
3699
- }
3700
- : prev);
3701
- setIsConnecting(false);
3702
- setIsWaitingForResponse(false);
3703
- setIsAgentThinking(false);
3704
- return;
3705
- }
3706
- if (normalizedStatus === "costLimitReached") {
3707
- setPendingBrowserCaptureDialogType(null);
2562
+ if (statusData?.state === "CostLimitReached") {
3708
2563
  const totalCost = Number(statusData.totalCost) || 0;
3709
2564
  const costLimit = Number(statusData.costLimit) || agent?.costLimit || 0;
3710
2565
  setCostLimitExceeded({
@@ -3722,7 +2577,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3722
2577
  // Server sends additionalData directly (with todoList inside)
3723
2578
  // Also may include ChildAgents for spawned agents
3724
2579
  try {
3725
- const serverAdditionalData = statusData.additionalData || {};
2580
+ const serverAdditionalData = statusData.additionalData || statusData.AdditionalData || {};
3726
2581
  setAgentMetadata((prev) => {
3727
2582
  const current = (prev || {});
3728
2583
  const updated = { ...current };
@@ -3736,10 +2591,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3736
2591
  }
3737
2592
  // Merge ChildAgents if present (for spawned agents) - replace entire array
3738
2593
  // Backend sends the complete updated list, not just additions
3739
- if (statusData.childAgents) {
3740
- const childAgents = statusData.childAgents;
2594
+ if (statusData.ChildAgents || statusData.childAgents) {
2595
+ const childAgents = statusData.ChildAgents || statusData.childAgents;
3741
2596
  if (Array.isArray(childAgents)) {
3742
- updated.childAgents = childAgents;
2597
+ updated.ChildAgents = childAgents;
3743
2598
  }
3744
2599
  }
3745
2600
  return updated;
@@ -3751,10 +2606,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3751
2606
  return;
3752
2607
  }
3753
2608
  // Handle "completed" state (fallback for legacy code paths that send status instead of lifecycle event)
3754
- if (normalizedStatus === "completed") {
2609
+ if (normalizedStatus === "completed" ||
2610
+ normalizedStatus === "Completed") {
3755
2611
  // Reset deduplication for the next run
3756
2612
  lastSeqRef.current = 0;
3757
- clearHeartbeatMessages();
3758
2613
  // Mark the last assistant message as completed
3759
2614
  setMessages((prev) => {
3760
2615
  const updated = prev.map((msg) => msg.role === "assistant" && !msg.isCompleted
@@ -3777,8 +2632,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3777
2632
  setIsAgentThinking(false);
3778
2633
  return;
3779
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;
2643
+ setIsAgentThinking(false);
2644
+ return;
2645
+ }
3780
2646
  // Handle "Running" state - agent is actively processing
3781
- if (normalizedStatus === "running") {
2647
+ if (normalizedStatus === "running" ||
2648
+ normalizedStatus === "Running") {
3782
2649
  // Update agent status to running
3783
2650
  setAgent((prev) => (prev ? { ...prev, status: "running" } : prev));
3784
2651
  setIsWaitingForResponse(true);
@@ -3787,9 +2654,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3787
2654
  return;
3788
2655
  }
3789
2656
  // Handle "Error" state
3790
- if (normalizedStatus === "error") {
2657
+ if (normalizedStatus === "error" || normalizedStatus === "Error") {
3791
2658
  const errorMsg = statusData?.statusMessage || statusData?.error || "Unknown error";
3792
- clearHeartbeatMessages();
3793
2659
  setAgent((prev) => prev
3794
2660
  ? { ...prev, status: "error", statusMessage: errorMsg }
3795
2661
  : prev);
@@ -3809,7 +2675,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3809
2675
  if (messageType === "agent:run:complete") {
3810
2676
  // Reset deduplication for the next run
3811
2677
  lastSeqRef.current = 0;
3812
- clearHeartbeatMessages();
3813
2678
  // Mark the last assistant message as completed
3814
2679
  setMessages((prev) => {
3815
2680
  const updated = prev.map((msg) => msg.role === "assistant" && !msg.isCompleted
@@ -3834,9 +2699,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3834
2699
  }
3835
2700
  // Lifecycle: agent:run:error
3836
2701
  if (messageType === "agent:run:error") {
3837
- const errorMsg = toUserFacingAgentErrorMessage(message.payload?.error) ||
3838
- "AI could not complete this request.";
3839
- clearHeartbeatMessages();
2702
+ const errorMsg = message.payload?.error || "Unknown error";
3840
2703
  // Reset deduplication for the next run after an error
3841
2704
  lastSeqRef.current = 0;
3842
2705
  setError(errorMsg);
@@ -3851,9 +2714,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3851
2714
  }
3852
2715
  }, [
3853
2716
  agent,
3854
- clearHeartbeatMessages,
3855
2717
  handleContentChunk,
3856
- handleHeartbeatMessage,
3857
2718
  handleToolCall,
3858
2719
  handleToolResult,
3859
2720
  onAgentUpdate,
@@ -3908,7 +2769,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3908
2769
  const isRunning = currentAgent.status === "running" || currentAgent.status === 1;
3909
2770
  const isWaitingForApproval = currentAgent.status === "waitingForApproval" ||
3910
2771
  currentAgent.status === 2;
3911
- const isWaitingForInput = currentAgent.status === "waitingForInput";
3912
2772
  if (isRunning) {
3913
2773
  setIsWaitingForResponse(true);
3914
2774
  isWaitingRef.current = true;
@@ -3916,10 +2776,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3916
2776
  // Agent is currently running, show thinking dots
3917
2777
  setIsAgentThinking(true);
3918
2778
  }
3919
- else if (isWaitingForApproval || isWaitingForInput) {
2779
+ else if (isWaitingForApproval) {
3920
2780
  setIsWaitingForResponse(false);
3921
2781
  isWaitingRef.current = false;
3922
- // Agent is waiting for user input/approval, hide thinking dots
2782
+ // Agent is waiting for user approval, hide thinking dots
3923
2783
  setIsAgentThinking(false);
3924
2784
  }
3925
2785
  else {
@@ -3966,157 +2826,36 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3966
2826
  window.addEventListener("editor:focusAgentPrompt", focusHandler);
3967
2827
  return () => window.removeEventListener("editor:focusAgentPrompt", focusHandler);
3968
2828
  }, []);
3969
- // Keep stable refs so we don't miss window events during effect re-runs.
3970
- const agentIdRefForDialogs = useRef(null);
3971
- const agentStubIdRefForDialogs = useRef(agentStub.id);
3972
- const isActiveRefForDialogs = useRef(isActive);
3973
- const scrollToBottomRefForDialogs = useRef(scrollToBottom);
3974
- useEffect(() => {
3975
- agentIdRefForDialogs.current = agent?.id ?? null;
3976
- }, [agent?.id]);
3977
- useEffect(() => {
3978
- const prevId = agentStubIdRefForDialogs.current;
3979
- agentStubIdRefForDialogs.current = agentStub.id;
3980
- const visibleRegistry = { ...getVisibleDialogRegistry() };
3981
- const normalizedPrevId = normalizeDialogAgentId(prevId);
3982
- if (normalizedPrevId) {
3983
- delete visibleRegistry[normalizedPrevId];
3984
- globalThis.__agentDialogVisibleCallbacks = visibleRegistry;
3985
- }
3986
- if (prevId && prevId !== agentStub.id) {
3987
- const orphanedDialog = activeInlineDialogRef.current;
3988
- if (orphanedDialog) {
3989
- setActiveInlineDialog(null);
3990
- window.dispatchEvent(new CustomEvent("agent:dialog:orphaned", {
3991
- detail: { callbackId: orphanedDialog.request.callbackId },
3992
- }));
3993
- }
3994
- }
3995
- }, [agentStub.id]);
3996
- useEffect(() => {
3997
- isActiveRefForDialogs.current = isActive;
3998
- }, [isActive]);
3999
- useEffect(() => {
4000
- scrollToBottomRefForDialogs.current = scrollToBottom;
4001
- }, [scrollToBottom]);
4002
2829
  // Listen for agent inline dialog requests from AgentDialogHandler
4003
2830
  useEffect(() => {
4004
- if (orphanTimeoutRef.current) {
4005
- clearTimeout(orphanTimeoutRef.current);
4006
- orphanTimeoutRef.current = null;
4007
- }
4008
- const globalListeners = (globalThis.__agentDialogMountedAgents ?? []).filter((x) => typeof x === "string");
4009
- const normalizedAgentStubId = normalizeDialogAgentId(agentStubIdRefForDialogs.current);
4010
- if (normalizedAgentStubId &&
4011
- !globalListeners.includes(normalizedAgentStubId)) {
4012
- globalListeners.push(normalizedAgentStubId);
4013
- }
4014
- globalThis.__agentDialogMountedAgents = globalListeners;
4015
2831
  const handleDialogShow = (event) => {
4016
- const detail = event?.detail;
4017
- const request = detail?.request;
4018
- const onComplete = detail?.onComplete;
4019
- const onCancel = detail?.onCancel;
4020
- const terminalAgentId = normalizeDialogAgentId(agentIdRefForDialogs.current);
4021
- const terminalAgentStubId = normalizeDialogAgentId(agentStubIdRefForDialogs.current);
4022
- const isActiveNow = isActiveRefForDialogs.current;
4023
- if (!request)
4024
- return;
4025
- const requestAgentId = normalizeDialogAgentId(request.agentId);
4026
- const agentMatch = !requestAgentId ||
4027
- !terminalAgentStubId ||
4028
- requestAgentId === terminalAgentStubId ||
4029
- requestAgentId === terminalAgentId;
4030
- if (!isActiveNow) {
4031
- return;
4032
- }
4033
- if (!request)
4034
- return;
4035
- // Only handle dialog requests for this terminal's agent stub
4036
- if (requestAgentId &&
4037
- terminalAgentStubId &&
4038
- requestAgentId !== terminalAgentStubId &&
4039
- 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) {
4040
2835
  return;
4041
2836
  }
4042
2837
  console.log("[AgentTerminal] Received inline dialog request:", request);
4043
- setActiveInlineDialog({
4044
- request,
4045
- onComplete: (result) => {
4046
- onComplete(result);
4047
- onInteractionSubmitted?.();
4048
- },
4049
- onCancel: () => {
4050
- onCancel();
4051
- onInteractionSubmitted?.();
4052
- },
4053
- });
2838
+ setActiveInlineDialog({ request, onComplete, onCancel });
4054
2839
  // Notify AgentDialogHandler that we accepted the dialog (stops retry mechanism)
4055
- if (request.callbackId) {
4056
- window.dispatchEvent(new CustomEvent("agent:dialog:accepted", {
4057
- detail: { callbackId: request.callbackId },
4058
- }));
4059
- }
4060
- setTimeout(() => {
4061
- if (request.dialogType === "questionnaire") {
4062
- scrollToBottomRefForDialogs.current?.();
4063
- return;
4064
- }
4065
- scrollToBottomRefForDialogs.current?.();
4066
- }, 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);
4067
2845
  };
4068
2846
  window.addEventListener("agent:dialog:show", handleDialogShow);
4069
- return () => {
4070
- const mounted = (globalThis.__agentDialogMountedAgents ?? []).filter((x) => typeof x === "string");
4071
- globalThis.__agentDialogMountedAgents = mounted.filter((id) => id !== normalizeDialogAgentId(agentStubIdRefForDialogs.current));
4072
- const visibleRegistry = { ...getVisibleDialogRegistry() };
4073
- const idsToClear = [
4074
- normalizeDialogAgentId(agentStubIdRefForDialogs.current),
4075
- normalizeDialogAgentId(agentIdRefForDialogs.current),
4076
- ].filter(Boolean);
4077
- idsToClear.forEach((id) => delete visibleRegistry[id]);
4078
- globalThis.__agentDialogVisibleCallbacks = visibleRegistry;
4079
- // If unmounting with an active dialog, defer the orphan event so that
4080
- // React strict mode remounts can cancel it. Only real unmounts fire.
4081
- const orphanedDialog = activeInlineDialogRef.current;
4082
- if (orphanedDialog) {
4083
- orphanTimeoutRef.current = setTimeout(() => {
4084
- orphanTimeoutRef.current = null;
4085
- window.dispatchEvent(new CustomEvent("agent:dialog:orphaned", {
4086
- detail: { callbackId: orphanedDialog.request.callbackId },
4087
- }));
4088
- }, 300);
4089
- }
4090
- window.removeEventListener("agent:dialog:show", handleDialogShow);
4091
- };
4092
- }, []);
4093
- // Announce when this terminal is ready to accept dialogs.
4094
- // Fire immediately on mount using agentStub.id so the AgentDialogHandler
4095
- // can re-dispatch pending dialogs without waiting for the async agent load.
4096
- // 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
4097
2851
  useEffect(() => {
4098
- const normalizedStubId = normalizeDialogAgentId(agentStub.id);
4099
- if (normalizedStubId) {
2852
+ if (agent?.id) {
2853
+ console.log("[AgentTerminal] Terminal ready for agent:", agent.id);
4100
2854
  window.dispatchEvent(new CustomEvent("agent:terminal:ready", {
4101
- detail: {
4102
- agentId: normalizedStubId,
4103
- terminalInstanceId: dialogTerminalInstanceIdRef.current,
4104
- },
2855
+ detail: { agentId: agent.id },
4105
2856
  }));
4106
2857
  }
4107
- }, [agentStub.id]);
4108
- useEffect(() => {
4109
- const normalizedAgentId = normalizeDialogAgentId(agent?.id);
4110
- const normalizedStubId = normalizeDialogAgentId(agentStub.id);
4111
- if (normalizedAgentId && normalizedAgentId !== normalizedStubId) {
4112
- window.dispatchEvent(new CustomEvent("agent:terminal:ready", {
4113
- detail: {
4114
- agentId: normalizedAgentId,
4115
- terminalInstanceId: dialogTerminalInstanceIdRef.current,
4116
- },
4117
- }));
4118
- }
4119
- }, [agent?.id, agentStub.id]);
2858
+ }, [agent?.id]);
4120
2859
  // Profiles are provided by parent component (Agents). No local loading here.
4121
2860
  // Select active profile based on agent.profileId or agentStub.profileId
4122
2861
  useEffect(() => {
@@ -4127,47 +2866,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4127
2866
  // Use case-insensitive comparison for GUID matching (backend may return different casing)
4128
2867
  const normalizedProfileId = profileIdToUse?.toLowerCase();
4129
2868
  const candidate = normalizedProfileId
4130
- ? profiles.find((p) => p.id?.toLowerCase() === normalizedProfileId)
4131
- : undefined;
4132
- if (!candidate) {
4133
- setActiveProfile(undefined);
4134
- 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);
4135
2874
  }
4136
- // Keep active profile in sync whenever the matching entry in `profiles` changes
4137
- // not only when the profile id changes. Otherwise availableSkills (and similar fields)
4138
- // that are merged in the parent after async loads never update (e.g. Template Builder
4139
- // settings skills merged into the profile).
4140
- setActiveProfile((prev) => {
4141
- if (!prev || prev.id !== candidate.id)
4142
- return candidate;
4143
- const skillKey = (p) => JSON.stringify({
4144
- allowed: (p.allowedSkills ?? []).map((s) => [
4145
- String(s?.id ?? ""),
4146
- String(s?.name ?? ""),
4147
- ]),
4148
- available: (p.availableSkills ?? []).map((s) => [
4149
- String(s?.id ?? ""),
4150
- String(s?.name ?? ""),
4151
- ]),
4152
- });
4153
- if (skillKey(prev) === skillKey(candidate))
4154
- return prev;
4155
- return candidate;
4156
- });
4157
- }, [
4158
- profiles,
4159
- agent?.id,
4160
- agent?.profileId,
4161
- agentStub.id,
4162
- agentStub.profileId,
4163
- ]);
2875
+ }, [profiles, agent?.profileId, agentStub.profileId]);
4164
2876
  // Clear queued prompts when agent changes or is new;
4165
2877
  // initial fetch is handled by loadAgent() for better performance and reliability
4166
2878
  useEffect(() => {
4167
- if (!agent?.id || isLocalOnlyDraftAgent) {
2879
+ if (!agent?.id || agent.status === "new") {
4168
2880
  setQueuedPrompts([]);
4169
2881
  }
4170
- }, [agent?.id, isLocalOnlyDraftAgent]);
2882
+ }, [agent?.id, agent?.status]);
4171
2883
  // Update selected model when the active profile or agent model changes
4172
2884
  useEffect(() => {
4173
2885
  if (!activeProfile)
@@ -4196,16 +2908,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4196
2908
  // Initialize mode from metadata; fall back to agent.Mode from server
4197
2909
  useEffect(() => {
4198
2910
  try {
4199
- const metaMode = normalizeAgentMode(agentMetadata?.mode);
4200
- if (metaMode) {
2911
+ const metaMode = agentMetadata?.mode;
2912
+ if (metaMode === "autonomous" ||
2913
+ metaMode === "read-only" ||
2914
+ metaMode === "supervised") {
4201
2915
  setMode(metaMode);
4202
2916
  return;
4203
2917
  }
4204
2918
  }
4205
2919
  catch { }
4206
2920
  try {
4207
- const serverMode = normalizeAgentMode(agent?.mode);
4208
- if (serverMode) {
2921
+ const serverMode = agent?.mode;
2922
+ if (serverMode === "autonomous" ||
2923
+ serverMode === "read-only" ||
2924
+ serverMode === "supervised") {
4209
2925
  setMode(serverMode);
4210
2926
  }
4211
2927
  }
@@ -4225,7 +2941,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4225
2941
  textareaRef.current.focus();
4226
2942
  }
4227
2943
  }, [messages, activePlaceholderInput]);
4228
- // 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
4229
2945
  const persistPendingSettingsIfNeeded = useCallback(async () => {
4230
2946
  try {
4231
2947
  if (!agent?.id)
@@ -4238,10 +2954,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4238
2954
  payload.model = pending.modelName;
4239
2955
  if (pending.mode)
4240
2956
  payload.mode = pending.mode;
4241
- if (pending.profileId)
4242
- payload.profileId = pending.profileId;
4243
- if (pending.profileName != null)
4244
- payload.profileName = pending.profileName;
4245
2957
  if (Object.keys(payload).length === 0)
4246
2958
  return;
4247
2959
  await updateAgentSettings(agent.id, payload);
@@ -4251,92 +2963,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4251
2963
  console.error("Failed to persist pending settings", e);
4252
2964
  }
4253
2965
  }, [agent?.id]);
4254
- const getPendingRequestSettings = useCallback(() => {
4255
- const pending = pendingSettingsRef.current;
4256
- const requestProfile = pending?.profileId
4257
- ? profiles.find((profile) => profile.id === pending.profileId) ||
4258
- activeProfile ||
4259
- profiles[0]
4260
- : activeProfile || profiles[0];
4261
- let requestModelId = selectedModelId;
4262
- if (pending?.modelName) {
4263
- const requestModel = (requestProfile?.models || []).find((model) => (model.name || "").trim().toLowerCase() ===
4264
- pending.modelName?.trim().toLowerCase());
4265
- requestModelId = requestModel?.id || requestModelId;
4266
- }
4267
- return {
4268
- mode: (pending?.mode || mode),
4269
- profileId: pending?.profileId || requestProfile?.id || "",
4270
- profileName: pending?.profileName || requestProfile?.name || "",
4271
- modelId: requestModelId,
4272
- };
4273
- }, [activeProfile, mode, profiles, selectedModelId]);
4274
- const getSubmitErrorMessage = (error) => {
4275
- const fallback = "Failed to submit prompt. Please try again.";
4276
- if (!(error instanceof Error))
4277
- return fallback;
4278
- const cleaned = toUserFacingAgentErrorMessage(error.message);
4279
- return cleaned || fallback;
4280
- };
4281
- const suppressedQueuedPromptsRef = useRef([]);
4282
- const pruneSuppressedQueuedPrompts = useCallback(() => {
4283
- const cutoff = Date.now() - 15_000;
4284
- suppressedQueuedPromptsRef.current =
4285
- suppressedQueuedPromptsRef.current.filter((entry) => entry.createdAt >= cutoff);
4286
- }, []);
4287
- const registerSuppressedQueuedPrompt = useCallback((agentId, promptText) => {
4288
- const normalizedAgentId = agentId.trim().toLowerCase();
4289
- const normalizedPrompt = promptText.trim();
4290
- if (!normalizedAgentId || !normalizedPrompt) {
4291
- return null;
4292
- }
4293
- pruneSuppressedQueuedPrompts();
4294
- const token = crypto.randomUUID();
4295
- suppressedQueuedPromptsRef.current = [
4296
- ...suppressedQueuedPromptsRef.current,
4297
- {
4298
- token,
4299
- agentId: normalizedAgentId,
4300
- prompt: normalizedPrompt,
4301
- createdAt: Date.now(),
4302
- },
4303
- ];
4304
- return token;
4305
- }, [pruneSuppressedQueuedPrompts]);
4306
- const clearSuppressedQueuedPrompt = useCallback((token) => {
4307
- if (!token) {
4308
- return;
4309
- }
4310
- suppressedQueuedPromptsRef.current =
4311
- suppressedQueuedPromptsRef.current.filter((entry) => entry.token !== token);
4312
- }, []);
4313
- const shouldSuppressQueuedPrompt = useCallback((queueEntry) => {
4314
- const normalizedAgentId = queueEntry?.targetAgentId?.trim().toLowerCase();
4315
- const normalizedPrompt = queueEntry?.prompt?.trim();
4316
- if (!normalizedAgentId || !normalizedPrompt) {
4317
- return false;
4318
- }
4319
- pruneSuppressedQueuedPrompts();
4320
- const matchedEntry = suppressedQueuedPromptsRef.current.find((entry) => entry.agentId === normalizedAgentId &&
4321
- entry.prompt === normalizedPrompt);
4322
- if (!matchedEntry) {
4323
- return false;
4324
- }
4325
- suppressedQueuedPromptsRef.current =
4326
- suppressedQueuedPromptsRef.current.filter((entry) => entry.token !== matchedEntry.token);
4327
- return true;
4328
- }, [pruneSuppressedQueuedPrompts]);
4329
- const cancelActiveInlineDialog = useCallback(() => {
4330
- const activeDialog = activeInlineDialogRef.current;
4331
- if (!activeDialog)
4332
- return;
4333
- try {
4334
- activeDialog.onCancel();
4335
- }
4336
- finally {
4337
- setActiveInlineDialog(null);
4338
- }
4339
- }, []);
4340
2966
  const handleSubmit = async () => {
4341
2967
  // Guard against double-submit and missing context
4342
2968
  if (isSubmitting) {
@@ -4380,12 +3006,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4380
3006
  setError("Agent not ready. Please try again.");
4381
3007
  return;
4382
3008
  }
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,73 +3748,7 @@ 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 {
@@ -5292,52 +3812,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5292
3812
  if (effectiveCostLimit === undefined) {
5293
3813
  effectiveCostLimit = undefined;
5294
3814
  }
5295
- // Calculate total token usage for cost display.
5296
- // Message rows do not currently persist imageCost, so on refresh we fall back
5297
- // to the persisted agent aggregate for that single field.
5298
- const totalTokens = (() => {
5299
- const totals = calculateTotalTokens(messages);
5300
- return {
5301
- ...totals,
5302
- imageCost: totals.imageCost || Number(agent?.totalImageCost) || 0,
5303
- };
5304
- })();
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
3818
  const isExecuting = isSubmitting ||
5307
3819
  isConnecting ||
5308
3820
  isWaitingForResponse ||
5309
3821
  hasActiveStreaming();
5310
- const runDiagnosticsSnapshot = useMemo(() => {
5311
- const lastEvent = recentAgentRunEvents[recentAgentRunEvents.length - 1];
5312
- return {
5313
- agentId: currentAgentId,
5314
- isSubmitting,
5315
- isConnecting,
5316
- isWaitingForResponse,
5317
- isAgentThinking,
5318
- isExecuting,
5319
- hasActiveStreaming: hasActiveStreaming(),
5320
- isSubscribed: normalizeDialogAgentId(subscribedAgentIdRef.current) ===
5321
- normalizeDialogAgentId(currentAgentId),
5322
- lastSeq: lastSeqRef.current,
5323
- lastEventType: lastEvent?.type ?? null,
5324
- lastEventAt: lastEvent?.timestamp ?? null,
5325
- recentEvents: recentAgentRunEvents,
5326
- };
5327
- }, [
5328
- currentAgentId,
5329
- hasActiveStreaming,
5330
- isAgentThinking,
5331
- isConnecting,
5332
- isExecuting,
5333
- isSubmitting,
5334
- isWaitingForResponse,
5335
- recentAgentRunEvents,
5336
- ]);
5337
- const showInitialThinkingSplash = messages.length === 0 &&
5338
- !error &&
5339
- hideGreeting &&
5340
- (isSubmitting || isConnecting);
5341
3822
  // Compute dots visibility: only show BEFORE any assistant message exists
5342
3823
  // This prevents duplicate headers - the dots indicator has its own header,
5343
3824
  // and we don't want to show a second header below existing messages
@@ -5352,20 +3833,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5352
3833
  // The message with the pending approval will display its own UI for approval
5353
3834
  if (allPendingApprovals.length > 0)
5354
3835
  return false;
5355
- // The hidden-greeting startup splash already renders its own bouncing dots.
5356
- // Suppress the generic indicator so reopening/running terminals don't show two.
5357
- if (showInitialThinkingSplash)
5358
- return false;
5359
3836
  // IMPORTANT: If the last message is an assistant message and we're still executing,
5360
3837
  // the AiResponseMessage for that message will show its own activity indicator.
5361
3838
  // We only want these global thinking dots if the last message was from the user
5362
3839
  // or if no messages exist yet (waiting for initial response).
5363
3840
  const lastMessage = messages.length > 0 ? messages[messages.length - 1] : null;
5364
- if (isExecuting &&
5365
- lastMessage?.role === "assistant" &&
5366
- !lastMessage.isCompleted) {
3841
+ if (isExecuting && lastMessage?.role === "assistant")
5367
3842
  return false;
5368
- }
5369
3843
  // Existing check for uncompleted assistant messages
5370
3844
  const hasActiveStreamingMessage = messages.some((m) => !m.isCompleted && m.role === "assistant");
5371
3845
  if (hasActiveStreamingMessage)
@@ -5381,22 +3855,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5381
3855
  messages,
5382
3856
  activeInlineDialog,
5383
3857
  allPendingApprovals,
5384
- showInitialThinkingSplash,
5385
3858
  ]);
5386
3859
  // Move useMemo hook before early return to comply with Rules of Hooks
5387
- const resolvedEditorContextMode = React.useMemo(() => {
3860
+ const isLiveEditorContextMode = React.useMemo(() => {
5388
3861
  try {
5389
3862
  const normalizedAgentProfileId = agent?.profileId?.toLowerCase();
5390
3863
  const profile = activeProfile ||
5391
3864
  profiles.find((p) => p.id?.toLowerCase() === normalizedAgentProfileId);
5392
- return profile?.editorContextMode ?? null;
3865
+ const mode = profile?.editorContextMode;
3866
+ return mode === "live";
5393
3867
  }
5394
3868
  catch {
5395
- return null;
3869
+ return false;
5396
3870
  }
5397
3871
  }, [activeProfile, profiles, agent?.profileId]);
5398
- const isLiveEditorContextMode = resolvedEditorContextMode === "live";
5399
- const omitsEditorContext = resolvedEditorContextMode === "none";
5400
3872
  // Get parent agent ID from agent or agentStub (handle both camelCase and PascalCase)
5401
3873
  const parentAgentId = agent?.parentAgentId ||
5402
3874
  agent?.ParentAgentId ||
@@ -5410,8 +3882,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5410
3882
  detail: { agentId: parentAgentId },
5411
3883
  }));
5412
3884
  }, [parentAgentId]);
5413
- 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;
5414
- 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 }));
5415
3889
  const renderCostLimitBanner = () => {
5416
3890
  if (!costLimitExceeded)
5417
3891
  return null;
@@ -5422,20 +3896,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5422
3896
  try {
5423
3897
  // Extend cost limit - backend will automatically resume the agent
5424
3898
  const result = await updateAgentCostLimit(agent.id, "extend");
5425
- // Update the agent's cost limit and clear the costLimitReached
5426
- // status in local state so the useEffect watcher doesn't
5427
- // immediately re-show the banner before the backend status
5428
- // update arrives.
3899
+ // Update the agent's cost limit in local state
5429
3900
  if (result.success && result.costLimit !== undefined) {
5430
- setAgent((prev) => prev
5431
- ? {
5432
- ...prev,
5433
- costLimit: result.costLimit,
5434
- status: prev.status === "costLimitReached"
5435
- ? "running"
5436
- : prev.status,
5437
- }
5438
- : prev);
3901
+ setAgent((prev) => prev ? { ...prev, costLimit: result.costLimit } : prev);
5439
3902
  }
5440
3903
  // Clear the banner and set waiting state
5441
3904
  // Agent will resume automatically via backend's ResumeAgentAsync
@@ -5455,245 +3918,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5455
3918
  };
5456
3919
  const renderErrorBanner = () => {
5457
3920
  const currentAgent = agent || agentStub;
5458
- const isErrorStatus = currentAgent?.status === "error";
5459
- const rawErrorMessage = (isErrorStatus ? currentAgent?.statusMessage : null) || error;
5460
- if (!rawErrorMessage)
3921
+ const isErrorStatus = currentAgent?.status === "error" || currentAgent?.status === 4;
3922
+ const errorMessage = currentAgent?.statusMessage;
3923
+ if (!isErrorStatus || !errorMessage)
5461
3924
  return null;
5462
- // Clean the error message (statusMessage from DB may contain raw JSON)
5463
- const errorMessage = toUserFacingAgentErrorMessage(rawErrorMessage) || rawErrorMessage;
5464
- 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 })] })] }) }));
5465
3926
  };
5466
- const renderBrowserClaimBanner = (variant = "inline") => {
5467
- if (!agent?.id || !editContext?.sessionId)
5468
- return null;
5469
- if (!isClaimedByCurrentSession && !isClaimedByAnotherBrowser) {
5470
- return null;
5471
- }
5472
- if (isPendingBrowserCaptureWait) {
5473
- return null;
5474
- }
5475
- const label = isClaimedByCurrentSession
5476
- ? "Attached to this browser"
5477
- : isClaimedByAnotherBrowser
5478
- ? "Attached in another browser"
5479
- : "No browser attached";
5480
- const description = isClaimedByCurrentSession
5481
- ? "This browser will handle page screenshot and DOM capture requests for the agent."
5482
- : isClaimedByAnotherBrowser
5483
- ? "Capture requests will stay with the other browser until you take over control here."
5484
- : "A page capture request is waiting for a browser attachment. Attach this browser to continue.";
5485
- 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");
5486
- 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: () => {
5487
- void handleReleaseBrowser();
5488
- }, 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: () => {
5489
- void handleClaimBrowser(isClaimedByAnotherBrowser);
5490
- }, children: isClaimedByAnotherBrowser
5491
- ? "Take over browser control"
5492
- : "Attach to this browser" })) })] }) }));
5493
- };
5494
- const fixedBrowserClaimBanner = renderBrowserClaimBanner("fixed");
5495
- const inlineBrowserClaimBanner = null;
5496
- const browserCaptureInlinePrompt = isPendingBrowserCaptureWait && !isClaimedByCurrentSession
5497
- ? {
5498
- toolNames: [
5499
- "capture-page-screenshot",
5500
- "capture-parhelia-ui-screenshot",
5501
- "capture-page-dom",
5502
- ],
5503
- label: isClaimedByAnotherBrowser
5504
- ? "Attached in another browser"
5505
- : "No browser attached",
5506
- description: isClaimedByAnotherBrowser
5507
- ? "This capture request is waiting in another browser. Take over browser control here to continue."
5508
- : "This capture request is waiting for a browser attachment. Attach this browser to continue.",
5509
- actionLabel: isClaimedByAnotherBrowser
5510
- ? "Take over browser control"
5511
- : "Attach to this browser",
5512
- isPending: isBrowserClaimMutationPending,
5513
- onAction: () => {
5514
- void handleClaimBrowser(isClaimedByAnotherBrowser);
5515
- },
5516
- }
5517
- : null;
5518
- useEffect(() => {
5519
- if (agent?.status !== "waitingForInput") {
5520
- setPendingBrowserCaptureDialogType(null);
5521
- }
5522
- }, [agent?.status]);
5523
- const renderInlineDialogContent = () => {
5524
- if (!activeInlineDialog)
5525
- return null;
5526
- if (activeInlineDialog.request.dialogType === "questionnaire") {
5527
- 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) => {
5528
- activeInlineDialog.onComplete(result);
5529
- setActiveInlineDialog(null);
5530
- void onInteractionSubmitted?.();
5531
- }, onCancel: () => {
5532
- activeInlineDialog.onCancel();
5533
- setActiveInlineDialog(null);
5534
- } }) }));
5535
- }
5536
- const dialogRegistration = editContext?.configuration?.editor?.agentDialogs?.find((d) => d.dialogType === activeInlineDialog.request.dialogType);
5537
- if (dialogRegistration) {
5538
- const DialogComponent = dialogRegistration.component;
5539
- return (_jsx("div", { className: "agent-inline-dialog", children: _jsx(DialogComponent, { title: activeInlineDialog.request.title, description: activeInlineDialog.request.description, parameters: activeInlineDialog.request.parameters, onClose: (result) => {
5540
- activeInlineDialog.onComplete(result);
5541
- setActiveInlineDialog(null);
5542
- if (activeInlineDialog.request.dialogType === "questionnaire") {
5543
- void onInteractionSubmitted?.();
5544
- }
5545
- }, onCancel: () => {
5546
- activeInlineDialog.onCancel();
5547
- setActiveInlineDialog(null);
5548
- } }) }));
5549
- }
5550
- 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] }) }));
5551
- };
5552
- const latestSummaryAssistantGroup = useMemo(() => {
5553
- if (hideSummaryMessages)
5554
- return null;
5555
- const groups = groupConsecutiveMessages(messages);
5556
- for (let groupIndex = groups.length - 1; groupIndex >= 0; groupIndex -= 1) {
5557
- const group = groups[groupIndex];
5558
- if (!group || group.type !== "assistant-group")
5559
- continue;
5560
- const filteredMessages = group.messages.filter((msg) => {
5561
- const content = msg.content || "";
5562
- return !content.startsWith("⚠️") || !content.includes("Cost limit");
5563
- });
5564
- if (filteredMessages.length === 0)
5565
- continue;
5566
- return {
5567
- messages: filteredMessages,
5568
- isLastGroup: groupIndex === groups.length - 1,
5569
- };
5570
- }
5571
- return null;
5572
- }, [messages, hideSummaryMessages]);
5573
- const summaryModeContent = displayMode === "summary"
5574
- ? (() => {
5575
- const inlineDialog = renderInlineDialogContent();
5576
- const summaryMessages = latestSummaryAssistantGroup
5577
- ? convertAgentMessagesToAiFormat(latestSummaryAssistantGroup.messages)
5578
- : [];
5579
- const summaryOperations = latestSummaryAssistantGroup
5580
- ? getOperationsForMessageGroup(summaryMessages, agentOperations)
5581
- : [];
5582
- 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 &&
5583
- !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: {
5584
- __html: sanitizeSvg(activeProfile.svgIcon),
5585
- } })) : (_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 ||
5586
- activeProfile?.displayTitle ||
5587
- activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, browserCaptureInlinePrompt: browserCaptureInlinePrompt, onQuickAction: (action) => {
5588
- const text = (action.prompt ||
5589
- action.value ||
5590
- action.label ||
5591
- "").trim();
5592
- if (!text)
5593
- return;
5594
- if (isExecuting) {
5595
- try {
5596
- handleStop();
5597
- }
5598
- catch { }
5599
- }
5600
- sendQuickMessage(text);
5601
- } }) })) : 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
5602
- ? "The agent is still working. The next update will appear here automatically."
5603
- : agent?.statusMessage ||
5604
- summaryPlaceholderMessage ||
5605
- "Waiting for the next agent update." }), summaryPlaceholderActions ? (_jsx("div", { className: `flex justify-center ${compact ? "mt-2" : "mt-3"}`, children: summaryPlaceholderActions })) : null] }) })), displayMode !== "summary" &&
5606
- shouldShowThinkingDots &&
5607
- !inlineDialog &&
5608
- !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: {
5609
- __html: sanitizeSvg(activeProfile.svgIcon),
5610
- } })) : (_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 ||
5611
- activeProfile?.displayTitle ||
5612
- activeProfile?.name ||
5613
- "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) => {
5614
- setActivePlaceholderInput(null);
5615
- setAllPlaceholdersFilled(false);
5616
- if (activePlaceholderInput.behavior === "compose" &&
5617
- !hideBottomControls) {
5618
- setPrompt(filledText);
5619
- setInputPlaceholder("Review and edit, then press Enter to send");
5620
- if (textareaRef.current) {
5621
- try {
5622
- textareaRef.current.focus();
5623
- const v = textareaRef.current.value || "";
5624
- textareaRef.current.selectionStart = v.length;
5625
- textareaRef.current.selectionEnd = v.length;
5626
- }
5627
- catch { }
5628
- }
5629
- }
5630
- else {
5631
- if (isExecuting) {
5632
- try {
5633
- handleStop();
5634
- }
5635
- catch { }
5636
- }
5637
- sendQuickMessage(filledText);
5638
- }
5639
- }, onCancel: () => {
5640
- setActivePlaceholderInput(null);
5641
- setAllPlaceholdersFilled(false);
5642
- } })) : prompt && /\{\{[^{}]+\}\}|<<[^<>]+>>/.test(prompt) ? (_jsx(PlaceholderInput, { ref: promptPlaceholderInputRef, text: prompt, showButtons: hideBottomControls, buttonsClassName: hideBottomControls ? "justify-end" : "", onFilledChange: setAllPlaceholdersFilled, onComplete: (filledText) => {
5643
- setPrompt(filledText);
5644
- setAllPlaceholdersFilled(false);
5645
- if (filledText.trim()) {
5646
- if (isExecuting) {
5647
- try {
5648
- handleStop();
5649
- }
5650
- catch { }
5651
- }
5652
- sendQuickMessage(filledText);
5653
- }
5654
- }, onCancel: () => {
5655
- setPrompt("");
5656
- setAllPlaceholdersFilled(false);
5657
- setInputPlaceholder("Type your message... (Enter to send, Shift+Enter or Ctrl+Enter for new line)");
5658
- } })) : (_jsx("div", { className: "flex items-stretch gap-2", children: _jsx(Textarea, { ref: textareaRef, style: { viewTransitionName: "assistant-chat-input" }, value: prompt, onChange: (e) => {
5659
- setPrompt(e.target.value);
5660
- if (!/\{\{[^{}]+\}\}|<<[^<>]+>>/.test(e.target.value)) {
5661
- setAllPlaceholdersFilled(false);
5662
- }
5663
- if (currentHistoryIndex !== -1) {
5664
- setCurrentHistoryIndex(-1);
5665
- }
5666
- }, onKeyDown: handleKeyPress, onPaste: handlePaste, onFocus: () => {
5667
- shouldMaintainFocusRef.current = true;
5668
- }, onBlur: () => {
5669
- shouldMaintainFocusRef.current = false;
5670
- }, 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 }) })), (() => {
5671
- const isInPlaceholderMode = activePlaceholderInput ||
5672
- (prompt && /\{\{[^{}]+\}\}|<<[^<>]+>>/.test(prompt));
5673
- const placeholderShowsOwnButtons = hideBottomControls && isInPlaceholderMode;
5674
- if (placeholderShowsOwnButtons)
5675
- return null;
5676
- return (_jsxs("div", { className: cn("mt-2 flex items-stretch gap-2", hideBottomControls ||
5677
- simpleMode ||
5678
- isInPlaceholderMode
5679
- ? "justify-end"
5680
- : "justify-between"), children: [!hideBottomControls &&
5681
- !simpleMode &&
5682
- !isInPlaceholderMode ? (_jsx("div", { className: "flex-1" })) : null, _jsx(Button, { type: "button", size: "sm", onClick: () => {
5683
- if (isExecuting) {
5684
- handleStop();
5685
- }
5686
- else {
5687
- handleSubmit();
5688
- }
5689
- }, disabled: !isExecuting &&
5690
- !activePlaceholderInput &&
5691
- (!prompt.trim() || isSubmitting), "data-testid": "agent-send-stop-button", children: isExecuting ? "Stop" : "Send" })] }));
5692
- })()] })) : null] }));
5693
- })()
5694
- : null;
5695
- const fullModeInlineDialog = displayMode === "full" ? renderInlineDialogContent() : null;
5696
- 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) => {
5697
3928
  setPrompt(p);
5698
3929
  // Use setTimeout to ensure state is updated before submission
5699
3930
  setTimeout(() => {
@@ -5706,9 +3937,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5706
3937
  handleSubmit();
5707
3938
  }
5708
3939
  }, 0);
5709
- } })) })), 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: {
5710
- __html: sanitizeSvg(activeProfile.svgIcon),
5711
- } })) : (_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: [(() => {
5712
3946
  const groups = groupConsecutiveMessages(messages);
5713
3947
  return groups.map((group, groupIndex) => {
5714
3948
  const isLastGroup = groupIndex === groups.length - 1;
@@ -5716,9 +3950,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5716
3950
  // Render user message
5717
3951
  return (_jsx(UserMessage, { message: group.messages[0] }, groupIndex));
5718
3952
  }
5719
- else if (group.type === "heartbeat" && group.messages[0]) {
5720
- return (_jsx(HeartbeatMessage, { message: group.messages[0] }, group.messages[0].id || groupIndex));
5721
- }
5722
3953
  else {
5723
3954
  // Render bundled assistant messages
5724
3955
  // Check if this group contains any streaming message
@@ -5735,9 +3966,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5735
3966
  }
5736
3967
  const convertedMessages = convertAgentMessagesToAiFormat(filteredMessages);
5737
3968
  const operationsForGroup = getOperationsForMessageGroup(convertedMessages, agentOperations);
5738
- 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 ||
5739
3970
  activeProfile?.displayTitle ||
5740
- activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, browserCaptureInlinePrompt: browserCaptureInlinePrompt, onQuickAction: (action) => {
3971
+ activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, onQuickAction: (action) => {
5741
3972
  const text = (action.prompt ||
5742
3973
  action.value ||
5743
3974
  action.label ||
@@ -5781,13 +4012,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5781
4012
  }
5782
4013
  });
5783
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: {
5784
- __html: sanitizeSvg(activeProfile.svgIcon),
4015
+ __html: activeProfile.svgIcon,
5785
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 ||
5786
4017
  activeProfile?.displayTitle ||
5787
4018
  activeProfile?.name ||
5788
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 &&
5789
4020
  !simpleMode &&
5790
- (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: [
5791
4022
  {
5792
4023
  id: "context",
5793
4024
  label: "Context",
@@ -5835,49 +4066,28 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5835
4066
  hasTodoContent,
5836
4067
  hasSpawnedAgents,
5837
4068
  agent?.id && hasHistoryContent,
5838
- ].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-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) => {
5839
- let triggerName = "";
5840
- if (qp.data) {
5841
- try {
5842
- const parsed = JSON.parse(qp.data);
5843
- triggerName = parsed?.triggerName?.trim() || "";
5844
- }
5845
- catch {
5846
- // Ignore invalid JSON metadata and render as regular queued prompt.
5847
- }
5848
- }
5849
- const isTriggerQueuedPrompt = !qp.sourceAgentName && triggerName.length > 0;
5850
- const isTriggerExpanded = !!expandedQueuedTriggerIds[qp.id];
5851
- if (isTriggerQueuedPrompt) {
5852
- return (_jsxs("div", { className: "text-[11px]", "data-testid": "queued-prompt-item", children: [_jsxs("button", { type: "button", onClick: () => setExpandedQueuedTriggerIds((prev) => ({
5853
- ...prev,
5854
- [qp.id]: !prev[qp.id],
5855
- })), 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));
5856
- }
5857
- 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 &&
5858
- new Date(qp.scheduledFor).getTime() >
5859
- new Date(qp.createdDate || 0).getTime() +
5860
- 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() ===
5861
- new Date().toDateString()
5862
- ? formatTime(new Date(qp.scheduledFor))
5863
- : formatDateTime(new Date(qp.scheduledFor))] })] }))] })] }) }) }, qp.id));
5864
- }) })] }) }))] }));
5865
- const showQuestionnaireSplitter = isQuestionnaireDialogOpen && !!fullModeInlineDialog;
5866
- const fullModeContent = showQuestionnaireSplitter ? (_jsx(Splitter, { panels: [
5867
- {
5868
- name: "conversation",
5869
- defaultSize: 65,
5870
- content: fullModeUpperContent,
5871
- },
5872
- {
5873
- name: "questionnaire",
5874
- defaultSize: 35,
5875
- content: fullModeInlineDialog,
5876
- },
5877
- ], direction: "vertical", localStorageKey: compact
5878
- ? "agent-terminal-compact-questionnaire-splitter"
5879
- : "agent-terminal-questionnaire-splitter", className: "min-h-0 flex-1", splitterClassName: "bg-gray-200 hover:bg-gray-300" })) : (_jsxs(_Fragment, { children: [fullModeUpperContent, fullModeInlineDialog] }));
5880
- 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 ? (
5881
4091
  // Placeholder Input (from quick actions)
5882
4092
  // Show internal buttons only in splash mode (hideBottomControls) since external buttons won't be visible there
5883
4093
  _jsx(PlaceholderInput, { ref: placeholderInputRef, text: activePlaceholderInput.text, showButtons: hideBottomControls, buttonsClassName: hideBottomControls ? "justify-end" : "", onFilledChange: setAllPlaceholdersFilled, onComplete: (filledText) => {
@@ -5954,221 +4164,122 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5954
4164
  return null;
5955
4165
  return (_jsxs("div", { className: cn("mt-2 flex items-stretch gap-2", hideBottomControls || simpleMode || isInPlaceholderMode
5956
4166
  ? "justify-end"
5957
- : "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"
5958
4168
  ? "border-green-300 bg-green-50! text-green-700 hover:bg-green-100!"
5959
4169
  : mode === "supervised"
5960
4170
  ? "border-amber-300 bg-amber-50! text-amber-700 hover:bg-amber-100!"
5961
4171
  : "border-red-300 bg-red-50! text-red-700 hover:bg-red-100!"), value: mode, options: modeOptions, onValueChange: async (val) => {
5962
4172
  const nextMode = val || "supervised";
4173
+ // Optimistic UI update
4174
+ setMode(nextMode);
5963
4175
  const current = agentMetadata || {};
5964
4176
  const nextMeta = {
5965
4177
  ...current,
5966
4178
  mode: nextMode,
5967
4179
  };
5968
4180
  try {
5969
- if (!agent?.id || isLocalOnlyDraftAgent) {
5970
- setMode(nextMode);
4181
+ if (!agent?.id || agent.status === "new") {
5971
4182
  setAgentMetadata(nextMeta);
4183
+ // Cache until first start when agent is persisted
5972
4184
  pendingSettingsRef.current = {
5973
4185
  ...(pendingSettingsRef.current || {}),
5974
4186
  mode: nextMode,
5975
4187
  };
5976
4188
  return;
5977
4189
  }
5978
- const result = await updateAgentSettings(agent.id, {
4190
+ await updateAgentSettings(agent.id, {
5979
4191
  mode: nextMode,
5980
4192
  });
5981
- if (result.success === false ||
5982
- result.updates?.mode === false) {
5983
- throw new Error("Mode change was not applied");
5984
- }
5985
- setMode(nextMode);
5986
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
5987
4231
  setAgent((prev) => prev
5988
4232
  ? {
5989
4233
  ...prev,
5990
- mode: nextMode,
5991
- 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
+ }),
5992
4244
  }
5993
4245
  : prev);
5994
4246
  }
5995
- catch (e2) {
5996
- 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
+ }
5997
4270
  }
5998
- } }), _jsxs(Popover, { open: showAgentSettings, onOpenChange: (open) => {
5999
- setShowAgentSettings(open);
6000
- if (!open) {
6001
- setShowSkillPicker(false);
4271
+ catch (err) {
4272
+ console.error("Failed to persist agent model", err);
6002
4273
  }
6003
- }, 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) => {
6004
- const target = e.target;
6005
- if (target?.closest('[data-help-panel="true"]')) {
6006
- e.preventDefault();
6007
- }
6008
- }, onPointerDownOutside: (e) => {
6009
- const target = e.target;
6010
- if (target?.closest('[data-help-panel="true"]')) {
6011
- e.preventDefault();
6012
- }
6013
- }, onFocusOutside: (e) => {
6014
- const target = e.target;
6015
- if (target?.closest('[data-help-panel="true"]')) {
6016
- e.preventDefault();
6017
- }
6018
- }, 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) => {
6019
- const nextProfile = profiles.find((x) => x.id === val);
6020
- if (!nextProfile)
6021
- return;
6022
- setActiveProfile(nextProfile);
6023
- try {
6024
- if (agent?.id && !isLocalOnlyDraftAgent) {
6025
- await updateAgentSettings(agent.id, {
6026
- profileId: nextProfile.id,
6027
- profileName: nextProfile.name,
6028
- });
6029
- }
6030
- else {
6031
- pendingSettingsRef.current = {
6032
- ...(pendingSettingsRef.current || {}),
6033
- profileId: nextProfile.id,
6034
- profileName: nextProfile.name,
6035
- };
6036
- setAgentMetadata((current) => {
6037
- const next = {
6038
- ...(current || {}),
6039
- };
6040
- next.profile = nextProfile.name;
6041
- next.additionalData = {
6042
- ...(next.additionalData || {}),
6043
- profileId: nextProfile.id,
6044
- profileName: nextProfile.name,
6045
- };
6046
- return next;
6047
- });
6048
- }
6049
- setAgent((prev) => prev
6050
- ? {
6051
- ...prev,
6052
- profileId: nextProfile.id,
6053
- profileName: nextProfile.name,
6054
- metadata: JSON.stringify({
6055
- ...(agentMetadata || {}),
6056
- profile: nextProfile.name,
6057
- additionalData: {
6058
- ...(agentMetadata
6059
- ?.additionalData || {}),
6060
- profileId: nextProfile.id,
6061
- profileName: nextProfile.name,
6062
- },
6063
- }),
6064
- }
6065
- : prev);
6066
- }
6067
- catch (err) {
6068
- console.error("Failed to persist agent profile", err);
6069
- }
6070
- } }), 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: () => {
6071
- void handleEditProfileSideBySide();
6072
- setShowAgentSettings(false);
6073
- }, "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: () => {
6074
- void handleOpenProfileSettings();
6075
- setShowAgentSettings(false);
6076
- }, 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) => {
6077
- const nextId = val;
6078
- setSelectedModelId(nextId);
6079
- const modelName = activeProfile?.models?.find((m) => m.id === nextId)?.name || "";
6080
- setAgent((prev) => prev ? { ...prev, model: modelName } : prev);
6081
- try {
6082
- if (agent?.id && !isLocalOnlyDraftAgent) {
6083
- await updateAgentSettings(agent.id, {
6084
- model: modelName,
6085
- });
6086
- }
6087
- else {
6088
- pendingSettingsRef.current = {
6089
- ...(pendingSettingsRef.current || {}),
6090
- modelName,
6091
- };
6092
- }
6093
- }
6094
- catch (err) {
6095
- console.error("Failed to persist agent model", err);
6096
- }
6097
- } })] })) : 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) => {
6098
- setShowSkillPicker(open);
6099
- if (open) {
6100
- setSkillActionError(null);
6101
- }
6102
- }, 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) => {
6103
- const selected = selection[0];
6104
- if (!selected?.id)
6105
- return;
6106
- setSkillActionError(null);
6107
- if (selectableTemplateIdSet.size > 0 &&
6108
- (!selected.templateId ||
6109
- !selectableTemplateIdSet.has(selected.templateId.toLowerCase()))) {
6110
- return;
6111
- }
6112
- if (!manuallyAssignableSkillIdSet.has(selected.id.toLowerCase())) {
6113
- setSkillActionError("This skill cannot be added for the current agent profile.");
6114
- return;
6115
- }
6116
- void (async () => {
6117
- const added = await handleAddSkill(selected.id);
6118
- if (added) {
6119
- setShowSkillPicker(false);
6120
- }
6121
- })();
6122
- } }), skillsLoading && (_jsx("div", { className: "bg-background/70 absolute inset-0 flex items-center justify-center text-[10px] text-gray-500", children: "Loading skills..." }))] }), !skillsLoading &&
6123
- !skillsError &&
6124
- 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 &&
6125
- !skillsError &&
6126
- skillRootIds.length === 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: "No skill roots available." })), !skillsLoading &&
6127
- !skillsError &&
6128
- profileFilteredSkills.length === 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: selectedSkillIds.length > 0
6129
- ? "All addable skills are selected"
6130
- : "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) => {
6131
- const skill = selectedSkills.find((s) => s.id === skillId);
6132
- 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: () => {
6133
- void handleOpenSkillItem(skillId);
6134
- }, 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: () => {
6135
- void handleRemoveSkill(skillId);
6136
- }, title: "Remove skill", "aria-label": `Remove ${skill?.name || skillId}`, children: _jsx(X, { className: "h-2.5 w-2.5", strokeWidth: 1 }) }))] }, skillId));
6137
- }) }))] }), _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
6138
- ? "No available tools for this profile and mode"
6139
- : "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) => {
6140
- const sourceLabel = formatAllowanceSource(allowance.source);
6141
- const pathLabel = "itemPath" in allowance
6142
- ? allowance.itemPath
6143
- : allowance.normalizedPath;
6144
- 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 ||
6145
- "*" }), _jsx("div", { className: "truncate text-[9px] text-gray-500", title: formatAllowanceLabel(allowance), children: pathLabel })] }), (sourceLabel ||
6146
- allowance.grantedBy) && (_jsx("div", { className: "truncate pl-6 text-[9px] text-gray-400", children: [
6147
- sourceLabel,
6148
- allowance.grantedBy,
6149
- ]
6150
- .filter(Boolean)
6151
- .join(" · ") }))] }, `${group.key}-${allowance.operationType}-${pathLabel}-${index}`));
6152
- })] }, group.key)) : null) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isLocalOnlyDraftAgent
6153
- ? "Allowances are shown after the agent is created"
6154
- : "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) => {
6155
- const filterText = (sub.filter || "").trim();
6156
- 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));
6157
- }) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isLocalOnlyDraftAgent
6158
- ? "Subscribed triggers are shown after the agent is created"
6159
- : "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: () => {
6160
4275
  setPrompt(p.prompt);
6161
4276
  setShowPredefined(false);
6162
4277
  if (textareaRef.current)
6163
4278
  textareaRef.current.focus();
6164
- }, children: p.title }, index))) }) })] })) : null, !hideBottomControls &&
6165
- !simpleMode &&
6166
- editContext?.isMobile && (_jsxs(Popover, { open: showCostAndAgent, onOpenChange: setShowCostAndAgent, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { onClick: () => {
6167
- 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)
6168
4281
  setShowCostAndAgent((prev) => !prev);
6169
- }, variant: "outline", size: "sm", className: "h-5.5 w-5.5 cursor-pointer rounded-full", "aria-expanded": editContext?.isMobile
6170
- ? showCostAndAgent
6171
- : 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
6172
4283
  ? {
6173
4284
  input: liveTotals.input,
6174
4285
  output: liveTotals.output,
@@ -6178,10 +4289,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
6178
4289
  outputCost: liveTotals.outputCost,
6179
4290
  cachedCost: liveTotals.cachedCost,
6180
4291
  cacheWriteCost: liveTotals.cacheWriteCost ?? 0,
6181
- imageCost: liveTotals.imageCost ?? 0,
6182
4292
  totalCost: liveTotals.totalCost,
6183
4293
  }
6184
- : totalTokens, effectiveCostLimit: effectiveCostLimit, 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
6185
4295
  ? "Your browser does not support Speech Recognition"
6186
4296
  : isListening
6187
4297
  ? "Stop voice input"
@@ -6202,10 +4312,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
6202
4312
  : allPendingApprovals.length > 0
6203
4313
  ? "Approve or reject pending tool calls first"
6204
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 })) })] })] }));
6205
- })(), !hideBottomControls &&
6206
- !simpleMode &&
6207
- editContext &&
6208
- !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
6209
4316
  ? {
6210
4317
  input: liveTotals.input,
6211
4318
  output: liveTotals.output,
@@ -6215,9 +4322,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
6215
4322
  outputCost: liveTotals.outputCost,
6216
4323
  cachedCost: liveTotals.cachedCost,
6217
4324
  cacheWriteCost: liveTotals.cacheWriteCost ?? 0,
6218
- imageCost: liveTotals.imageCost ?? 0,
6219
4325
  totalCost: liveTotals.totalCost,
6220
4326
  }
6221
- : totalTokens, effectiveCostLimit: effectiveCostLimit, messages: messages, showCompressionPopover: showCompressionPopover, setShowCompressionPopover: setShowCompressionPopover }))] })] }));
4327
+ : totalTokens, effectiveCostLimit: effectiveCostLimit, messages: messages, activeProfile: activeProfile, showCompressionPopover: showCompressionPopover, setShowCompressionPopover: setShowCompressionPopover }))] })] }));
6222
4328
  }
6223
4329
  //# sourceMappingURL=AgentTerminal.js.map