@parhelia/core 0.1.12565 → 0.1.12570

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 +99 -191
  11. package/dist/agents-view/AgentsView.js.map +1 -1
  12. package/dist/agents-view/AgentsWorkspaceView.d.ts +6 -2
  13. package/dist/agents-view/AgentsWorkspaceView.js +113 -266
  14. package/dist/agents-view/AgentsWorkspaceView.js.map +1 -1
  15. package/dist/agents-view/ProfileAgentsGroup.d.ts +1 -2
  16. package/dist/agents-view/ProfileAgentsGroup.js +3 -4
  17. package/dist/agents-view/ProfileAgentsGroup.js.map +1 -1
  18. package/dist/components/ActionButton.d.ts +1 -1
  19. package/dist/components/ActionButton.js.map +1 -1
  20. package/dist/components/FilterInput.d.ts +1 -1
  21. package/dist/components/FilterInput.js +1 -1
  22. package/dist/components/FilterInput.js.map +1 -1
  23. package/dist/components/ui/LanguageSelector.js +4 -2
  24. package/dist/components/ui/LanguageSelector.js.map +1 -1
  25. package/dist/components/ui/PlaceholderInput.js +3 -3
  26. package/dist/components/ui/PlaceholderInput.js.map +1 -1
  27. package/dist/components/ui/PlaceholderInputTypes.js +1 -1
  28. package/dist/components/ui/PlaceholderInputTypes.js.map +1 -1
  29. package/dist/components/ui/alert-dialog.d.ts +1 -1
  30. package/dist/components/ui/alert-dialog.js +10 -6
  31. package/dist/components/ui/alert-dialog.js.map +1 -1
  32. package/dist/components/ui/button.d.ts +4 -4
  33. package/dist/components/ui/button.js +1 -4
  34. package/dist/components/ui/button.js.map +1 -1
  35. package/dist/components/ui/context-menu.d.ts +1 -1
  36. package/dist/components/ui/context-menu.js +4 -12
  37. package/dist/components/ui/context-menu.js.map +1 -1
  38. package/dist/components/ui/copy-button.d.ts +1 -2
  39. package/dist/components/ui/copy-button.js +2 -2
  40. package/dist/components/ui/copy-button.js.map +1 -1
  41. package/dist/components/ui/dialog.d.ts +1 -1
  42. package/dist/components/ui/dialog.js +126 -21
  43. package/dist/components/ui/dialog.js.map +1 -1
  44. package/dist/components/ui/input.d.ts +1 -1
  45. package/dist/components/ui/input.js +3 -5
  46. package/dist/components/ui/input.js.map +1 -1
  47. package/dist/components/ui/paste-button.d.ts +1 -2
  48. package/dist/components/ui/paste-button.js +2 -2
  49. package/dist/components/ui/paste-button.js.map +1 -1
  50. package/dist/components/ui/popover.js +9 -1
  51. package/dist/components/ui/popover.js.map +1 -1
  52. package/dist/components/ui/select.js +1 -1
  53. package/dist/components/ui/select.js.map +1 -1
  54. package/dist/components/ui/styled-dialog-title.js +1 -1
  55. package/dist/components/ui/styled-dialog-title.js.map +1 -1
  56. package/dist/components/ui/tabs.d.ts +1 -1
  57. package/dist/components/ui/tabs.js +11 -4
  58. package/dist/components/ui/tabs.js.map +1 -1
  59. package/dist/config/config.d.ts +2 -4
  60. package/dist/config/config.js +70 -250
  61. package/dist/config/config.js.map +1 -1
  62. package/dist/config/types/workspace.d.ts +0 -6
  63. package/dist/config/types.d.ts +12 -63
  64. package/dist/config/types.js.map +1 -1
  65. package/dist/editor/ComponentInfo.d.ts +4 -0
  66. package/dist/editor/ComponentInfo.js +41 -0
  67. package/dist/editor/ComponentInfo.js.map +1 -0
  68. package/dist/editor/ConfirmationDialog.js +4 -20
  69. package/dist/editor/ConfirmationDialog.js.map +1 -1
  70. package/dist/editor/ContentTree.d.ts +1 -2
  71. package/dist/editor/ContentTree.js +32 -93
  72. package/dist/editor/ContentTree.js.map +1 -1
  73. package/dist/editor/Editor.js +22 -87
  74. package/dist/editor/Editor.js.map +1 -1
  75. package/dist/editor/FieldHistory.js +36 -84
  76. package/dist/editor/FieldHistory.js.map +1 -1
  77. package/dist/editor/FieldListField.js +9 -21
  78. package/dist/editor/FieldListField.js.map +1 -1
  79. package/dist/editor/FieldListFieldWithFallbacks.js +2 -23
  80. package/dist/editor/FieldListFieldWithFallbacks.js.map +1 -1
  81. package/dist/editor/GlobalMenuBar.js +2 -29
  82. package/dist/editor/GlobalMenuBar.js.map +1 -1
  83. package/dist/editor/ImageEditor.js +2 -5
  84. package/dist/editor/ImageEditor.js.map +1 -1
  85. package/dist/editor/ItemInfo.js +1 -36
  86. package/dist/editor/ItemInfo.js.map +1 -1
  87. package/dist/editor/LinkEditorDialog.js +0 -3
  88. package/dist/editor/LinkEditorDialog.js.map +1 -1
  89. package/dist/editor/MainLayout.d.ts +2 -0
  90. package/dist/editor/MainLayout.js +8 -65
  91. package/dist/editor/MainLayout.js.map +1 -1
  92. package/dist/editor/MigrationsView.js +5 -29
  93. package/dist/editor/MigrationsView.js.map +1 -1
  94. package/dist/editor/MobileLayout.js +12 -37
  95. package/dist/editor/MobileLayout.js.map +1 -1
  96. package/dist/editor/PictureCropper.js +45 -54
  97. package/dist/editor/PictureCropper.js.map +1 -1
  98. package/dist/editor/PictureEditor.js +15 -17
  99. package/dist/editor/PictureEditor.js.map +1 -1
  100. package/dist/editor/QuickItemSwitcher.js +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 +496 -2406
  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 -481
  123. package/dist/editor/ai/AgentTerminalStatusBar.js.map +1 -1
  124. package/dist/editor/ai/Agents.js +113 -161
  125. package/dist/editor/ai/Agents.js.map +1 -1
  126. package/dist/editor/ai/AiResponseMessage.d.ts +1 -10
  127. package/dist/editor/ai/AiResponseMessage.js +26 -267
  128. package/dist/editor/ai/AiResponseMessage.js.map +1 -1
  129. package/dist/editor/ai/ContextInfoBar.d.ts +3 -2
  130. package/dist/editor/ai/ContextInfoBar.js +7 -64
  131. package/dist/editor/ai/ContextInfoBar.js.map +1 -1
  132. package/dist/editor/ai/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 +150 -542
  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 +100 -90
  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 +237 -770
  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 +15 -73
  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 -24
  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 -39
@@ -1,11 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import React, { useEffect, useState, useRef, useCallback, useLayoutEffect, useMemo, } from "react";
3
- import { flushSync } from "react-dom";
4
- import { Send, AlertCircle, Loader2, User, Wand2, Square, Mic, MicOff, ChevronDown, ChevronUp, ListTodo, ArrowLeft, DollarSign, ExternalLink, Settings2, Target, X, Plus, } from "lucide-react";
5
- import { getAgent, startAgent, claimAgentBrowser, assignAgentSkill, persistDraftAgent, updateAgentSettings, updateAgentCostLimit, updateAgentContext, getAgentSkillCatalog, getAgentAvailableTools, getAgentOperationAllowances, getAgentTriggerSubscriptions, cancelAgent, canonicalizeAgentMetadata, getPendingPrompts, releaseAgentBrowser, revokeAgentSkill, } from "../services/agentService";
6
- import { parseAgentStatus } from "../services/agentStatus";
3
+ import { Send, AlertCircle, Loader2, User, Wand2, Square, Mic, MicOff, ChevronDown, ChevronUp, ListTodo, ArrowLeft, DollarSign, } from "lucide-react";
4
+ import { getAgent, startAgent, updateAgentSettings, updateAgentCostLimit, updateAgentContext, cancelAgent, canonicalizeAgentMetadata, getPendingPrompts, } from "../services/agentService";
7
5
  import { useEditContext, useFieldsEditContext } from "../client/editContext";
8
- import { localStorageService } from "../services/localStorageService";
9
6
  import { Textarea } from "../../components/ui/textarea";
10
7
  import { Button } from "../../components/ui/button";
11
8
  import { PlaceholderInput, } from "../../components/ui/PlaceholderInput";
@@ -15,145 +12,21 @@ import { AgentDocumentList, } from "./AgentDocumentList";
15
12
  import { AgentEditOperationsPanel } from "./EditOperationsPanel";
16
13
  import { SpawnedAgentsPanel } from "./SpawnedAgentsPanel";
17
14
  import { getComponentById } from "../componentTreeHelper";
18
- import { toUserFacingAgentErrorMessage } from "../services/agentErrorMessage";
19
15
  import { AgentGreeting } from "./AgentGreeting";
20
16
  import { getAgentHistory } from "../services/editService";
21
- import { QuestionnaireInline } from "./dialogs/QuestionnaireInline";
22
- import { getBrowserCaptureClaim, setBrowserCaptureClaim, } from "./dialogs/browserBoundCapture";
23
- import { DIALOG_TYPES, } from "./dialogs/agentDialogTypes";
24
17
  import { Popover, PopoverContent, PopoverTrigger, } from "../../components/ui/popover";
25
18
  import { SecretAgentIcon } from "../ui/Icons";
26
19
  import { formatTime, formatDateTime } from "../utils";
27
20
  import { cn } from "../../lib/utils";
28
- import { sanitizeSvg } from "../../lib/sanitize";
29
21
  import { Select } from "../../components/ui/select";
30
22
  import { AgentTerminalStatusBar } from "./AgentTerminalStatusBar";
23
+ import { useMediaQuery } from "../client/hooks/useMediaQuery";
31
24
  import { SimpleTabs } from "../ui/SimpleTabs";
32
- import { Splitter } from "../ui/Splitter";
33
- import { ScrollingContentTree } from "../ScrollingContentTree";
34
- import { MarkdownDisplay, } from "../../components/MarkdownDisplay";
35
- const userMessageMarkdownComponents = {
36
- h1: (props) => (_jsx("h1", { ...props, className: "mb-2 text-sm leading-5 font-semibold text-gray-900" })),
37
- h2: (props) => (_jsx("h2", { ...props, className: "mb-1.5 text-[13px] leading-5 font-semibold text-gray-900" })),
38
- h3: (props) => (_jsx("h3", { ...props, className: "mb-1 text-[12px] leading-5 font-semibold text-gray-900" })),
39
- h4: (props) => (_jsx("h4", { ...props, className: "mb-1 text-[12px] leading-5 font-medium text-gray-800" })),
40
- p: (props) => (_jsx("p", { ...props, className: "my-1 text-[12px] leading-5 text-gray-700" })),
41
- ul: (props) => (_jsx("ul", { ...props, className: "my-2 ml-5 list-disc space-y-1 text-[12px] leading-5 text-gray-700" })),
42
- ol: (props) => (_jsx("ol", { ...props, className: "my-2 ml-5 list-decimal space-y-1 text-[12px] leading-5 text-gray-700" })),
43
- li: (props) => (_jsx("li", { ...props, className: "text-[12px] leading-5 text-gray-700" })),
44
- 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" })),
45
- 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 })),
46
- };
47
- function buildPlaceholderAgentDetails(agentStub) {
48
- const now = new Date().toISOString();
49
- const updated = agentStub.updatedDate || now;
50
- // AgentDetails has required fields, but some workspaces only pass an Agent stub initially.
51
- // This placeholder keeps streaming/tool-call UI working until `getAgent()` returns full details.
52
- return {
53
- ...agentStub,
54
- name: agentStub.name || "Agent",
55
- userId: agentStub.userId || "",
56
- updatedDate: updated,
57
- profileName: agentStub.profileName || "",
58
- model: agentStub.model || "",
59
- createdDate: agentStub.createdDate || updated,
60
- totalTokensUsed: 0,
61
- totalInputTokens: 0,
62
- totalOutputTokens: 0,
63
- totalCachedInputTokens: 0,
64
- totalInputTokenCost: 0,
65
- totalOutputTokenCost: 0,
66
- totalCachedInputTokenCost: 0,
67
- totalImageCost: 0,
68
- totalCost: 0,
69
- currency: agentStub.currency || "USD",
70
- messageCount: agentStub.messageCount || 0,
71
- };
72
- }
73
- function normalizeDialogAgentId(value) {
74
- return value?.trim().toLowerCase() || "";
75
- }
76
- function formatAllowanceSource(source) {
77
- const normalized = source?.trim();
78
- if (!normalized)
79
- return null;
80
- if (normalized === "user")
81
- return "User granted";
82
- if (normalized.startsWith("preconfigured:profile:"))
83
- return "Profile";
84
- if (normalized.startsWith("preconfigured:skill:"))
85
- return "Skill";
86
- if (normalized.startsWith("system:")) {
87
- return normalized.slice("system:".length).replace(/[-_]+/g, " ").trim();
88
- }
89
- return normalized;
90
- }
91
- function formatAllowanceLabel(allowance) {
92
- return `${allowance.operationType || "*"}${"itemPath" in allowance
93
- ? ` ${allowance.itemPath}`
94
- : ` ${allowance.normalizedPath}`}`;
95
- }
96
- function getAgentRunMessageAgentId(payload) {
97
- const agentId = payload?.agentId;
98
- return typeof agentId === "string" ? normalizeDialogAgentId(agentId) : null;
99
- }
100
- function getAgentRunMessageSeq(payload) {
101
- const seq = payload?.seq;
102
- return typeof seq === "number" ? seq : null;
103
- }
104
- function getAgentRunMessageDetail(type, payload) {
105
- if (type === "agent:run:delta") {
106
- return payload?.type || null;
107
- }
108
- if (type === "agent:run:status") {
109
- return payload?.data?.state || payload?.data?.status || null;
110
- }
111
- if (type === "agent:run:error") {
112
- return payload?.error || null;
113
- }
114
- if (type === "agent:run:complete") {
115
- return payload?.finalStatus || null;
116
- }
117
- if (type === "agent:run:start") {
118
- return payload?.agentName || null;
119
- }
120
- return null;
121
- }
122
- function isHeartbeatRunEventMessage(message) {
123
- if (!message)
124
- return false;
125
- if (message.type === "agent:run:delta") {
126
- const deltaType = message.payload?.type;
127
- return String(deltaType || "").toLowerCase() === "heartbeat";
128
- }
129
- if (message.type === "agent:run:status") {
130
- const state = message.payload?.data?.state || message.payload?.data?.status;
131
- return String(state || "").toLowerCase() === "heartbeat";
132
- }
133
- return false;
134
- }
135
- function getVisibleDialogRegistry() {
136
- const registry = globalThis.__agentDialogVisibleCallbacks;
137
- return registry && typeof registry === "object" ? registry : {};
138
- }
139
- function isAgentErrorStatusValue(status) {
140
- return status === "error";
141
- }
142
25
  // Simple user message component
143
26
  const UserMessage = ({ message }) => {
144
- const content = message.content || "";
145
- const [isTriggerExpanded, setIsTriggerExpanded] = useState(false);
146
- // Trigger-sourced prompts are prefixed by backend as "[Trigger: {name}]: {content}"
147
- const triggerPattern = /^\[Trigger: ([^\]]+)\]:\s*(.*)$/s;
148
- const triggerMatch = content.match(triggerPattern);
149
- const triggerName = triggerMatch?.[1]?.trim() || "";
150
- const triggerContent = triggerMatch?.[2] || "";
151
- const isTriggerMessage = triggerName.length > 0;
152
- if (isTriggerMessage) {
153
- 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 }) }))] }) }));
154
- }
155
27
  // Parse source agent name from content if it starts with "[From ...]:"
156
28
  // Backend formats messages from other agents as "[From {sourceAgentName}]: {content}"
29
+ const content = message.content || "";
157
30
  const fromPattern = /^\[From ([^\]]+)\]:\s*(.*)$/s;
158
31
  const match = content.match(fromPattern);
159
32
  let sourceAgentName;
@@ -178,10 +51,7 @@ const UserMessage = ({ message }) => {
178
51
  message.sourceAgent?.name;
179
52
  }
180
53
  const displayName = sourceAgentName ? `[From ${sourceAgentName}]` : "You";
181
- 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 }) })] })] }));
182
- };
183
- const HeartbeatMessage = ({ message }) => {
184
- 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 })] })] }));
185
55
  };
186
56
  // Helper to extract todos from potentially incomplete JSON during streaming
187
57
  const extractPartialTodos = (jsonText) => {
@@ -415,6 +285,14 @@ const extractTodosFromMessages = (messages) => {
415
285
  todoMap.set(key, todo);
416
286
  }
417
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
+ }
418
296
  return result;
419
297
  };
420
298
  // TodoListPanel component
@@ -422,9 +300,12 @@ const TodoListPanel = ({ messages, agentMetadata, }) => {
422
300
  const [isExpanded, setIsExpanded] = useState(true);
423
301
  // First try to get todos from agent metadata (real-time updates)
424
302
  // Server sends additionalData.todoList directly via contextChanged status
303
+ // Also check top-level todoList for backward compatibility with stored contexts
425
304
  const metadataTodos = (() => {
426
305
  try {
427
- 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;
428
309
  if (todoList?.items && Array.isArray(todoList.items)) {
429
310
  const rawItems = todoList.items
430
311
  .map((item, idx) => ({
@@ -552,17 +433,6 @@ const groupConsecutiveMessages = (agentMessages) => {
552
433
  // Add user message
553
434
  groups.push({ type: "user", messages: [message] });
554
435
  }
555
- else if (message.messageType === "heartbeat" ||
556
- message.role === "system") {
557
- if (currentAssistantGroup.length > 0) {
558
- groups.push({
559
- type: "assistant-group",
560
- messages: currentAssistantGroup,
561
- });
562
- currentAssistantGroup = [];
563
- }
564
- groups.push({ type: "heartbeat", messages: [message] });
565
- }
566
436
  else if (message.role === "assistant") {
567
437
  // Add to current assistant group
568
438
  currentAssistantGroup.push(message);
@@ -630,7 +500,6 @@ const calculateTotalTokens = (messages) => {
630
500
  outputCost: acc.outputCost + (message.outputTokenCost || 0),
631
501
  cachedCost: acc.cachedCost + (message.cachedInputTokenCost || 0),
632
502
  cacheWriteCost: acc.cacheWriteCost,
633
- imageCost: acc.imageCost,
634
503
  totalCost: acc.totalCost + (message.totalCost || 0),
635
504
  };
636
505
  }, {
@@ -642,7 +511,6 @@ const calculateTotalTokens = (messages) => {
642
511
  outputCost: 0,
643
512
  cachedCost: 0,
644
513
  cacheWriteCost: 0,
645
- imageCost: 0,
646
514
  totalCost: 0,
647
515
  });
648
516
  return totals;
@@ -653,84 +521,6 @@ const getOperationsForMessageGroup = (messages, agentOperations) => {
653
521
  const matched = agentOperations.filter((op) => op.toolCallId && toolCallIds.has(op.toolCallId));
654
522
  return matched;
655
523
  };
656
- const stringifyToolField = (value) => {
657
- if (value === undefined || value === null)
658
- return undefined;
659
- if (typeof value === "string") {
660
- return value.trim().length > 0 ? value : undefined;
661
- }
662
- try {
663
- return JSON.stringify(value);
664
- }
665
- catch {
666
- return String(value);
667
- }
668
- };
669
- const parseToolResultValue = (value) => {
670
- if (value === undefined || value === null) {
671
- return undefined;
672
- }
673
- if (typeof value === "object") {
674
- return value;
675
- }
676
- if (typeof value !== "string") {
677
- return String(value);
678
- }
679
- const trimmed = value.trim();
680
- if (!trimmed) {
681
- return undefined;
682
- }
683
- try {
684
- let parsed = JSON.parse(trimmed);
685
- if (typeof parsed === "string" &&
686
- (parsed.startsWith("{") || parsed.startsWith("["))) {
687
- parsed = JSON.parse(parsed);
688
- }
689
- if (parsed && typeof parsed === "object") {
690
- return parsed;
691
- }
692
- }
693
- catch {
694
- // Fall back to the original string when the payload is plain text.
695
- }
696
- return value;
697
- };
698
- const getFirstToolCallEnvelope = (data) => {
699
- if (!data || typeof data !== "object")
700
- return undefined;
701
- const direct = data.toolCall || data.tool_call;
702
- if (direct)
703
- return direct;
704
- const arrayCandidates = [data.tool_calls, data.toolCalls];
705
- for (const candidate of arrayCandidates) {
706
- if (Array.isArray(candidate) && candidate.length > 0) {
707
- return candidate[0];
708
- }
709
- }
710
- return undefined;
711
- };
712
- const extractToolCallFields = (data) => {
713
- const envelope = getFirstToolCallEnvelope(data);
714
- const functionPayload = data?.function || envelope?.function;
715
- const functionName = data?.functionName ||
716
- data?.name ||
717
- functionPayload?.name ||
718
- envelope?.functionName ||
719
- envelope?.name ||
720
- "unknown";
721
- const toolCallId = data?.toolCallId || data?.id || envelope?.id;
722
- const functionArguments = stringifyToolField(data?.functionArguments) ||
723
- stringifyToolField(data?.arguments) ||
724
- stringifyToolField(functionPayload?.arguments) ||
725
- stringifyToolField(envelope?.functionArguments) ||
726
- stringifyToolField(envelope?.arguments) ||
727
- "{}";
728
- return {
729
- toolCallId,
730
- functionName,
731
- functionArguments,
732
- };
733
- };
734
524
  // Convert agent messages to AI terminal format for a response group
735
525
  const convertAgentMessagesToAiFormat = (agentMessages) => {
736
526
  return agentMessages.map((agentMessage) => {
@@ -751,39 +541,28 @@ const convertAgentMessagesToAiFormat = (agentMessages) => {
751
541
  role: agentMessage.role,
752
542
  createdDate: agentMessage.createdDate,
753
543
  tool_calls: agentMessage.toolCalls
754
- ? agentMessage.toolCalls.map((toolCall) => {
755
- const isPruned = !!toolCall.isPruned ||
756
- /^PRUNED$/i.test(toolCall.functionError || "");
757
- const displayResult = parseToolResultValue(toolCall.functionResultRichContent) ?? toolCall.functionResult;
758
- return {
759
- id: toolCall.toolCallId,
760
- displayName: toolCall.functionName,
761
- function: {
762
- name: toolCall.functionName,
763
- arguments: toolCall.functionArguments,
764
- result: displayResult,
765
- error: toolCall.functionError,
766
- },
767
- // Pass through approval info if present on the tool call
768
- requiresApproval: toolCall.requiresApproval,
769
- // Pass through prune metadata so the terminal can render a neutral state
770
- isPruned,
771
- prunedAt: toolCall.prunedAt,
772
- // Pass through isCompleted so ToolCallDisplay knows when to hide spinner
773
- isCompleted: toolCall.isCompleted,
774
- // Tool call is streaming if message is not completed and tool call has no result yet
775
- isStreaming: !agentMessage.isCompleted &&
776
- !toolCall.isCompleted &&
777
- !displayResult &&
778
- !toolCall.functionError &&
779
- !isPruned,
780
- // Pass through message IDs for approval/rejection events
781
- messageId: toolCall.messageId,
782
- dbMessageId: toolCall.dbMessageId,
783
- responseTimeMs: toolCall.responseTimeMs,
784
- createdDate: toolCall.createdDate,
785
- };
786
- })
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
+ }))
787
566
  : [],
788
567
  };
789
568
  if (agentMessage.toolCallId) {
@@ -795,10 +574,10 @@ const convertAgentMessagesToAiFormat = (agentMessages) => {
795
574
  // interface AgentTerminalProps {
796
575
  // agentStub: Agent;
797
576
  // }
798
- 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, }) {
799
578
  const editContext = useEditContext();
800
579
  const fieldsContext = useFieldsEditContext();
801
- const [agent, setAgent] = useState(() => buildPlaceholderAgentDetails(agentStub));
580
+ const [agent, setAgent] = useState(undefined);
802
581
  const [messages, setMessages] = useState([]);
803
582
  const [agentOperations, setAgentOperations] = useState([]);
804
583
  const [prompt, setPrompt] = useState("");
@@ -809,35 +588,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
809
588
  const [activePlaceholderInput, setActivePlaceholderInput] = useState(null);
810
589
  const [allPlaceholdersFilled, setAllPlaceholdersFilled] = useState(false);
811
590
  const [agentMetadata, setAgentMetadata] = useState(null);
812
- const [isBrowserClaimMutationPending, setIsBrowserClaimMutationPending] = useState(false);
813
- const [pendingBrowserCaptureDialogType, setPendingBrowserCaptureDialogType] = useState(null);
814
- // Ensure we always have an agent object for streaming handlers, even before `getAgent()` resolves.
815
- // This prevents early tool calls (e.g., ask-questionnaire) from being dropped in compact/workspace UIs.
816
- useEffect(() => {
817
- setAgent((prev) => {
818
- if (prev?.id === agentStub.id)
819
- return prev;
820
- return buildPlaceholderAgentDetails(agentStub);
821
- });
822
- }, [agentStub.id]);
823
- const observedMessageIdsRef = useRef(new Set());
824
- useEffect(() => {
825
- observedMessageIdsRef.current = new Set();
826
- }, [agentStub.id]);
827
- useEffect(() => {
828
- if (!onMessage)
829
- return;
830
- for (const message of messages) {
831
- if (!message?.id)
832
- continue;
833
- if (!message.isCompleted)
834
- continue;
835
- if (observedMessageIdsRef.current.has(message.id))
836
- continue;
837
- observedMessageIdsRef.current.add(message.id);
838
- onMessage(message);
839
- }
840
- }, [messages, onMessage]);
841
591
  // Generate a stable clientSessionId per component instance for stream deduplication
842
592
  const clientSessionIdRef = useRef(null);
843
593
  if (!clientSessionIdRef.current) {
@@ -849,7 +599,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
849
599
  const [isListening, setIsListening] = useState(false);
850
600
  const recognitionRef = useRef(null);
851
601
  const prevPlaceholderRef = useRef(null);
852
- const promptBeforeVoiceRef = useRef("");
853
602
  // Voice button press-and-hold tracking
854
603
  const voicePressStartRef = useRef(null);
855
604
  const voiceHoldTimerRef = useRef(null);
@@ -859,70 +608,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
859
608
  const [showCompressionPopover, setShowCompressionPopover] = useState(false);
860
609
  // Agent inline dialog state (for component type selector, etc.)
861
610
  const [activeInlineDialog, setActiveInlineDialog] = useState(null);
862
- const dialogTerminalInstanceIdRef = useRef("");
863
- if (!dialogTerminalInstanceIdRef.current) {
864
- dialogTerminalInstanceIdRef.current = crypto.randomUUID();
865
- }
866
- const activeInlineDialogRef = useRef(activeInlineDialog);
867
- const isQuestionnaireDialogOpen = activeInlineDialog?.request.dialogType === "questionnaire";
868
- const orphanTimeoutRef = useRef(null);
869
- useEffect(() => {
870
- activeInlineDialogRef.current = activeInlineDialog;
871
- const visibleRegistry = { ...getVisibleDialogRegistry() };
872
- const callbackId = activeInlineDialog?.request.callbackId || null;
873
- const terminalInstanceId = dialogTerminalInstanceIdRef.current;
874
- const agentKeys = [
875
- normalizeDialogAgentId(agentStubIdRefForDialogs.current),
876
- normalizeDialogAgentId(agentIdRefForDialogs.current),
877
- ].filter(Boolean);
878
- agentKeys.forEach((key) => {
879
- if (callbackId) {
880
- visibleRegistry[key] = {
881
- callbackId,
882
- terminalInstanceId,
883
- };
884
- }
885
- else {
886
- delete visibleRegistry[key];
887
- }
888
- });
889
- globalThis.__agentDialogVisibleCallbacks = visibleRegistry;
890
- }, [activeInlineDialog]);
891
- useEffect(() => {
892
- onQuestionnaireOpenChange?.(isQuestionnaireDialogOpen);
893
- }, [isQuestionnaireDialogOpen, onQuestionnaireOpenChange]);
894
- useLayoutEffect(() => {
895
- if (displayMode !== "summary" || !isQuestionnaireDialogOpen) {
896
- return;
897
- }
898
- const scrollSummaryTerminalToTop = () => {
899
- const container = messagesContainerRef.current;
900
- if (!container) {
901
- return;
902
- }
903
- container.scrollTop = 0;
904
- };
905
- scrollSummaryTerminalToTop();
906
- const frame1 = requestAnimationFrame(() => {
907
- scrollSummaryTerminalToTop();
908
- });
909
- let frame3 = 0;
910
- const frame2 = requestAnimationFrame(() => {
911
- frame3 = requestAnimationFrame(() => {
912
- scrollSummaryTerminalToTop();
913
- });
914
- });
915
- return () => {
916
- cancelAnimationFrame(frame1);
917
- cancelAnimationFrame(frame2);
918
- cancelAnimationFrame(frame3);
919
- };
920
- }, [displayMode, isQuestionnaireDialogOpen]);
921
- useEffect(() => {
922
- return () => {
923
- onQuestionnaireOpenChange?.(false);
924
- };
925
- }, [onQuestionnaireOpenChange]);
926
611
  const isWaitingRef = useRef(false);
927
612
  useEffect(() => {
928
613
  isWaitingRef.current = isWaitingForResponse;
@@ -933,64 +618,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
933
618
  const isStoppingRef = useRef(false);
934
619
  // Server-driven state: true when agent is actively processing (set by WebSocket messages)
935
620
  const [isAgentThinking, setIsAgentThinking] = useState(false);
936
- useEffect(() => {
937
- if (!initialMetadata)
938
- return;
939
- setAgentMetadata((prev) => sanitizeAgentMetadata({
940
- ...(prev || {}),
941
- ...initialMetadata,
942
- additionalData: {
943
- ...(prev?.additionalData || {}),
944
- ...(initialMetadata?.additionalData || {}),
945
- },
946
- }));
947
- }, [initialMetadata]);
948
621
  const hasActiveStreaming = useCallback(() => {
949
622
  const current = messagesRef.current || [];
950
623
  return current.some((m) => !m.isCompleted && m.messageType === "streaming");
951
624
  }, []);
952
- const currentAgentId = agent?.id || agentStub.id;
953
- const recentAgentRunEvents = useMemo(() => {
954
- const normalizedAgentId = normalizeDialogAgentId(currentAgentId);
955
- if (!normalizedAgentId) {
956
- return [];
957
- }
958
- if (!editContext) {
959
- return [];
960
- }
961
- return (editContext.webSocketMessages || [])
962
- .filter((message) => {
963
- if (!message?.type?.startsWith("agent:run:")) {
964
- return false;
965
- }
966
- if (isHeartbeatRunEventMessage(message)) {
967
- return false;
968
- }
969
- return getAgentRunMessageAgentId(message.payload) === normalizedAgentId;
970
- })
971
- .slice(-8)
972
- .map((message) => ({
973
- timestamp: message.timestamp,
974
- type: message.type,
975
- seq: getAgentRunMessageSeq(message.payload),
976
- detail: getAgentRunMessageDetail(message.type, message.payload),
977
- }));
978
- }, [currentAgentId, editContext?.webSocketMessages]);
979
- const appendToolUiEvent = useCallback((type, detail, seq) => {
980
- const timestamp = new Date().toISOString();
981
- setRecentToolUiEvents((prev) => {
982
- const next = [
983
- ...prev,
984
- {
985
- timestamp,
986
- type,
987
- detail: detail || null,
988
- seq: seq ?? null,
989
- },
990
- ];
991
- return next.slice(-40);
992
- });
993
- }, []);
994
625
  // Collect all pending tool calls for batch approval functionality
995
626
  const allPendingApprovals = useMemo(() => {
996
627
  const pending = [];
@@ -1032,82 +663,41 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1032
663
  }, [allPendingApprovals]);
1033
664
  // Handle mode switch to autonomous
1034
665
  const handleSwitchToAutonomous = useCallback(() => {
1035
- const nextMode = "autonomous";
1036
- setMode(nextMode);
1037
- setAgentMetadata((prev) => ({
1038
- ...(prev ?? {}),
1039
- mode: nextMode,
1040
- }));
1041
- setAgent((prev) => {
1042
- if (!prev)
1043
- return prev;
1044
- return {
1045
- ...prev,
1046
- mode: nextMode,
1047
- };
1048
- });
1049
- }, [setAgent, setAgentMetadata]);
666
+ setMode("autonomous");
667
+ }, []);
1050
668
  const [resolvedPageName, setResolvedPageName] = useState(undefined);
1051
669
  const [resolvedComponentName, setResolvedComponentName] = useState(undefined);
1052
670
  const [resolvedFieldName, setResolvedFieldName] = useState(undefined);
1053
671
  const [promptHistory, setPromptHistory] = useState(() => {
1054
- return (localStorageService.getItem("editor.agent.promptHistory") || []);
672
+ if (typeof window !== "undefined") {
673
+ return JSON.parse(localStorage.getItem("editor.agent.promptHistory") || "[]");
674
+ }
675
+ return [];
1055
676
  });
1056
677
  const [currentHistoryIndex, setCurrentHistoryIndex] = useState(-1);
1057
678
  const [showPredefined, setShowPredefined] = useState(false);
1058
679
  const [activeProfile, setActiveProfile] = useState(undefined);
1059
680
  const [selectedModelId, setSelectedModelId] = useState(undefined);
1060
- const normalizeAgentMode = (value) => {
1061
- if (value === "autonomous" ||
1062
- value === "read-only" ||
1063
- value === "supervised") {
1064
- return value;
1065
- }
1066
- return null;
1067
- };
1068
681
  const [mode, setMode] = useState("supervised");
1069
682
  const [queuedPrompts, setQueuedPrompts] = useState([]);
1070
- const [expandedQueuedTriggerIds, setExpandedQueuedTriggerIds] = useState({});
683
+ const isMobile = useMediaQuery("(max-width: 768px)");
1071
684
  const [contextPanelsActiveTab, setContextPanelsActiveTab] = useState(0);
1072
685
  const [hiddenContextPanelTabIds, setHiddenContextPanelTabIds] = useState(new Set());
1073
686
  const [showCostAndAgent, setShowCostAndAgent] = useState(false);
1074
- const [showAgentSettings, setShowAgentSettings] = useState(false);
1075
- const [showSkillPicker, setShowSkillPicker] = useState(false);
1076
- const [availableSkills, setAvailableSkills] = useState([]);
1077
- const [skillRootIds, setSkillRootIds] = useState([]);
1078
- const [selectableTemplateIds, setSelectableTemplateIds] = useState([]);
1079
- const [skillsLoading, setSkillsLoading] = useState(false);
1080
- const [skillsError, setSkillsError] = useState(null);
1081
- const [skillActionError, setSkillActionError] = useState(null);
1082
- const [triggerSubscriptions, setTriggerSubscriptions] = useState([]);
1083
- const [availableTools, setAvailableTools] = useState([]);
1084
- const [operationAllowances, setOperationAllowances] = useState({
1085
- sitecore: [],
1086
- filesystem: [],
1087
- });
1088
- const [triggerSubscriptionsLoading, setTriggerSubscriptionsLoading] = useState(false);
1089
- const [availableToolsLoading, setAvailableToolsLoading] = useState(false);
1090
- const [operationAllowancesLoading, setOperationAllowancesLoading] = useState(false);
1091
- const [triggerSubscriptionsError, setTriggerSubscriptionsError] = useState(null);
1092
- const [availableToolsError, setAvailableToolsError] = useState(null);
1093
- const [operationAllowancesError, setOperationAllowancesError] = useState(null);
1094
- const [toolsSectionExpanded, setToolsSectionExpanded] = useState(false);
1095
- const [allowancesSectionExpanded, setAllowancesSectionExpanded] = useState(false);
1096
- const [subscribedTriggersSectionExpanded, setSubscribedTriggersSectionExpanded,] = useState(false);
1097
- const isPersistedAgent = !!agent?.userId;
1098
- const isLocalOnlyDraftAgent = agent?.status === "new" && !isPersistedAgent;
1099
687
  const hasSpawnedAgents = useMemo(() => {
1100
688
  if (!agentMetadata)
1101
689
  return false;
1102
- const childAgents = agentMetadata?.childAgents;
690
+ const childAgents = agentMetadata?.ChildAgents ||
691
+ agentMetadata?.childAgents;
1103
692
  if (!Array.isArray(childAgents) || childAgents.length === 0)
1104
693
  return false;
1105
- 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));
1106
695
  }, [agentMetadata]);
1107
696
  const hasTodoContent = useMemo(() => {
1108
697
  const metadataTodos = (() => {
1109
698
  try {
1110
- const todoList = agentMetadata?.additionalData?.todoList;
699
+ const todoList = agentMetadata?.additionalData?.todoList ||
700
+ agentMetadata?.todoList;
1111
701
  if (todoList?.items && Array.isArray(todoList.items)) {
1112
702
  const raw = todoList.items.filter((item) => item?.title || item?.text || item?.label || item?.task);
1113
703
  return raw.length > 0;
@@ -1155,9 +745,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1155
745
  .then((result) => {
1156
746
  if (cancelled)
1157
747
  return;
1158
- if (result.type === "success" &&
1159
- result.data &&
1160
- result.data.length > 0) {
748
+ if (result.type === "success" && result.data && result.data.length > 0) {
1161
749
  setHasHistoryContent(true);
1162
750
  }
1163
751
  else {
@@ -1188,38 +776,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1188
776
  });
1189
777
  return () => unsubscribe();
1190
778
  }, [agent?.id, editContext?.addSocketMessageListener]);
1191
- useEffect(() => {
1192
- let active = true;
1193
- const loadSkills = async () => {
1194
- try {
1195
- setSkillsLoading(true);
1196
- setSkillsError(null);
1197
- const catalog = await getAgentSkillCatalog(false);
1198
- if (active) {
1199
- setAvailableSkills(catalog.skills.filter((s) => !s.disabled));
1200
- setSkillRootIds(catalog.rootIds);
1201
- setSelectableTemplateIds(catalog.selectableTemplateIds);
1202
- }
1203
- }
1204
- catch (e) {
1205
- if (active) {
1206
- setSkillsError(e?.message || "Failed to load skills");
1207
- setAvailableSkills([]);
1208
- setSkillRootIds([]);
1209
- setSelectableTemplateIds([]);
1210
- }
1211
- }
1212
- finally {
1213
- if (active) {
1214
- setSkillsLoading(false);
1215
- }
1216
- }
1217
- };
1218
- void loadSkills();
1219
- return () => {
1220
- active = false;
1221
- };
1222
- }, []);
1223
779
  const modeOptions = useMemo(() => [
1224
780
  {
1225
781
  value: "supervised",
@@ -1254,239 +810,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1254
810
  value: m.id,
1255
811
  label: m.name,
1256
812
  })) || []).sort((a, b) => a.label.localeCompare(b.label)), [activeProfile]);
1257
- const metadataSelectedSkillIds = useMemo(() => {
1258
- const rawSkillIds = agentMetadata?.additionalData?.skillIds ?? [];
1259
- if (!Array.isArray(rawSkillIds)) {
1260
- return [];
1261
- }
1262
- return rawSkillIds
1263
- .map((x) => String(x || "").trim())
1264
- .filter((x) => x.length > 0);
1265
- }, [agentMetadata]);
1266
- const backendAssignedSkillIds = useMemo(() => {
1267
- const rawSkillIds = agent?.assignedSkillIds ?? [];
1268
- if (!Array.isArray(rawSkillIds)) {
1269
- return [];
1270
- }
1271
- return rawSkillIds
1272
- .map((x) => String(x || "").trim())
1273
- .filter((x) => x.length > 0);
1274
- }, [agent]);
1275
- const preloadedSkillIds = useMemo(() => {
1276
- const rawSkillIds = activeProfile?.preloadSkills ?? [];
1277
- if (!Array.isArray(rawSkillIds)) {
1278
- return [];
1279
- }
1280
- return rawSkillIds
1281
- .map((skill) => String(skill?.id || "").trim())
1282
- .filter((id) => id.length > 0);
1283
- }, [activeProfile?.preloadSkills]);
1284
- const autoAssignedSkillIds = useMemo(() => {
1285
- const preloadedIdSet = new Set(preloadedSkillIds.map((id) => id.toLowerCase()));
1286
- const all = isLocalOnlyDraftAgent
1287
- ? preloadedSkillIds
1288
- : backendAssignedSkillIds.filter((id) => preloadedIdSet.has(id.toLowerCase()));
1289
- const seen = new Set();
1290
- const unique = [];
1291
- for (const id of all) {
1292
- const key = id.toLowerCase();
1293
- if (seen.has(key))
1294
- continue;
1295
- seen.add(key);
1296
- unique.push(id);
1297
- }
1298
- return unique;
1299
- }, [backendAssignedSkillIds, isLocalOnlyDraftAgent, preloadedSkillIds]);
1300
- const selectedSkillIds = useMemo(() => {
1301
- const all = [
1302
- ...autoAssignedSkillIds,
1303
- ...backendAssignedSkillIds,
1304
- ...metadataSelectedSkillIds,
1305
- ];
1306
- const seen = new Set();
1307
- const unique = [];
1308
- for (const id of all) {
1309
- const key = id.toLowerCase();
1310
- if (seen.has(key))
1311
- continue;
1312
- seen.add(key);
1313
- unique.push(id);
1314
- }
1315
- return unique;
1316
- }, [autoAssignedSkillIds, backendAssignedSkillIds, metadataSelectedSkillIds]);
1317
- useEffect(() => {
1318
- let active = true;
1319
- if (!showAgentSettings) {
1320
- return () => {
1321
- active = false;
1322
- };
1323
- }
1324
- if (!agent?.id || isLocalOnlyDraftAgent) {
1325
- setTriggerSubscriptions([]);
1326
- setTriggerSubscriptionsLoading(false);
1327
- setTriggerSubscriptionsError(null);
1328
- setAvailableTools([]);
1329
- setAvailableToolsLoading(false);
1330
- setAvailableToolsError(null);
1331
- setOperationAllowances({ sitecore: [], filesystem: [] });
1332
- setOperationAllowancesLoading(false);
1333
- setOperationAllowancesError(null);
1334
- return () => {
1335
- active = false;
1336
- };
1337
- }
1338
- const loadTriggerSubscriptions = async () => {
1339
- try {
1340
- setTriggerSubscriptionsLoading(true);
1341
- setTriggerSubscriptionsError(null);
1342
- const subscriptions = await getAgentTriggerSubscriptions(agent.id);
1343
- if (active) {
1344
- setTriggerSubscriptions(subscriptions);
1345
- }
1346
- }
1347
- catch (e) {
1348
- if (active) {
1349
- setTriggerSubscriptionsError(e?.message || "Failed to load trigger subscriptions");
1350
- }
1351
- }
1352
- finally {
1353
- if (active) {
1354
- setTriggerSubscriptionsLoading(false);
1355
- }
1356
- }
1357
- };
1358
- const loadOperationAllowances = async () => {
1359
- try {
1360
- setOperationAllowancesLoading(true);
1361
- setOperationAllowancesError(null);
1362
- const allowances = await getAgentOperationAllowances(agent.id);
1363
- if (active) {
1364
- setOperationAllowances(allowances);
1365
- }
1366
- }
1367
- catch (e) {
1368
- if (active) {
1369
- setOperationAllowancesError(e?.message || "Failed to load allowances");
1370
- }
1371
- }
1372
- finally {
1373
- if (active) {
1374
- setOperationAllowancesLoading(false);
1375
- }
1376
- }
1377
- };
1378
- const loadAvailableTools = async () => {
1379
- try {
1380
- setAvailableToolsLoading(true);
1381
- setAvailableToolsError(null);
1382
- const tools = await getAgentAvailableTools(agent.id);
1383
- if (active) {
1384
- setAvailableTools(tools);
1385
- }
1386
- }
1387
- catch (e) {
1388
- if (active) {
1389
- setAvailableToolsError(e?.message || "Failed to load available tools");
1390
- }
1391
- }
1392
- finally {
1393
- if (active) {
1394
- setAvailableToolsLoading(false);
1395
- }
1396
- }
1397
- };
1398
- void loadTriggerSubscriptions();
1399
- void loadAvailableTools();
1400
- void loadOperationAllowances();
1401
- return () => {
1402
- active = false;
1403
- };
1404
- }, [
1405
- showAgentSettings,
1406
- agent?.id,
1407
- agent?.status,
1408
- agent?.userId,
1409
- agent?.profileId,
1410
- mode,
1411
- selectedSkillIds,
1412
- ]);
1413
- const activeTriggerSubscriptions = useMemo(() => triggerSubscriptions.filter((sub) => sub.isActive), [triggerSubscriptions]);
1414
- const allowanceGroups = useMemo(() => [
1415
- {
1416
- key: "sitecore",
1417
- label: "Sitecore",
1418
- rows: operationAllowances.sitecore,
1419
- },
1420
- {
1421
- key: "filesystem",
1422
- label: "Filesystem",
1423
- rows: operationAllowances.filesystem,
1424
- },
1425
- ], [operationAllowances]);
1426
- const hasAnyAllowances = useMemo(() => operationAllowances.sitecore.length > 0 ||
1427
- operationAllowances.filesystem.length > 0, [operationAllowances]);
1428
- const allowancesTotalCount = useMemo(() => operationAllowances.sitecore.length +
1429
- operationAllowances.filesystem.length, [operationAllowances]);
1430
- const listedProfileSkillIdSet = useMemo(() => {
1431
- const ids = [
1432
- ...(activeProfile?.availableSkills ?? []),
1433
- ...(activeProfile?.allowedSkills ?? []),
1434
- ]
1435
- .map((skill) => String(skill?.id || "").toLowerCase())
1436
- .filter((id) => id.length > 0);
1437
- return new Set(ids);
1438
- }, [activeProfile?.availableSkills, activeProfile?.allowedSkills]);
1439
- const profileFilteredSkills = useMemo(() => {
1440
- if (listedProfileSkillIdSet.size === 0) {
1441
- return [];
1442
- }
1443
- return availableSkills.filter((skill) => listedProfileSkillIdSet.has(skill.id.toLowerCase()));
1444
- }, [availableSkills, listedProfileSkillIdSet]);
1445
- const profileFilteredSkillIdSet = useMemo(() => new Set(profileFilteredSkills.map((s) => s.id.toLowerCase())), [profileFilteredSkills]);
1446
- const manuallyAssignableSkillIdSet = useMemo(() => new Set((activeProfile?.allowedSkills ?? [])
1447
- .map((skill) => String(skill?.id || "").toLowerCase())
1448
- .filter((id) => id.length > 0)), [activeProfile?.allowedSkills]);
1449
- const selectableTemplateIdSet = useMemo(() => new Set(selectableTemplateIds.map((id) => id.toLowerCase())), [selectableTemplateIds]);
1450
- const selectedSkills = useMemo(() => selectedSkillIds
1451
- .map((id) => availableSkills.find((s) => s.id.toLowerCase() === id.toLowerCase()))
1452
- .filter((s) => !!s), [availableSkills, selectedSkillIds]);
1453
- const selectedSkillSet = useMemo(() => new Set(selectedSkillIds.map((id) => id.toLowerCase())), [selectedSkillIds]);
1454
- const autoAssignedSkillSet = useMemo(() => new Set(autoAssignedSkillIds.map((id) => id.toLowerCase())), [autoAssignedSkillIds]);
1455
- const previewAvailableTools = useMemo(() => {
1456
- const baseToolNames = mode === "read-only"
1457
- ? (activeProfile?.readOnlyToolNames ?? [])
1458
- : (activeProfile?.allowedToolNames ?? []);
1459
- const toolNames = new Set();
1460
- for (const toolName of baseToolNames) {
1461
- const normalized = String(toolName || "").trim();
1462
- if (normalized) {
1463
- toolNames.add(normalized);
1464
- }
1465
- }
1466
- for (const skill of selectedSkills) {
1467
- for (const toolName of skill.additionalAllowedTools ?? []) {
1468
- const normalized = String(toolName || "").trim();
1469
- if (normalized) {
1470
- toolNames.add(normalized);
1471
- }
1472
- }
1473
- }
1474
- return Array.from(toolNames).sort((a, b) => a.localeCompare(b));
1475
- }, [
1476
- activeProfile?.allowedToolNames,
1477
- activeProfile?.readOnlyToolNames,
1478
- mode,
1479
- selectedSkills,
1480
- ]);
1481
- const displayedAvailableTools = isLocalOnlyDraftAgent
1482
- ? previewAvailableTools
1483
- : availableTools;
1484
- const displayedAvailableToolsLoading = isLocalOnlyDraftAgent
1485
- ? false
1486
- : availableToolsLoading;
1487
- const displayedAvailableToolsError = isLocalOnlyDraftAgent
1488
- ? null
1489
- : availableToolsError;
1490
813
  // Remove deprecated cost limit fields from metadata to avoid confusion with agent/profile settings
1491
814
  const sanitizeAgentMetadata = useCallback((meta) => {
1492
815
  try {
@@ -1511,239 +834,28 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1511
834
  return meta;
1512
835
  }
1513
836
  }, []);
1514
- const getSkillActionErrorMessage = useCallback((error) => {
1515
- const message = error instanceof Error && error.message.trim()
1516
- ? error.message.trim()
1517
- : "Failed to update skill";
1518
- if (message.includes("Skill is not available for this agent profile")) {
1519
- return "This skill cannot be added for the current agent profile.";
1520
- }
1521
- return message;
1522
- }, []);
1523
- const clearLegacySelectedSkillsFromMetadata = useCallback(async () => {
1524
- const legacySkillIds = metadataSelectedSkillIds;
1525
- if (!agent?.id || legacySkillIds.length === 0) {
1526
- return;
1527
- }
1528
- const current = agentMetadata || {};
1529
- const currentAdditionalData = current.additionalData ||
1530
- {};
1531
- const nextAdditionalData = Object.fromEntries(Object.entries(currentAdditionalData).filter(([key]) => key.toLowerCase() !== "skillids"));
1532
- const next = {
1533
- ...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,
1534
849
  };
1535
- if (Object.keys(nextAdditionalData).length > 0) {
1536
- next.additionalData = nextAdditionalData;
1537
- }
1538
- else {
1539
- delete next.additionalData;
1540
- }
1541
- try {
1542
- await updateAgentContext(agent.id, next);
1543
- setAgentMetadata(next);
1544
- setAgent((prev) => prev ? { ...prev, agentContext: JSON.stringify(next) } : prev);
1545
- }
1546
- catch (e) {
1547
- console.error("Failed to clear legacy selected skills", e);
1548
- }
1549
- }, [
1550
- agent?.id,
1551
- agentMetadata,
1552
- metadataSelectedSkillIds,
1553
- setAgent,
1554
- setAgentMetadata,
1555
- ]);
1556
- const parsePersistedAgentContext = (contextJson) => {
1557
- try {
1558
- if (!contextJson)
1559
- return null;
1560
- const parsedContext = JSON.parse(contextJson);
1561
- if (!parsedContext || typeof parsedContext !== "object") {
1562
- return null;
1563
- }
1564
- return sanitizeAgentMetadata(parsedContext);
1565
- }
1566
- catch {
1567
- return null;
1568
- }
1569
- };
1570
- const buildDraftPersistenceContext = () => {
1571
- let effectiveContext = agentMetadata;
1572
- try {
1573
- const normalizedAgentProfileId = agent?.profileId?.toLowerCase();
1574
- const profile = activeProfile ||
1575
- profiles.find((p) => p.id?.toLowerCase() === normalizedAgentProfileId);
1576
- const isLiveMode = profile?.editorContextMode === "live";
1577
- if (isLiveMode && typeof buildCurrentContext === "function") {
1578
- const freshContext = buildCurrentContext();
1579
- if (freshContext) {
1580
- effectiveContext = {
1581
- ...(agentMetadata || {}),
1582
- items: freshContext.items,
1583
- currentItemId: freshContext.currentItemId,
1584
- components: freshContext.components,
1585
- field: freshContext.field,
1586
- activeWorkspace: freshContext.activeWorkspace,
1587
- hasPageLoaded: freshContext.hasPageLoaded,
1588
- openSidebars: freshContext.openSidebars,
1589
- };
1590
- }
1591
- }
1592
- }
1593
- catch (e) {
1594
- console.warn("[AgentTerminal] Failed to compute draft context:", e);
1595
- }
1596
- return sanitizeAgentMetadata(effectiveContext || null);
1597
- };
1598
- const ensureDraftAgentPersisted = async () => {
1599
- if (!agent?.id) {
1600
- throw new Error("Agent not ready. Please try again.");
1601
- }
1602
- if (!isLocalOnlyDraftAgent) {
1603
- return agent;
1604
- }
1605
- const requestSettings = getPendingRequestSettings();
1606
- if (!requestSettings.profileId) {
1607
- throw new Error("Select an agent profile before adding a skill.");
1608
- }
1609
- const effectiveContext = buildDraftPersistenceContext();
1610
- const persistedAgent = await persistDraftAgent({
1611
- agentId: agent.id,
1612
- sessionId: editContext?.sessionId || undefined,
1613
- profileId: requestSettings.profileId,
1614
- mode: requestSettings.mode,
1615
- model: requestSettings.modelId || undefined,
1616
- name: agent.name,
1617
- context: effectiveContext,
1618
- });
1619
- pendingSettingsRef.current = null;
1620
- const persistedMetadata = parsePersistedAgentContext(persistedAgent.agentContext) ??
1621
- effectiveContext;
1622
- setAgentMetadata(persistedMetadata ? { ...persistedMetadata } : null);
1623
- setAgent((prev) => {
1624
- const next = {
1625
- ...(prev || {}),
1626
- ...persistedAgent,
1627
- };
1628
- if (prev?.messages?.length && !persistedAgent.messages?.length) {
1629
- next.messages = prev.messages;
1630
- }
1631
- return next;
1632
- });
1633
- onAgentUpdate?.(persistedAgent);
1634
- return persistedAgent;
1635
- };
1636
- const handleAddSkill = useCallback(async (skillId) => {
1637
- if (selectedSkillSet.has(skillId.toLowerCase()))
1638
- return;
1639
- if (!agent?.id)
1640
- return;
1641
- try {
1642
- setSkillActionError(null);
1643
- const persistedAgent = await ensureDraftAgentPersisted();
1644
- await assignAgentSkill({ agentId: persistedAgent.id, skillId });
1645
- setAgent((prev) => {
1646
- if (!prev)
1647
- return prev;
1648
- const currentAssigned = Array.isArray(prev.assignedSkillIds)
1649
- ? prev.assignedSkillIds
1650
- : [];
1651
- if (currentAssigned.some((existingId) => String(existingId).toLowerCase() === skillId.toLowerCase())) {
1652
- return prev;
1653
- }
1654
- return {
1655
- ...prev,
1656
- assignedSkillIds: [...currentAssigned, skillId],
1657
- };
1658
- });
1659
- await clearLegacySelectedSkillsFromMetadata();
1660
- return true;
1661
- }
1662
- catch (e) {
1663
- setSkillActionError(getSkillActionErrorMessage(e));
1664
- return false;
1665
- }
1666
- }, [
1667
- agent?.id,
1668
- assignAgentSkill,
1669
- clearLegacySelectedSkillsFromMetadata,
1670
- ensureDraftAgentPersisted,
1671
- getSkillActionErrorMessage,
1672
- setAgent,
1673
- selectedSkillSet,
1674
- ]);
1675
- const handleRemoveSkill = useCallback(async (skillId) => {
1676
- if (!agent?.id)
1677
- return;
1678
- try {
1679
- setSkillActionError(null);
1680
- await revokeAgentSkill({ agentId: agent.id, skillId });
1681
- setAgent((prev) => {
1682
- if (!prev)
1683
- return prev;
1684
- const currentAssigned = Array.isArray(prev.assignedSkillIds)
1685
- ? prev.assignedSkillIds
1686
- : [];
1687
- return {
1688
- ...prev,
1689
- assignedSkillIds: currentAssigned.filter((existingId) => String(existingId).toLowerCase() !== skillId.toLowerCase()),
1690
- };
1691
- });
1692
- await clearLegacySelectedSkillsFromMetadata();
1693
- }
1694
- catch (e) {
1695
- setSkillActionError(getSkillActionErrorMessage(e));
1696
- }
1697
- }, [
1698
- agent?.id,
1699
- clearLegacySelectedSkillsFromMetadata,
1700
- getSkillActionErrorMessage,
1701
- revokeAgentSkill,
1702
- setAgent,
1703
- ]);
1704
- const handleOpenProfileSettings = useCallback(async () => {
1705
- if (!editContext || !activeProfile?.id)
1706
- return;
1707
- const lang = editContext.currentItemDescriptor?.language || "en";
1708
- await editContext.loadItem({
1709
- id: activeProfile.id,
1710
- language: lang,
1711
- version: 0,
1712
- });
1713
- editContext.switchWorkspace("editor");
1714
- }, [editContext, activeProfile?.id]);
1715
- const handleEditProfileSideBySide = useCallback(async () => {
1716
- if (!editContext || !activeProfile?.id)
1717
- return;
1718
- const lang = editContext.currentItemDescriptor?.language || "en";
1719
- const profileItem = {
1720
- id: activeProfile.id,
1721
- language: lang,
1722
- version: 0,
850
+ }
851
+ catch {
852
+ deterministicFlags = {
853
+ deterministic: false,
854
+ seed: undefined,
1723
855
  };
1724
- if (editContext.workspaceId === "agents") {
1725
- editContext.setShowAgentsWorkspaceEditor(true);
1726
- await editContext.loadItem(profileItem);
1727
- return;
1728
- }
1729
- await editContext.loadItem(profileItem, {
1730
- openInNewSlot: true,
1731
- });
1732
- editContext.switchWorkspace("editor");
1733
- }, [editContext, activeProfile?.id]);
1734
- const handleOpenSkillItem = useCallback(async (skillId) => {
1735
- if (!editContext || !skillId)
1736
- return;
1737
- const lang = editContext.currentItemDescriptor?.language || "en";
1738
- await editContext.loadItem({
1739
- id: skillId,
1740
- language: lang,
1741
- version: 0,
1742
- });
1743
- editContext.switchWorkspace("editor");
1744
- }, [editContext]);
856
+ }
1745
857
  useEffect(() => {
1746
- localStorageService.setItem("editor.agent.promptHistory", promptHistory);
858
+ localStorage.setItem("editor.agent.promptHistory", JSON.stringify(promptHistory));
1747
859
  }, [promptHistory]);
1748
860
  useEffect(() => {
1749
861
  // Keep messagesRef synchronized with messages state
@@ -1755,32 +867,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1755
867
  const [liveTotals, setLiveTotals] = useState(null);
1756
868
  // Context window status from backend (extracted from delta cost object)
1757
869
  const [contextWindowStatus, setContextWindowStatus] = useState(null);
1758
- const effectiveModelName = useMemo(() => {
1759
- const fromContextWindow = contextWindowStatus?.model?.trim();
1760
- if (fromContextWindow)
1761
- return fromContextWindow;
1762
- const fromAgent = agent?.model?.trim();
1763
- if (fromAgent)
1764
- return fromAgent;
1765
- const models = activeProfile?.models || [];
1766
- const selectedModelName = selectedModelId
1767
- ? models.find((m) => m.id === selectedModelId)?.name?.trim()
1768
- : undefined;
1769
- if (selectedModelName)
1770
- return selectedModelName;
1771
- const defaultModelName = activeProfile?.defaultModelId
1772
- ? models.find((m) => m.id === activeProfile.defaultModelId)?.name?.trim()
1773
- : undefined;
1774
- if (defaultModelName)
1775
- return defaultModelName;
1776
- return models[0]?.name?.trim() || undefined;
1777
- }, [
1778
- contextWindowStatus?.model,
1779
- agent?.model,
1780
- activeProfile?.models,
1781
- activeProfile?.defaultModelId,
1782
- selectedModelId,
1783
- ]);
1784
870
  // Flag to track when we should create a new message
1785
871
  const shouldCreateNewMessage = useRef(false);
1786
872
  // Keep a ref to the current messages for immediate access
@@ -1791,16 +877,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1791
877
  const placeholderInputRef = useRef(null);
1792
878
  const promptPlaceholderInputRef = useRef(null);
1793
879
  const messagesContainerRef = useRef(null);
1794
- const inlineDialogContainerRef = useRef(null);
1795
880
  const [shouldAutoScroll, setShouldAutoScroll] = useState(true);
1796
- const [recentToolUiEvents, setRecentToolUiEvents] = useState([]);
1797
881
  // WebSocket subscription state for agent streaming
1798
882
  const seenMessageIdsRef = useRef(new Set());
1799
883
  const lastSeqRef = useRef(0);
1800
884
  const subscribedAgentIdRef = useRef(null);
1801
- const toolCallFirstSeenAtRef = useRef({});
1802
- const pendingToolCompletionTimersRef = useRef({});
1803
- // 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)
1804
886
  const pendingSettingsRef = useRef(null);
1805
887
  // Track whether textarea should maintain focus during re-renders
1806
888
  const shouldMaintainFocusRef = useRef(false);
@@ -1822,11 +904,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1822
904
  setIsVoiceSupported(!!SR);
1823
905
  if (SR === undefined) {
1824
906
  setIsVoiceDisabled(true);
1825
- localStorageService.setString(VOICE_DISABLED_KEY, "true");
907
+ localStorage.setItem(VOICE_DISABLED_KEY, "true");
1826
908
  return;
1827
909
  }
1828
910
  else {
1829
- const wasDisabled = localStorageService.getString(VOICE_DISABLED_KEY) === "true";
911
+ const wasDisabled = localStorage.getItem(VOICE_DISABLED_KEY) === "true";
1830
912
  setIsVoiceDisabled(wasDisabled);
1831
913
  }
1832
914
  }
@@ -1837,12 +919,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1837
919
  // Auto-focus terminal input on mount
1838
920
  useEffect(() => {
1839
921
  if (textareaRef.current) {
1840
- try {
1841
- textareaRef.current.focus({ preventScroll: true });
1842
- }
1843
- catch {
1844
- textareaRef.current.focus();
1845
- }
922
+ textareaRef.current.focus();
1846
923
  }
1847
924
  }, []);
1848
925
  // Start voice recognition
@@ -1861,29 +938,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1861
938
  r.lang = editContext?.currentItemDescriptor?.language || "en-US";
1862
939
  r.continuous = true;
1863
940
  r.interimResults = true;
1864
- promptBeforeVoiceRef.current = prompt;
1865
941
  r.onstart = () => {
1866
942
  setIsListening(true);
1867
943
  prevPlaceholderRef.current = inputPlaceholder;
1868
944
  setInputPlaceholder("Listening...");
1869
945
  };
1870
- // Desktop Chrome: single result at index 0, transitions interim→final once.
1871
- // Mobile Chrome: new result index per event, ALL immediately isFinal,
1872
- // each containing the full cumulative transcript. We always take the last
1873
- // non-empty final result and REPLACE the prompt to handle both patterns.
1874
946
  r.onresult = (event) => {
1875
- let lastFinalTranscript = "";
947
+ let finalText = "";
1876
948
  let interimText = "";
1877
- for (let i = event.results.length - 1; i >= 0; i--) {
949
+ for (let i = event.resultIndex; i < event.results.length; i++) {
1878
950
  const res = event.results[i];
1879
- if (res.isFinal &&
1880
- !lastFinalTranscript &&
1881
- (res[0]?.transcript || "").trim()) {
1882
- lastFinalTranscript = res[0].transcript;
1883
- }
1884
- if (!res.isFinal) {
1885
- interimText = (res[0]?.transcript || "") + interimText;
1886
- }
951
+ if (res.isFinal)
952
+ finalText += res[0]?.transcript || "";
953
+ else
954
+ interimText += res[0]?.transcript || "";
1887
955
  }
1888
956
  if (interimText) {
1889
957
  setInputPlaceholder(`Listening... ${interimText.trim()}`);
@@ -1891,11 +959,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1891
959
  else {
1892
960
  setInputPlaceholder("Listening...");
1893
961
  }
1894
- const base = promptBeforeVoiceRef.current;
1895
- const separator = base && !base.endsWith(" ") ? " " : "";
1896
- const recognized = lastFinalTranscript.trim();
1897
- if (recognized) {
1898
- 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
+ });
1899
967
  if (textareaRef.current) {
1900
968
  try {
1901
969
  const v = textareaRef.current.value || "";
@@ -1911,7 +979,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1911
979
  // network error also comes from incompatible chromium client
1912
980
  if (e?.error === "network") {
1913
981
  try {
1914
- localStorageService.setString(VOICE_DISABLED_KEY, "true");
982
+ localStorage.setItem(VOICE_DISABLED_KEY, "true");
1915
983
  setIsVoiceDisabled(true);
1916
984
  }
1917
985
  catch { }
@@ -2080,18 +1148,25 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2080
1148
  // Shared stream message handlers with messageId support
2081
1149
  const createNewStreamMessage = useCallback((messageId, agentData) => {
2082
1150
  const currentAgent = agentData || agent;
2083
- 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
+ }
2084
1159
  // Reduced: avoid verbose logging during streaming
2085
1160
  return {
2086
1161
  id: messageId,
2087
- agentId: effectiveAgentId,
1162
+ agentId: currentAgent.id,
2088
1163
  messageIndex: -1,
2089
1164
  role: "assistant",
2090
1165
  content: "",
2091
1166
  name: "agent",
2092
1167
  messageType: "streaming",
2093
1168
  isCompleted: false,
2094
- model: currentAgent?.model || "",
1169
+ model: currentAgent.model || "",
2095
1170
  tokensUsed: 0,
2096
1171
  inputTokens: 0,
2097
1172
  outputTokens: 0,
@@ -2100,71 +1175,16 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2100
1175
  outputTokenCost: 0,
2101
1176
  cachedInputTokenCost: 0,
2102
1177
  totalCost: 0,
2103
- currency: currentAgent?.currency || "USD",
1178
+ currency: currentAgent.currency || "USD",
2104
1179
  createdDate: new Date().toISOString(),
2105
1180
  toolCalls: [],
2106
1181
  };
2107
- }, [agent, agentStub.id]);
2108
- const stripHeartbeatMessages = useCallback((currentMessages) => currentMessages.filter((message) => message.messageType !== "heartbeat"), []);
2109
- const handleHeartbeatMessage = useCallback((message) => {
2110
- const heartbeatText = (message.data?.message ||
2111
- message.data?.Message ||
2112
- "Still working on your request. This is taking a little longer than usual.").trim() ||
2113
- "Still working on your request. This is taking a little longer than usual.";
2114
- const createdDate = message.timestamp || new Date().toISOString();
2115
- const heartbeatId = `heartbeat-${agent?.id || agentStub.id}`;
2116
- setMessages((prev) => {
2117
- const withoutHeartbeats = stripHeartbeatMessages(prev);
2118
- const updated = [
2119
- ...withoutHeartbeats,
2120
- {
2121
- id: heartbeatId,
2122
- agentId: agent?.id || agentStub.id,
2123
- messageIndex: -1,
2124
- role: "system",
2125
- content: heartbeatText,
2126
- name: "system",
2127
- messageType: "heartbeat",
2128
- isCompleted: true,
2129
- model: "",
2130
- tokensUsed: 0,
2131
- inputTokens: 0,
2132
- outputTokens: 0,
2133
- cachedInputTokens: 0,
2134
- inputTokenCost: 0,
2135
- outputTokenCost: 0,
2136
- cachedInputTokenCost: 0,
2137
- totalCost: 0,
2138
- currency: "USD",
2139
- createdDate,
2140
- toolCalls: [],
2141
- },
2142
- ];
2143
- messagesRef.current = updated;
2144
- return updated;
2145
- });
2146
- }, [agent?.id, agentStub.id, stripHeartbeatMessages]);
2147
- const clearHeartbeatMessages = useCallback(() => {
2148
- setMessages((prev) => {
2149
- const updated = stripHeartbeatMessages(prev);
2150
- if (updated.length === prev.length) {
2151
- return prev;
2152
- }
2153
- messagesRef.current = updated;
2154
- return updated;
2155
- });
2156
- }, [stripHeartbeatMessages]);
1182
+ }, [agent]);
2157
1183
  const handleContentChunk = useCallback((message, agentData) => {
2158
1184
  // Get messageId from data, or generate one from agent ID (for backward compatibility)
2159
1185
  // If no messageId is provided, we'll use the last assistant message or create a new one
2160
1186
  let messageId = message.data?.messageId;
2161
1187
  if (!messageId && agentData?.id) {
2162
- console.warn("[AgentTerminal] Content chunk missing messageId; falling back to local resolution", {
2163
- agentId: agentData.id,
2164
- isIncremental: message.data?.isIncremental,
2165
- previousContentLength: message.data?.previousContentLength,
2166
- totalContentLength: message.data?.totalContentLength,
2167
- });
2168
1188
  // For backward compatibility: if no messageId, find or create the current streaming message
2169
1189
  // This handles cases where the backend doesn't send messageId
2170
1190
  const currentMessages = messagesRef.current;
@@ -2178,7 +1198,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2178
1198
  // If the agent isn't currently running (e.g., we switched tabs after the run
2179
1199
  // completed), skip creating a new streaming message to avoid duplicates.
2180
1200
  const currentAgentStatus = (agentData || agent)?.status;
2181
- const isAgentRunning = currentAgentStatus === "running";
1201
+ const isAgentRunning = currentAgentStatus === "running" || currentAgentStatus === 1;
2182
1202
  if (!isAgentRunning) {
2183
1203
  return;
2184
1204
  }
@@ -2211,8 +1231,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2211
1231
  outputCost: Number(cost.output) || 0,
2212
1232
  cachedCost: Number(cost.cached) || 0,
2213
1233
  cacheWriteCost: Number(cost.cacheWrite) || 0,
2214
- imageCost: Number(cost.imageCost ?? cost.totalImageCost) ||
2215
- 0,
2216
1234
  totalCost: Number(cost.total) || 0,
2217
1235
  currency: "USD",
2218
1236
  };
@@ -2236,20 +1254,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2236
1254
  setIsWaitingForResponse(false);
2237
1255
  shouldCreateNewMessage.current = false;
2238
1256
  }
2239
- // Extract context window info from cost object.
2240
- // Note: contextUsed can legitimately be 0 (especially early in OpenAI streams),
2241
- // so we must check for null/undefined instead of truthiness.
2242
- const contextWindowRaw = cost.contextWindow;
2243
- const contextUsedRaw = cost.contextUsed;
2244
- if (contextWindowRaw !== undefined &&
2245
- contextWindowRaw !== null &&
2246
- contextUsedRaw !== undefined &&
2247
- contextUsedRaw !== null) {
2248
- const contextWindowValue = Number(contextWindowRaw);
2249
- const contextUsedValue = Number(contextUsedRaw);
2250
- if (contextWindowValue > 0 &&
2251
- Number.isFinite(contextUsedValue) &&
2252
- 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) {
2253
1262
  setContextWindowStatus({
2254
1263
  contextWindowTokens: contextWindowValue,
2255
1264
  estimatedInputTokens: contextUsedValue,
@@ -2265,15 +1274,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2265
1274
  const existingMessageIndex = prev.findIndex((msg) => msg.id === messageId);
2266
1275
  if (existingMessageIndex === -1) {
2267
1276
  // Message doesn't exist - create new streaming message
2268
- const previousContentLength = message.data?.previousContentLength || 0;
2269
- if (message.data?.isIncremental && previousContentLength > 0) {
2270
- console.warn("[AgentTerminal] Incremental chunk arrived before its base message existed", {
2271
- messageId,
2272
- previousContentLength,
2273
- totalContentLength: message.data?.totalContentLength,
2274
- deltaLength: (message.data?.deltaContent || "").length,
2275
- });
2276
- }
2277
1277
  const newStreamMessage = createNewStreamMessage(messageId, agentData);
2278
1278
  // Set the content for the new message
2279
1279
  const updatedNewMessage = { ...newStreamMessage };
@@ -2296,21 +1296,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2296
1296
  return prev;
2297
1297
  // Check if existing content is already longer than what we're trying to stream
2298
1298
  const currentContentLength = existingMessage.content?.length || 0;
2299
- const previousContentLength = message.data?.previousContentLength || 0;
2300
1299
  const totalContentLength = message.data?.totalContentLength || 0;
2301
- if (message.data?.isIncremental &&
2302
- previousContentLength !== currentContentLength &&
2303
- (previousContentLength > 0 || currentContentLength > 0)) {
2304
- console.warn("[AgentTerminal] Content chunk length mismatch", {
2305
- messageId,
2306
- previousContentLength,
2307
- currentContentLength,
2308
- totalContentLength,
2309
- deltaLength: (message.data?.deltaContent || "").length,
2310
- });
2311
- }
2312
- if (message.data?.isIncremental &&
2313
- currentContentLength >= totalContentLength &&
1300
+ if (currentContentLength >= totalContentLength &&
2314
1301
  totalContentLength > 0) {
2315
1302
  return prev;
2316
1303
  }
@@ -2334,15 +1321,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2334
1321
  });
2335
1322
  }, [createNewStreamMessage, agent]);
2336
1323
  const handleToolCall = useCallback((message, agentData) => {
2337
- const extractedToolCall = extractToolCallFields(message.data);
2338
- const toolCallId = extractedToolCall.toolCallId || crypto.randomUUID();
1324
+ const toolCallId = message.data?.toolCallId || message.data?.id || crypto.randomUUID();
2339
1325
  // Prefer provided messageId, otherwise fall back to the last streaming assistant message
2340
1326
  let toolCallMessageId = message.data?.messageId;
2341
1327
  if (!toolCallMessageId) {
2342
- console.warn("[AgentTerminal] Tool call missing messageId; falling back", {
2343
- toolCallId,
2344
- toolName: message.data?.name || message.data?.displayName,
2345
- });
2346
1328
  const current = messagesRef.current;
2347
1329
  const lastStreaming = [...current]
2348
1330
  .reverse()
@@ -2350,13 +1332,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2350
1332
  if (lastStreaming?.id) {
2351
1333
  toolCallMessageId = lastStreaming.id;
2352
1334
  }
2353
- else {
2354
- // Tool calls can arrive before any assistant content chunk (common for dialog tools like ask-questionnaire).
2355
- // Create a synthetic streaming message so the UI can render the tool call immediately.
2356
- toolCallMessageId = crypto.randomUUID();
2357
- }
2358
1335
  }
2359
- appendToolUiEvent("ui:tool-call-targeted", `${extractedToolCall.functionName || "unknown"} toolCallId=${toolCallId} targetMessageId=${toolCallMessageId || "none"} providedMessageId=${String(message.data?.messageId || "none")}`);
2360
1336
  // Find or create the target message for this tool call
2361
1337
  if (toolCallMessageId) {
2362
1338
  const currentMessages = messagesRef.current;
@@ -2383,25 +1359,24 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2383
1359
  }
2384
1360
  // Add tool call to the message in the array
2385
1361
  if (toolCallMessageId && message.data && toolCallId) {
2386
- const toolCallError = message.data.functionError || message.data.error || "";
2387
- const isPruned = !!message.data?.isPruned || /^PRUNED$/i.test(String(toolCallError));
2388
- const toolCallCreatedDate = message.data.createdDate ||
2389
- message.timestamp ||
2390
- new Date().toISOString();
1362
+ const functionName = message.data.functionName ||
1363
+ message.data.name ||
1364
+ message.data.function?.name ||
1365
+ "unknown";
2391
1366
  const toolCall = {
2392
1367
  id: toolCallId,
2393
1368
  messageId: toolCallMessageId,
2394
1369
  dbMessageId: message.data.messageId, // Database message ID for approval/rejection
2395
1370
  toolCallId: toolCallId,
2396
- functionName: extractedToolCall.functionName,
2397
- functionArguments: extractedToolCall.functionArguments,
1371
+ functionName: functionName,
1372
+ functionArguments: message.data.functionArguments ||
1373
+ message.data.arguments ||
1374
+ JSON.stringify(message.data.function?.arguments || {}),
2398
1375
  functionResult: message.data.functionResult || message.data.result || "",
2399
- functionResultRichContent: message.data.richContent || undefined,
2400
- functionError: toolCallError,
2401
- isPruned,
1376
+ functionError: message.data.functionError || message.data.error || "",
2402
1377
  isCompleted: false,
2403
1378
  responseTimeMs: message.data.responseTimeMs,
2404
- createdDate: toolCallCreatedDate,
1379
+ createdDate: new Date().toISOString(),
2405
1380
  requiresApproval: message.data?.requiresApproval,
2406
1381
  };
2407
1382
  // Check for existing tool call - search across ALL messages by toolCallId first
@@ -2440,21 +1415,14 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2440
1415
  // Check if the new data has more information than what we have
2441
1416
  const newArgs = toolCall.functionArguments;
2442
1417
  const existingArgs = existingToolCall.functionArguments;
2443
- const newArgsText = stringifyToolField(newArgs) || "";
2444
- const existingArgsText = stringifyToolField(existingArgs) || "";
2445
- const hasMoreCompleteArgs = (newArgsText.length > existingArgsText.length &&
2446
- newArgsText !== existingArgsText) ||
2447
- (existingArgsText === "{}" && newArgsText !== "{}");
1418
+ const hasMoreCompleteArgs = newArgs && newArgs.length > (existingArgs?.length || 0);
2448
1419
  const hasNewResult = toolCall.functionResult && !existingToolCall.functionResult;
2449
- const hasNewRichContent = toolCall.functionResultRichContent &&
2450
- !existingToolCall.functionResultRichContent;
2451
1420
  const hasNewError = toolCall.functionError && !existingToolCall.functionError;
2452
1421
  const hasNewApprovalInfo = toolCall.requiresApproval && !existingToolCall.requiresApproval;
2453
1422
  const hasNewDbMessageId = toolCall.dbMessageId && !existingToolCall.dbMessageId;
2454
1423
  // Only update if there's meaningful new data
2455
1424
  if (hasMoreCompleteArgs ||
2456
1425
  hasNewResult ||
2457
- hasNewRichContent ||
2458
1426
  hasNewError ||
2459
1427
  hasNewApprovalInfo ||
2460
1428
  hasNewDbMessageId) {
@@ -2471,11 +1439,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2471
1439
  updatedToolCalls[idx] = {
2472
1440
  ...existing,
2473
1441
  functionArguments: hasMoreCompleteArgs
2474
- ? newArgsText
2475
- : existingArgsText || existing.functionArguments,
1442
+ ? newArgs
1443
+ : existing.functionArguments,
2476
1444
  functionResult: toolCall.functionResult || existing.functionResult,
2477
- functionResultRichContent: toolCall.functionResultRichContent ||
2478
- existing.functionResultRichContent,
2479
1445
  functionError: toolCall.functionError || existing.functionError,
2480
1446
  requiresApproval: toolCall.requiresApproval || existing.requiresApproval,
2481
1447
  };
@@ -2493,36 +1459,27 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2493
1459
  }
2494
1460
  return; // Done updating existing tool call
2495
1461
  }
2496
- flushSync(() => {
2497
- setMessages((prev) => {
2498
- const updated = prev.map((msg) => {
2499
- if (msg.id !== toolCallMessageId)
2500
- return msg;
2501
- const existingToolCalls = msg.toolCalls || [];
2502
- return { ...msg, toolCalls: [...existingToolCalls, toolCall] };
2503
- });
2504
- messagesRef.current = updated;
2505
- return updated;
1462
+ setMessages((prev) => {
1463
+ const updated = prev.map((msg) => {
1464
+ if (msg.id !== toolCallMessageId)
1465
+ return msg;
1466
+ const existingToolCalls = msg.toolCalls || [];
1467
+ return { ...msg, toolCalls: [...existingToolCalls, toolCall] };
2506
1468
  });
1469
+ messagesRef.current = updated;
1470
+ return updated;
2507
1471
  });
2508
- const messageWithToolCall = messagesRef.current.find((msg) => msg.id === toolCallMessageId);
2509
- appendToolUiEvent("ui:tool-call-attached", `${extractedToolCall.functionName || "unknown"} toolCallId=${toolCallId} targetMessageId=${toolCallMessageId} messageToolCalls=${messageWithToolCall?.toolCalls?.length || 0} assistantMessages=${messagesRef.current.filter((msg) => msg.role === "assistant").length}`);
2510
1472
  // If tool requires approval, agent is now waiting for user action - stop thinking
2511
1473
  if (message.data?.requiresApproval) {
2512
1474
  setIsAgentThinking(false);
2513
1475
  }
2514
1476
  }
2515
- }, [appendToolUiEvent, createNewStreamMessage]);
1477
+ }, [createNewStreamMessage]);
2516
1478
  const handleToolResult = useCallback((message, agentData) => {
2517
- const extractedToolCall = extractToolCallFields(message.data);
2518
- const resultToolCallId = extractedToolCall.toolCallId || crypto.randomUUID();
1479
+ const resultToolCallId = message.data?.toolCallId || message.data?.id || crypto.randomUUID();
2519
1480
  // Prefer provided messageId, otherwise fall back to the last streaming assistant message
2520
1481
  let resultMessageId = message.data?.messageId;
2521
1482
  if (!resultMessageId) {
2522
- console.warn("[AgentTerminal] Tool result missing messageId; falling back", {
2523
- toolCallId: resultToolCallId,
2524
- toolName: message.data?.functionName || message.data?.displayName,
2525
- });
2526
1483
  const current = messagesRef.current;
2527
1484
  const lastStreaming = [...current]
2528
1485
  .reverse()
@@ -2544,7 +1501,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2544
1501
  outputCost: Number(cost.output) || 0,
2545
1502
  cachedCost: Number(cost.cached) || 0,
2546
1503
  cacheWriteCost: Number(cost.cacheWrite) || 0,
2547
- imageCost: Number(cost.imageCost) || 0,
2548
1504
  totalCost: Number(cost.total) || 0,
2549
1505
  currency: "USD",
2550
1506
  };
@@ -2556,20 +1512,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2556
1512
  if (anyNonZero) {
2557
1513
  setLiveTotals(nextTotals);
2558
1514
  }
2559
- // Extract context window info from cost object.
2560
- // Note: contextUsed can legitimately be 0 (especially early in OpenAI streams),
2561
- // so we must check for null/undefined instead of truthiness.
2562
- const contextWindowRaw = cost.contextWindow;
2563
- const contextUsedRaw = cost.contextUsed;
2564
- if (contextWindowRaw !== undefined &&
2565
- contextWindowRaw !== null &&
2566
- contextUsedRaw !== undefined &&
2567
- contextUsedRaw !== null) {
2568
- const contextWindowValue = Number(contextWindowRaw);
2569
- const contextUsedValue = Number(contextUsedRaw);
2570
- if (contextWindowValue > 0 &&
2571
- Number.isFinite(contextUsedValue) &&
2572
- 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) {
2573
1520
  setContextWindowStatus({
2574
1521
  contextWindowTokens: contextWindowValue,
2575
1522
  estimatedInputTokens: contextUsedValue,
@@ -2593,7 +1540,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2593
1540
  outputCost: Number(data.totalOutputTokenCost) || 0,
2594
1541
  cachedCost: Number(data.totalCachedTokenCost) || 0,
2595
1542
  cacheWriteCost: Number(data.totalCacheWriteTokenCost) || 0,
2596
- imageCost: Number(data.totalImageCost) || 0,
2597
1543
  totalCost: Number(data.totalCost) || 0,
2598
1544
  currency: data.currency || "USD",
2599
1545
  };
@@ -2609,10 +1555,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2609
1555
  }
2610
1556
  // Update tool result directly in the messages array
2611
1557
  if (!resultMessageId) {
2612
- appendToolUiEvent("ui:tool-result-dropped", `${extractedToolCall.functionName || "unknown"} toolCallId=${resultToolCallId} reason=no-result-message-id`);
2613
1558
  return;
2614
1559
  }
2615
- appendToolUiEvent("ui:tool-result-targeted", `${extractedToolCall.functionName || "unknown"} toolCallId=${resultToolCallId} targetMessageId=${resultMessageId}`);
2616
1560
  // Update the message with tool result
2617
1561
  setMessages((prev) => {
2618
1562
  const updated = prev.map((msg) => {
@@ -2628,22 +1572,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2628
1572
  const existingToolCall = updatedMessage.toolCalls[toolCallIndex];
2629
1573
  if (existingToolCall && message.data) {
2630
1574
  const updatedToolCalls = [...updatedMessage.toolCalls];
2631
- const nextArgsText = stringifyToolField(extractedToolCall.functionArguments) || "";
2632
- const existingArgsText = stringifyToolField(existingToolCall.functionArguments) || "";
2633
- const hasMoreCompleteArgs = (nextArgsText.length > existingArgsText.length &&
2634
- nextArgsText !== existingArgsText) ||
2635
- (existingArgsText === "{}" && nextArgsText !== "{}");
2636
1575
  const toolCall = {
2637
1576
  id: existingToolCall.id,
2638
1577
  messageId: existingToolCall.messageId,
2639
1578
  toolCallId: existingToolCall.toolCallId,
2640
1579
  functionName: existingToolCall.functionName,
2641
- functionArguments: hasMoreCompleteArgs
2642
- ? nextArgsText
2643
- : existingToolCall.functionArguments,
1580
+ functionArguments: existingToolCall.functionArguments,
2644
1581
  functionResult: message.data.functionResult || message.data.result || "",
2645
- functionResultRichContent: message.data.richContent ||
2646
- existingToolCall.functionResultRichContent,
2647
1582
  functionError: message.data.functionError || message.data.error || "",
2648
1583
  isCompleted: true,
2649
1584
  responseTimeMs: message.data.responseTimeMs,
@@ -2660,21 +1595,23 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2660
1595
  }
2661
1596
  else if (message.data && resultToolCallId && resultMessageId) {
2662
1597
  // Create new tool call if it doesn't exist
2663
- const toolCallCreatedDate = message.data.createdDate ||
2664
- message.timestamp ||
2665
- new Date().toISOString();
1598
+ const functionName = message.data.functionName ||
1599
+ message.data.name ||
1600
+ message.data.function?.name ||
1601
+ "unknown";
2666
1602
  const toolCall = {
2667
1603
  id: resultToolCallId,
2668
1604
  messageId: resultMessageId,
2669
1605
  toolCallId: resultToolCallId,
2670
- functionName: extractedToolCall.functionName,
2671
- functionArguments: extractedToolCall.functionArguments,
1606
+ functionName: functionName,
1607
+ functionArguments: message.data.functionArguments ||
1608
+ message.data.arguments ||
1609
+ JSON.stringify(message.data.function?.arguments || {}),
2672
1610
  functionResult: message.data.functionResult || message.data.result || "",
2673
- functionResultRichContent: message.data.richContent || undefined,
2674
1611
  functionError: message.data.functionError || message.data.error || "",
2675
1612
  isCompleted: true,
2676
1613
  responseTimeMs: message.data.responseTimeMs,
2677
- createdDate: toolCallCreatedDate,
1614
+ createdDate: new Date().toISOString(),
2678
1615
  };
2679
1616
  updatedMessage.toolCalls = [...updatedMessage.toolCalls, toolCall];
2680
1617
  }
@@ -2682,12 +1619,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2682
1619
  return updatedMessage;
2683
1620
  });
2684
1621
  messagesRef.current = updated;
2685
- const messageWithToolResult = updated.find((msg) => msg.id === resultMessageId);
2686
- const matchingToolCall = messageWithToolResult?.toolCalls?.find((tc) => tc.toolCallId === resultToolCallId);
2687
- appendToolUiEvent("ui:tool-result-applied", `${extractedToolCall.functionName || "unknown"} toolCallId=${resultToolCallId} targetMessageId=${resultMessageId} completed=${matchingToolCall?.isCompleted ? "yes" : "no"} messageToolCalls=${messageWithToolResult?.toolCalls?.length || 0}`);
2688
1622
  return updated;
2689
1623
  });
2690
- }, [appendToolUiEvent]);
1624
+ }, []);
2691
1625
  // Listen for local approval resolution to update UI
2692
1626
  useEffect(() => {
2693
1627
  const onApprovalResolved = (ev) => {
@@ -2768,9 +1702,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2768
1702
  // The agent might have been persisted after sending a prompt
2769
1703
  // Only treat as "new" if backend returns 404
2770
1704
  const hasExistingMessages = messagesRef.current.length > 0;
2771
- if (agentStub.status === "new" &&
2772
- !agentStub.userId &&
2773
- !hasExistingMessages) {
1705
+ if (agentStub.status === "new" && !hasExistingMessages) {
2774
1706
  // Only initialize as new if we have no messages yet (initial mount)
2775
1707
  // Derive initial profile from provided metadata if present
2776
1708
  const initialProfileIdFromMeta = (() => {
@@ -2810,7 +1742,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2810
1742
  totalInputTokenCost: 0,
2811
1743
  totalOutputTokenCost: 0,
2812
1744
  totalCachedInputTokenCost: 0,
2813
- totalImageCost: 0,
2814
1745
  totalCost: 0,
2815
1746
  messageCount: 0,
2816
1747
  });
@@ -2853,7 +1784,48 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2853
1784
  }
2854
1785
  })();
2855
1786
  // Create context with top-level properties (what ContextInfoBar expects)
2856
- 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;
2857
1829
  let nextMetadata = null;
2858
1830
  if (initialMetadata) {
2859
1831
  // Merge initial metadata with local context (using top-level structure)
@@ -2929,16 +1901,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2929
1901
  seenMessageIdsRef.current.add(msg.id.toLowerCase());
2930
1902
  }
2931
1903
  });
2932
- // Keep local streaming if the agent is still active (running/waiting); otherwise discard locals.
2933
- // This is important for dialog-style tools (e.g., ask-questionnaire) where the agent may be
2934
- // "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
2935
1905
  const isRunning = agentData.status === "running" || agentData.status === 1;
2936
- const isWaiting = agentData.status === "waitingForApproval" ||
2937
- agentData.status === 2 ||
2938
- agentData.status === "waitingForInput" ||
2939
- agentData.status === "costLimitReached" ||
2940
- agentData.status === 7;
2941
- const keepLocalStreaming = isRunning || isWaiting;
2942
1906
  // Filter local messages to only include streaming/incomplete messages that aren't in DB
2943
1907
  // This prevents duplicates when reloading - DB messages are source of truth for completed messages
2944
1908
  const localStreaming = isRunning
@@ -2956,19 +1920,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2956
1920
  // Don't keep completed messages from local state - DB is source of truth
2957
1921
  return false;
2958
1922
  })
2959
- : keepLocalStreaming
2960
- ? messagesRef.current.filter((localMsg) => {
2961
- if (!localMsg.id)
2962
- return false;
2963
- if (!localMsg.isCompleted &&
2964
- localMsg.messageType === "streaming") {
2965
- const existsInDb = dbMessages.some((dbMsg) => dbMsg.id &&
2966
- dbMsg.id.toLowerCase() === localMsg.id.toLowerCase());
2967
- return !existsInDb;
2968
- }
2969
- return false;
2970
- })
2971
- : [];
1923
+ : [];
2972
1924
  const merged = mergeMessagesById(dbMessages, localStreaming);
2973
1925
  messagesRef.current = merged;
2974
1926
  setMessages(merged);
@@ -2979,7 +1931,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2979
1931
  const runningNow = agentData.status === "running" || agentData.status === 1;
2980
1932
  const waitingForApprovalNow = agentData.status === "waitingForApproval" ||
2981
1933
  agentData.status === 2;
2982
- const waitingForInputNow = agentData.status === "waitingForInput";
2983
1934
  const hasStreamingNow = merged.some((m) => !m.isCompleted && m.messageType === "streaming");
2984
1935
  if (runningNow || hasStreamingNow) {
2985
1936
  setIsWaitingForResponse(true);
@@ -2987,11 +1938,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2987
1938
  // Agent is actively running, show thinking dots
2988
1939
  setIsAgentThinking(true);
2989
1940
  }
2990
- else if (waitingForApprovalNow || waitingForInputNow) {
1941
+ else if (waitingForApprovalNow) {
2991
1942
  setIsWaitingForResponse(false);
2992
1943
  isWaitingRef.current = false;
2993
1944
  setIsConnecting(false);
2994
- // Agent is waiting for user input/approval, hide thinking dots
1945
+ // Agent is waiting for user approval, hide thinking dots
2995
1946
  setIsAgentThinking(false);
2996
1947
  }
2997
1948
  else {
@@ -3040,8 +1991,19 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3040
1991
  if (!contextJson)
3041
1992
  return null;
3042
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
3043
1997
  if (parsedContext && typeof parsedContext === "object") {
3044
- 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;
3045
2007
  }
3046
2008
  return null;
3047
2009
  }
@@ -3127,7 +2089,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3127
2089
  return;
3128
2090
  }
3129
2091
  // Check if cost limit exceeded based on status or cost values
3130
- const statusIndicatesLimit = agent.status === "costLimitReached";
2092
+ const statusIndicatesLimit = agent.status === "costLimitReached" || agent.status === 7;
3131
2093
  // Use liveTotals.totalCost as fallback if agent.totalCost is missing or 0
3132
2094
  const effectiveTotalCost = agent.totalCost || liveTotals?.totalCost || 0;
3133
2095
  const costExceedsLimit = agent.costLimit &&
@@ -3212,9 +2174,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3212
2174
  // Handle agent:profile:switched (profile changed via switch-profile function)
3213
2175
  if (messageType === "agent:profile:switched") {
3214
2176
  const payload = message.payload || {};
3215
- const switchedAgentId = payload.agentId;
3216
- const newProfileId = payload.newProfileId;
3217
- 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;
3218
2180
  if (switchedAgentId === agent.id && newProfileId) {
3219
2181
  // Update the agent's profile
3220
2182
  setAgent((prev) => {
@@ -3255,10 +2217,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3255
2217
  return;
3256
2218
  }
3257
2219
  // For other agent messages, check if this is for our agent
3258
- const agentId = message.payload?.agentId;
3259
- if (agentId !== agent.id) {
2220
+ const agentId = message.payload?.agentId || message.payload?.AgentId;
2221
+ if (agentId !== agent.id)
3260
2222
  return;
3261
- }
3262
2223
  // Handle agent:run:start
3263
2224
  if (messageType === "agent:run:start") {
3264
2225
  // If a stop operation is in progress, ignore this message to prevent
@@ -3267,15 +2228,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3267
2228
  console.log("[AgentTerminal] Ignoring agent:run:start during stop operation");
3268
2229
  return;
3269
2230
  }
3270
- const currentStatus = agentRef.current?.status;
3271
- if (currentStatus === "waitingForInput" ||
3272
- currentStatus === "waitingForApproval" ||
3273
- currentStatus === "costLimitReached") {
3274
- // Replayed start messages arrive before the buffered status payload when
3275
- // reopening an already-paused agent. Preserve the attention state instead
3276
- // of flashing "running" until the follow-up status update lands.
3277
- return;
3278
- }
3279
2231
  // Reset run-scoped deduplication so new run seq values (starting at 1)
3280
2232
  // are not discarded due to previous run's lastSeqRef
3281
2233
  lastSeqRef.current = 0;
@@ -3371,9 +2323,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3371
2323
  if (messageType === "agent:prompt:queued") {
3372
2324
  const { queueEntry } = message.payload;
3373
2325
  if (queueEntry) {
3374
- if (shouldSuppressQueuedPrompt(queueEntry)) {
3375
- return;
3376
- }
3377
2326
  // Add the new prompt to the queued prompts list
3378
2327
  setQueuedPrompts((prev) => {
3379
2328
  // Check if prompt already exists (deduplication)
@@ -3402,29 +2351,17 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3402
2351
  return;
3403
2352
  }
3404
2353
  const { seq, type, data, cost } = message.payload;
3405
- if (type === "ToolCall" || type === "toolCall") {
3406
- }
3407
2354
  // Always allow ContextUpdate messages (metadata updates) regardless of agent status
3408
2355
  const isContextUpdate = type === "ContextUpdate" || type === "contextUpdate";
3409
- const isHeartbeat = type === "Heartbeat" || type === "heartbeat";
3410
- const shouldClearHeartbeat = type === "ContentChunk" ||
3411
- type === "contentChunk" ||
3412
- type === "ToolCall" ||
3413
- type === "toolCall" ||
3414
- type === "ToolResult" ||
3415
- type === "toolResult";
3416
2356
  // Allow deltas if the agent is running OR if we already have an active streaming message
3417
2357
  // OR if the server provided a messageId for targeted updates.
3418
2358
  // This avoids dropping early deltas that may arrive before the 'running' status update.
3419
2359
  // ContextUpdate messages are always allowed as they're metadata updates, not content.
3420
2360
  const isRunning = agent.status === "running" || agent.status === 1;
3421
2361
  const hasStreaming = messagesRef.current.some((m) => !m.isCompleted && m.messageType === "streaming");
3422
- const hasMessageId = !!message?.payload?.data?.messageId;
3423
- if (!isContextUpdate &&
3424
- !isHeartbeat &&
3425
- !isRunning &&
3426
- !hasStreaming &&
3427
- !hasMessageId) {
2362
+ const hasMessageId = !!message?.payload?.data?.messageId ||
2363
+ !!message?.payload?.data?.MessageId;
2364
+ if (!isContextUpdate && !isRunning && !hasStreaming && !hasMessageId) {
3428
2365
  return; // ignore only if we cannot safely target an existing streaming message
3429
2366
  }
3430
2367
  // Deduplicate by sequence
@@ -3441,23 +2378,15 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3441
2378
  cost,
3442
2379
  timestamp: new Date().toISOString(),
3443
2380
  };
3444
- if (shouldClearHeartbeat) {
3445
- clearHeartbeatMessages();
3446
- }
3447
2381
  if (type === "ContentChunk" || type === "contentChunk") {
3448
2382
  handleContentChunk(agentStreamMessage, agent);
3449
2383
  }
3450
2384
  else if (type === "ToolCall" || type === "toolCall") {
3451
- appendToolUiEvent("ui:tool-delta-received", `ToolCall:${String(data?.functionName || data?.name || data?.displayName || "unknown")} messageId=${String(data?.messageId || "none")} activeStreaming=${messagesRef.current.some((m) => !m.isCompleted && m.messageType === "streaming") ? "yes" : "no"}`, seq ?? null);
3452
2385
  handleToolCall(agentStreamMessage, agent);
3453
2386
  }
3454
2387
  else if (type === "ToolResult" || type === "toolResult") {
3455
- appendToolUiEvent("ui:tool-delta-received", `ToolResult:${String(data?.functionName || data?.name || data?.displayName || "unknown")} messageId=${String(data?.messageId || "none")} activeStreaming=${messagesRef.current.some((m) => !m.isCompleted && m.messageType === "streaming") ? "yes" : "no"}`, seq ?? null);
3456
2388
  handleToolResult(agentStreamMessage, agent);
3457
2389
  }
3458
- else if (type === "Heartbeat" || type === "heartbeat") {
3459
- handleHeartbeatMessage(agentStreamMessage);
3460
- }
3461
2390
  else if (type === "ContextUpdate" || type === "contextUpdate") {
3462
2391
  // Handle context updates from streaming - data contains additionalData.todoList and ChildAgents
3463
2392
  const contextData = data;
@@ -3467,8 +2396,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3467
2396
  const current = (prev || {});
3468
2397
  const updated = { ...current };
3469
2398
  // Merge additionalData if present (deep merge to preserve existing values)
3470
- if (contextData.additionalData) {
3471
- const sourceAdditionalData = contextData.additionalData || {};
2399
+ if (contextData.additionalData || contextData.AdditionalData) {
2400
+ const sourceAdditionalData = contextData.additionalData ||
2401
+ contextData.AdditionalData ||
2402
+ {};
3472
2403
  updated.additionalData = {
3473
2404
  ...(current.additionalData || {}),
3474
2405
  ...sourceAdditionalData,
@@ -3476,10 +2407,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3476
2407
  }
3477
2408
  // Merge ChildAgents if present (for spawned agents) - replace entire array
3478
2409
  // Backend sends the complete updated list, not just additions
3479
- if (contextData.childAgents) {
3480
- const childAgents = contextData.childAgents;
2410
+ if (contextData.ChildAgents || contextData.childAgents) {
2411
+ const childAgents = contextData.ChildAgents || contextData.childAgents;
3481
2412
  if (Array.isArray(childAgents)) {
3482
- updated.childAgents = childAgents;
2413
+ updated.ChildAgents = childAgents;
3483
2414
  }
3484
2415
  }
3485
2416
  return updated;
@@ -3505,18 +2436,15 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3505
2436
  // Route based on statusData.state
3506
2437
  try {
3507
2438
  // Normalize various status shapes and handle Cancelled uniformly
3508
- const normalizedStatus = parseAgentStatus(statusData?.state) ||
3509
- parseAgentStatus(statusData?.status);
3510
- if (normalizedStatus === "idle") {
2439
+ const normalizedStatus = statusData?.state ||
2440
+ statusData?.Status ||
2441
+ statusData?.status;
2442
+ if (normalizedStatus === "Cancelled" ||
2443
+ normalizedStatus === "canceled") {
3511
2444
  // Stop indicators and mark any in-progress streaming messages as completed
3512
- clearHeartbeatMessages();
3513
- setAgent((prev) => (prev ? { ...prev, status: "idle" } : prev));
3514
2445
  setIsWaitingForResponse(false);
3515
2446
  isWaitingRef.current = false;
3516
2447
  setIsConnecting(false);
3517
- setIsSubmitting(false);
3518
- shouldCreateNewMessage.current = false;
3519
- setIsAgentThinking(false);
3520
2448
  setMessages((prev) => {
3521
2449
  const updated = prev.map((msg) => !msg.isCompleted && msg.messageType === "streaming"
3522
2450
  ? {
@@ -3559,7 +2487,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3559
2487
  outputCost: Number(totals.totalOutputTokenCost) || 0,
3560
2488
  cachedCost: Number(totals.totalCachedInputTokenCost) || 0,
3561
2489
  cacheWriteCost: Number(totals.totalCacheWriteTokenCost) || 0,
3562
- imageCost: Number(totals.totalImageCost) || 0,
3563
2490
  totalCost: totalCost,
3564
2491
  currency: totals.currency,
3565
2492
  };
@@ -3571,26 +2498,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3571
2498
  if (anyNonZero) {
3572
2499
  setLiveTotals(nextTotals);
3573
2500
  }
3574
- // Fallback context usage update for providers that do not include
3575
- // context usage in every stream delta (for example some OpenAI streams).
3576
- // Prefer explicit status values when present; otherwise derive from totals.
3577
- const contextWindowValue = Number(statusData?.contextWindow ??
3578
- agent?.contextWindowTokens ??
3579
- 0);
3580
- const contextUsedValue = Number(statusData?.contextUsed ??
3581
- (Number(totals.totalInputTokens) || 0) +
3582
- (Number(totals.totalCachedInputTokens) || 0) +
3583
- (Number(totals.totalCacheWriteTokens) || 0));
3584
- if (contextWindowValue > 0 &&
3585
- Number.isFinite(contextUsedValue) &&
3586
- contextUsedValue >= 0) {
3587
- setContextWindowStatus({
3588
- contextWindowTokens: contextWindowValue,
3589
- estimatedInputTokens: contextUsedValue,
3590
- contextUsedPercent: (contextUsedValue / contextWindowValue) * 100,
3591
- model: agent?.model || "",
3592
- });
3593
- }
3594
2501
  // If server provides costLimit along with totals, persist it locally
3595
2502
  if (statusCostLimit) {
3596
2503
  setAgent((prev) => prev &&
@@ -3612,7 +2519,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3612
2519
  return;
3613
2520
  }
3614
2521
  if (statusData?.state === "ToolApprovalsRequired") {
3615
- setPendingBrowserCaptureDialogType(null);
3616
2522
  const msgId = statusData.messageId;
3617
2523
  const ids = statusData.toolCallIds || [];
3618
2524
  if (msgId && Array.isArray(ids) && ids.length > 0) {
@@ -3644,40 +2550,16 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3644
2550
  setIsWaitingForResponse(false);
3645
2551
  return;
3646
2552
  }
3647
- // Handle waiting states explicitly
3648
- if (normalizedStatus === "waitingForApproval") {
3649
- setPendingBrowserCaptureDialogType(null);
2553
+ // Handle "WaitingForApproval" state explicitly
2554
+ if (statusData?.state === "WaitingForApproval" ||
2555
+ statusData?.state === "waitingForApproval") {
3650
2556
  setAgent((prev) => prev ? { ...prev, status: "waitingForApproval" } : prev);
3651
2557
  setIsConnecting(false);
3652
2558
  setIsWaitingForResponse(false);
3653
2559
  setIsAgentThinking(false);
3654
2560
  return;
3655
2561
  }
3656
- if (normalizedStatus === "waitingForInput") {
3657
- const dialogType = typeof statusData?.dialogType === "string"
3658
- ? statusData.dialogType
3659
- : null;
3660
- const isBrowserCaptureWait = dialogType === DIALOG_TYPES.CAPTURE_PAGE_DOM ||
3661
- dialogType === DIALOG_TYPES.CAPTURE_PAGE_SCREENSHOT;
3662
- setPendingBrowserCaptureDialogType(isBrowserCaptureWait ? dialogType : null);
3663
- setAgent((prev) => prev
3664
- ? {
3665
- ...prev,
3666
- status: "waitingForInput",
3667
- statusMessage: isBrowserCaptureWait &&
3668
- typeof statusData?.title === "string" &&
3669
- statusData.title.trim()
3670
- ? statusData.title.trim()
3671
- : prev.statusMessage,
3672
- }
3673
- : prev);
3674
- setIsConnecting(false);
3675
- setIsWaitingForResponse(false);
3676
- setIsAgentThinking(false);
3677
- return;
3678
- }
3679
- if (normalizedStatus === "costLimitReached") {
3680
- setPendingBrowserCaptureDialogType(null);
2562
+ if (statusData?.state === "CostLimitReached") {
3681
2563
  const totalCost = Number(statusData.totalCost) || 0;
3682
2564
  const costLimit = Number(statusData.costLimit) || agent?.costLimit || 0;
3683
2565
  setCostLimitExceeded({
@@ -3695,7 +2577,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3695
2577
  // Server sends additionalData directly (with todoList inside)
3696
2578
  // Also may include ChildAgents for spawned agents
3697
2579
  try {
3698
- const serverAdditionalData = statusData.additionalData || {};
2580
+ const serverAdditionalData = statusData.additionalData || statusData.AdditionalData || {};
3699
2581
  setAgentMetadata((prev) => {
3700
2582
  const current = (prev || {});
3701
2583
  const updated = { ...current };
@@ -3709,10 +2591,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3709
2591
  }
3710
2592
  // Merge ChildAgents if present (for spawned agents) - replace entire array
3711
2593
  // Backend sends the complete updated list, not just additions
3712
- if (statusData.childAgents) {
3713
- const childAgents = statusData.childAgents;
2594
+ if (statusData.ChildAgents || statusData.childAgents) {
2595
+ const childAgents = statusData.ChildAgents || statusData.childAgents;
3714
2596
  if (Array.isArray(childAgents)) {
3715
- updated.childAgents = childAgents;
2597
+ updated.ChildAgents = childAgents;
3716
2598
  }
3717
2599
  }
3718
2600
  return updated;
@@ -3724,10 +2606,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3724
2606
  return;
3725
2607
  }
3726
2608
  // Handle "completed" state (fallback for legacy code paths that send status instead of lifecycle event)
3727
- if (normalizedStatus === "completed") {
2609
+ if (normalizedStatus === "completed" ||
2610
+ normalizedStatus === "Completed") {
3728
2611
  // Reset deduplication for the next run
3729
2612
  lastSeqRef.current = 0;
3730
- clearHeartbeatMessages();
3731
2613
  // Mark the last assistant message as completed
3732
2614
  setMessages((prev) => {
3733
2615
  const updated = prev.map((msg) => msg.role === "assistant" && !msg.isCompleted
@@ -3750,21 +2632,30 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3750
2632
  setIsAgentThinking(false);
3751
2633
  return;
3752
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
+ }
3753
2646
  // Handle "Running" state - agent is actively processing
3754
- if (normalizedStatus === "running") {
3755
- // Update agent status to running and clear any previous error statusMessage
3756
- setAgent((prev) => prev
3757
- ? { ...prev, status: "running", statusMessage: undefined }
3758
- : prev);
2647
+ if (normalizedStatus === "running" ||
2648
+ normalizedStatus === "Running") {
2649
+ // Update agent status to running
2650
+ setAgent((prev) => (prev ? { ...prev, status: "running" } : prev));
3759
2651
  setIsWaitingForResponse(true);
3760
2652
  isWaitingRef.current = true;
3761
2653
  setIsAgentThinking(true);
3762
2654
  return;
3763
2655
  }
3764
2656
  // Handle "Error" state
3765
- if (normalizedStatus === "error") {
3766
- const errorMsg = toUserFacingAgentErrorMessage(statusData?.statusMessage ?? statusData?.error) || "Unknown error";
3767
- clearHeartbeatMessages();
2657
+ if (normalizedStatus === "error" || normalizedStatus === "Error") {
2658
+ const errorMsg = statusData?.statusMessage || statusData?.error || "Unknown error";
3768
2659
  setAgent((prev) => prev
3769
2660
  ? { ...prev, status: "error", statusMessage: errorMsg }
3770
2661
  : prev);
@@ -3784,7 +2675,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3784
2675
  if (messageType === "agent:run:complete") {
3785
2676
  // Reset deduplication for the next run
3786
2677
  lastSeqRef.current = 0;
3787
- clearHeartbeatMessages();
3788
2678
  // Mark the last assistant message as completed
3789
2679
  setMessages((prev) => {
3790
2680
  const updated = prev.map((msg) => msg.role === "assistant" && !msg.isCompleted
@@ -3809,9 +2699,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3809
2699
  }
3810
2700
  // Lifecycle: agent:run:error
3811
2701
  if (messageType === "agent:run:error") {
3812
- const errorMsg = toUserFacingAgentErrorMessage(message.payload?.error) ||
3813
- "AI could not complete this request.";
3814
- clearHeartbeatMessages();
2702
+ const errorMsg = message.payload?.error || "Unknown error";
3815
2703
  // Reset deduplication for the next run after an error
3816
2704
  lastSeqRef.current = 0;
3817
2705
  setError(errorMsg);
@@ -3826,9 +2714,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3826
2714
  }
3827
2715
  }, [
3828
2716
  agent,
3829
- clearHeartbeatMessages,
3830
2717
  handleContentChunk,
3831
- handleHeartbeatMessage,
3832
2718
  handleToolCall,
3833
2719
  handleToolResult,
3834
2720
  onAgentUpdate,
@@ -3883,7 +2769,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3883
2769
  const isRunning = currentAgent.status === "running" || currentAgent.status === 1;
3884
2770
  const isWaitingForApproval = currentAgent.status === "waitingForApproval" ||
3885
2771
  currentAgent.status === 2;
3886
- const isWaitingForInput = currentAgent.status === "waitingForInput";
3887
2772
  if (isRunning) {
3888
2773
  setIsWaitingForResponse(true);
3889
2774
  isWaitingRef.current = true;
@@ -3891,10 +2776,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3891
2776
  // Agent is currently running, show thinking dots
3892
2777
  setIsAgentThinking(true);
3893
2778
  }
3894
- else if (isWaitingForApproval || isWaitingForInput) {
2779
+ else if (isWaitingForApproval) {
3895
2780
  setIsWaitingForResponse(false);
3896
2781
  isWaitingRef.current = false;
3897
- // Agent is waiting for user input/approval, hide thinking dots
2782
+ // Agent is waiting for user approval, hide thinking dots
3898
2783
  setIsAgentThinking(false);
3899
2784
  }
3900
2785
  else {
@@ -3941,157 +2826,36 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3941
2826
  window.addEventListener("editor:focusAgentPrompt", focusHandler);
3942
2827
  return () => window.removeEventListener("editor:focusAgentPrompt", focusHandler);
3943
2828
  }, []);
3944
- // Keep stable refs so we don't miss window events during effect re-runs.
3945
- const agentIdRefForDialogs = useRef(null);
3946
- const agentStubIdRefForDialogs = useRef(agentStub.id);
3947
- const isActiveRefForDialogs = useRef(isActive);
3948
- const scrollToBottomRefForDialogs = useRef(scrollToBottom);
3949
- useEffect(() => {
3950
- agentIdRefForDialogs.current = agent?.id ?? null;
3951
- }, [agent?.id]);
3952
- useEffect(() => {
3953
- const prevId = agentStubIdRefForDialogs.current;
3954
- agentStubIdRefForDialogs.current = agentStub.id;
3955
- const visibleRegistry = { ...getVisibleDialogRegistry() };
3956
- const normalizedPrevId = normalizeDialogAgentId(prevId);
3957
- if (normalizedPrevId) {
3958
- delete visibleRegistry[normalizedPrevId];
3959
- globalThis.__agentDialogVisibleCallbacks = visibleRegistry;
3960
- }
3961
- if (prevId && prevId !== agentStub.id) {
3962
- const orphanedDialog = activeInlineDialogRef.current;
3963
- if (orphanedDialog) {
3964
- setActiveInlineDialog(null);
3965
- window.dispatchEvent(new CustomEvent("agent:dialog:orphaned", {
3966
- detail: { callbackId: orphanedDialog.request.callbackId },
3967
- }));
3968
- }
3969
- }
3970
- }, [agentStub.id]);
3971
- useEffect(() => {
3972
- isActiveRefForDialogs.current = isActive;
3973
- }, [isActive]);
3974
- useEffect(() => {
3975
- scrollToBottomRefForDialogs.current = scrollToBottom;
3976
- }, [scrollToBottom]);
3977
2829
  // Listen for agent inline dialog requests from AgentDialogHandler
3978
2830
  useEffect(() => {
3979
- if (orphanTimeoutRef.current) {
3980
- clearTimeout(orphanTimeoutRef.current);
3981
- orphanTimeoutRef.current = null;
3982
- }
3983
- const globalListeners = (globalThis.__agentDialogMountedAgents ?? []).filter((x) => typeof x === "string");
3984
- const normalizedAgentStubId = normalizeDialogAgentId(agentStubIdRefForDialogs.current);
3985
- if (normalizedAgentStubId &&
3986
- !globalListeners.includes(normalizedAgentStubId)) {
3987
- globalListeners.push(normalizedAgentStubId);
3988
- }
3989
- globalThis.__agentDialogMountedAgents = globalListeners;
3990
2831
  const handleDialogShow = (event) => {
3991
- const detail = event?.detail;
3992
- const request = detail?.request;
3993
- const onComplete = detail?.onComplete;
3994
- const onCancel = detail?.onCancel;
3995
- const terminalAgentId = normalizeDialogAgentId(agentIdRefForDialogs.current);
3996
- const terminalAgentStubId = normalizeDialogAgentId(agentStubIdRefForDialogs.current);
3997
- const isActiveNow = isActiveRefForDialogs.current;
3998
- if (!request)
3999
- return;
4000
- const requestAgentId = normalizeDialogAgentId(request.agentId);
4001
- const agentMatch = !requestAgentId ||
4002
- !terminalAgentStubId ||
4003
- requestAgentId === terminalAgentStubId ||
4004
- requestAgentId === terminalAgentId;
4005
- if (!isActiveNow) {
4006
- return;
4007
- }
4008
- if (!request)
4009
- return;
4010
- // Only handle dialog requests for this terminal's agent stub
4011
- if (requestAgentId &&
4012
- terminalAgentStubId &&
4013
- requestAgentId !== terminalAgentStubId &&
4014
- 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) {
4015
2835
  return;
4016
2836
  }
4017
2837
  console.log("[AgentTerminal] Received inline dialog request:", request);
4018
- setActiveInlineDialog({
4019
- request,
4020
- onComplete: (result) => {
4021
- onComplete(result);
4022
- onInteractionSubmitted?.();
4023
- },
4024
- onCancel: () => {
4025
- onCancel();
4026
- onInteractionSubmitted?.();
4027
- },
4028
- });
2838
+ setActiveInlineDialog({ request, onComplete, onCancel });
4029
2839
  // Notify AgentDialogHandler that we accepted the dialog (stops retry mechanism)
4030
- if (request.callbackId) {
4031
- window.dispatchEvent(new CustomEvent("agent:dialog:accepted", {
4032
- detail: { callbackId: request.callbackId },
4033
- }));
4034
- }
4035
- setTimeout(() => {
4036
- if (request.dialogType === "questionnaire") {
4037
- scrollToBottomRefForDialogs.current?.();
4038
- return;
4039
- }
4040
- scrollToBottomRefForDialogs.current?.();
4041
- }, 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);
4042
2845
  };
4043
2846
  window.addEventListener("agent:dialog:show", handleDialogShow);
4044
- return () => {
4045
- const mounted = (globalThis.__agentDialogMountedAgents ?? []).filter((x) => typeof x === "string");
4046
- globalThis.__agentDialogMountedAgents = mounted.filter((id) => id !== normalizeDialogAgentId(agentStubIdRefForDialogs.current));
4047
- const visibleRegistry = { ...getVisibleDialogRegistry() };
4048
- const idsToClear = [
4049
- normalizeDialogAgentId(agentStubIdRefForDialogs.current),
4050
- normalizeDialogAgentId(agentIdRefForDialogs.current),
4051
- ].filter(Boolean);
4052
- idsToClear.forEach((id) => delete visibleRegistry[id]);
4053
- globalThis.__agentDialogVisibleCallbacks = visibleRegistry;
4054
- // If unmounting with an active dialog, defer the orphan event so that
4055
- // React strict mode remounts can cancel it. Only real unmounts fire.
4056
- const orphanedDialog = activeInlineDialogRef.current;
4057
- if (orphanedDialog) {
4058
- orphanTimeoutRef.current = setTimeout(() => {
4059
- orphanTimeoutRef.current = null;
4060
- window.dispatchEvent(new CustomEvent("agent:dialog:orphaned", {
4061
- detail: { callbackId: orphanedDialog.request.callbackId },
4062
- }));
4063
- }, 300);
4064
- }
4065
- window.removeEventListener("agent:dialog:show", handleDialogShow);
4066
- };
4067
- }, []);
4068
- // Announce when this terminal is ready to accept dialogs.
4069
- // Fire immediately on mount using agentStub.id so the AgentDialogHandler
4070
- // can re-dispatch pending dialogs without waiting for the async agent load.
4071
- // Also fire when agent?.id becomes available in case it differs from the stub.
4072
- useEffect(() => {
4073
- const normalizedStubId = normalizeDialogAgentId(agentStub.id);
4074
- if (normalizedStubId) {
4075
- window.dispatchEvent(new CustomEvent("agent:terminal:ready", {
4076
- detail: {
4077
- agentId: normalizedStubId,
4078
- terminalInstanceId: dialogTerminalInstanceIdRef.current,
4079
- },
4080
- }));
4081
- }
4082
- }, [agentStub.id]);
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
4083
2851
  useEffect(() => {
4084
- const normalizedAgentId = normalizeDialogAgentId(agent?.id);
4085
- const normalizedStubId = normalizeDialogAgentId(agentStub.id);
4086
- if (normalizedAgentId && normalizedAgentId !== normalizedStubId) {
2852
+ if (agent?.id) {
2853
+ console.log("[AgentTerminal] Terminal ready for agent:", agent.id);
4087
2854
  window.dispatchEvent(new CustomEvent("agent:terminal:ready", {
4088
- detail: {
4089
- agentId: normalizedAgentId,
4090
- terminalInstanceId: dialogTerminalInstanceIdRef.current,
4091
- },
2855
+ detail: { agentId: agent.id },
4092
2856
  }));
4093
2857
  }
4094
- }, [agent?.id, agentStub.id]);
2858
+ }, [agent?.id]);
4095
2859
  // Profiles are provided by parent component (Agents). No local loading here.
4096
2860
  // Select active profile based on agent.profileId or agentStub.profileId
4097
2861
  useEffect(() => {
@@ -4102,47 +2866,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4102
2866
  // Use case-insensitive comparison for GUID matching (backend may return different casing)
4103
2867
  const normalizedProfileId = profileIdToUse?.toLowerCase();
4104
2868
  const candidate = normalizedProfileId
4105
- ? profiles.find((p) => p.id?.toLowerCase() === normalizedProfileId)
4106
- : undefined;
4107
- if (!candidate) {
4108
- setActiveProfile(undefined);
4109
- 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);
4110
2874
  }
4111
- // Keep active profile in sync whenever the matching entry in `profiles` changes
4112
- // not only when the profile id changes. Otherwise availableSkills (and similar fields)
4113
- // that are merged in the parent after async loads never update (e.g. Template Builder
4114
- // settings skills merged into the profile).
4115
- setActiveProfile((prev) => {
4116
- if (!prev || prev.id !== candidate.id)
4117
- return candidate;
4118
- const skillKey = (p) => JSON.stringify({
4119
- allowed: (p.allowedSkills ?? []).map((s) => [
4120
- String(s?.id ?? ""),
4121
- String(s?.name ?? ""),
4122
- ]),
4123
- available: (p.availableSkills ?? []).map((s) => [
4124
- String(s?.id ?? ""),
4125
- String(s?.name ?? ""),
4126
- ]),
4127
- });
4128
- if (skillKey(prev) === skillKey(candidate))
4129
- return prev;
4130
- return candidate;
4131
- });
4132
- }, [
4133
- profiles,
4134
- agent?.id,
4135
- agent?.profileId,
4136
- agentStub.id,
4137
- agentStub.profileId,
4138
- ]);
2875
+ }, [profiles, agent?.profileId, agentStub.profileId]);
4139
2876
  // Clear queued prompts when agent changes or is new;
4140
2877
  // initial fetch is handled by loadAgent() for better performance and reliability
4141
2878
  useEffect(() => {
4142
- if (!agent?.id || isLocalOnlyDraftAgent) {
2879
+ if (!agent?.id || agent.status === "new") {
4143
2880
  setQueuedPrompts([]);
4144
2881
  }
4145
- }, [agent?.id, isLocalOnlyDraftAgent]);
2882
+ }, [agent?.id, agent?.status]);
4146
2883
  // Update selected model when the active profile or agent model changes
4147
2884
  useEffect(() => {
4148
2885
  if (!activeProfile)
@@ -4171,16 +2908,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4171
2908
  // Initialize mode from metadata; fall back to agent.Mode from server
4172
2909
  useEffect(() => {
4173
2910
  try {
4174
- const metaMode = normalizeAgentMode(agentMetadata?.mode);
4175
- if (metaMode) {
2911
+ const metaMode = agentMetadata?.mode;
2912
+ if (metaMode === "autonomous" ||
2913
+ metaMode === "read-only" ||
2914
+ metaMode === "supervised") {
4176
2915
  setMode(metaMode);
4177
2916
  return;
4178
2917
  }
4179
2918
  }
4180
2919
  catch { }
4181
2920
  try {
4182
- const serverMode = normalizeAgentMode(agent?.mode);
4183
- if (serverMode) {
2921
+ const serverMode = agent?.mode;
2922
+ if (serverMode === "autonomous" ||
2923
+ serverMode === "read-only" ||
2924
+ serverMode === "supervised") {
4184
2925
  setMode(serverMode);
4185
2926
  }
4186
2927
  }
@@ -4200,7 +2941,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4200
2941
  textareaRef.current.focus();
4201
2942
  }
4202
2943
  }, [messages, activePlaceholderInput]);
4203
- // 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
4204
2945
  const persistPendingSettingsIfNeeded = useCallback(async () => {
4205
2946
  try {
4206
2947
  if (!agent?.id)
@@ -4213,10 +2954,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4213
2954
  payload.model = pending.modelName;
4214
2955
  if (pending.mode)
4215
2956
  payload.mode = pending.mode;
4216
- if (pending.profileId)
4217
- payload.profileId = pending.profileId;
4218
- if (pending.profileName != null)
4219
- payload.profileName = pending.profileName;
4220
2957
  if (Object.keys(payload).length === 0)
4221
2958
  return;
4222
2959
  await updateAgentSettings(agent.id, payload);
@@ -4226,92 +2963,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4226
2963
  console.error("Failed to persist pending settings", e);
4227
2964
  }
4228
2965
  }, [agent?.id]);
4229
- const getPendingRequestSettings = useCallback(() => {
4230
- const pending = pendingSettingsRef.current;
4231
- const requestProfile = pending?.profileId
4232
- ? profiles.find((profile) => profile.id === pending.profileId) ||
4233
- activeProfile ||
4234
- profiles[0]
4235
- : activeProfile || profiles[0];
4236
- let requestModelId = selectedModelId;
4237
- if (pending?.modelName) {
4238
- const requestModel = (requestProfile?.models || []).find((model) => (model.name || "").trim().toLowerCase() ===
4239
- pending.modelName?.trim().toLowerCase());
4240
- requestModelId = requestModel?.id || requestModelId;
4241
- }
4242
- return {
4243
- mode: (pending?.mode || mode),
4244
- profileId: pending?.profileId || requestProfile?.id || "",
4245
- profileName: pending?.profileName || requestProfile?.name || "",
4246
- modelId: requestModelId,
4247
- };
4248
- }, [activeProfile, mode, profiles, selectedModelId]);
4249
- const getSubmitErrorMessage = (error) => {
4250
- const fallback = "Failed to submit prompt. Please try again.";
4251
- if (!(error instanceof Error))
4252
- return fallback;
4253
- const cleaned = toUserFacingAgentErrorMessage(error.message);
4254
- return cleaned || fallback;
4255
- };
4256
- const suppressedQueuedPromptsRef = useRef([]);
4257
- const pruneSuppressedQueuedPrompts = useCallback(() => {
4258
- const cutoff = Date.now() - 15_000;
4259
- suppressedQueuedPromptsRef.current =
4260
- suppressedQueuedPromptsRef.current.filter((entry) => entry.createdAt >= cutoff);
4261
- }, []);
4262
- const registerSuppressedQueuedPrompt = useCallback((agentId, promptText) => {
4263
- const normalizedAgentId = agentId.trim().toLowerCase();
4264
- const normalizedPrompt = promptText.trim();
4265
- if (!normalizedAgentId || !normalizedPrompt) {
4266
- return null;
4267
- }
4268
- pruneSuppressedQueuedPrompts();
4269
- const token = crypto.randomUUID();
4270
- suppressedQueuedPromptsRef.current = [
4271
- ...suppressedQueuedPromptsRef.current,
4272
- {
4273
- token,
4274
- agentId: normalizedAgentId,
4275
- prompt: normalizedPrompt,
4276
- createdAt: Date.now(),
4277
- },
4278
- ];
4279
- return token;
4280
- }, [pruneSuppressedQueuedPrompts]);
4281
- const clearSuppressedQueuedPrompt = useCallback((token) => {
4282
- if (!token) {
4283
- return;
4284
- }
4285
- suppressedQueuedPromptsRef.current =
4286
- suppressedQueuedPromptsRef.current.filter((entry) => entry.token !== token);
4287
- }, []);
4288
- const shouldSuppressQueuedPrompt = useCallback((queueEntry) => {
4289
- const normalizedAgentId = queueEntry?.targetAgentId?.trim().toLowerCase();
4290
- const normalizedPrompt = queueEntry?.prompt?.trim();
4291
- if (!normalizedAgentId || !normalizedPrompt) {
4292
- return false;
4293
- }
4294
- pruneSuppressedQueuedPrompts();
4295
- const matchedEntry = suppressedQueuedPromptsRef.current.find((entry) => entry.agentId === normalizedAgentId &&
4296
- entry.prompt === normalizedPrompt);
4297
- if (!matchedEntry) {
4298
- return false;
4299
- }
4300
- suppressedQueuedPromptsRef.current =
4301
- suppressedQueuedPromptsRef.current.filter((entry) => entry.token !== matchedEntry.token);
4302
- return true;
4303
- }, [pruneSuppressedQueuedPrompts]);
4304
- const cancelActiveInlineDialog = useCallback(() => {
4305
- const activeDialog = activeInlineDialogRef.current;
4306
- if (!activeDialog)
4307
- return;
4308
- try {
4309
- activeDialog.onCancel();
4310
- }
4311
- finally {
4312
- setActiveInlineDialog(null);
4313
- }
4314
- }, []);
4315
2966
  const handleSubmit = async () => {
4316
2967
  // Guard against double-submit and missing context
4317
2968
  if (isSubmitting) {
@@ -4355,12 +3006,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4355
3006
  setError("Agent not ready. Please try again.");
4356
3007
  return;
4357
3008
  }
4358
- const hadQuestionnaireDialogOpen = activeInlineDialogRef.current?.request.dialogType === "questionnaire";
4359
- const suppressedQueuedPromptToken = hadQuestionnaireDialogOpen && savedPrompt
4360
- ? registerSuppressedQueuedPrompt(agentId, savedPrompt)
4361
- : null;
4362
- // A new user prompt supersedes any active questionnaire/inline dialog.
4363
- cancelActiveInlineDialog();
4364
3009
  // Generate a temporary ID for optimistic UI - will be replaced by server ID
4365
3010
  const tempMessageId = `temp-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
4366
3011
  try {
@@ -4447,24 +3092,26 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4447
3092
  console.warn("[AgentTerminal] Failed to compute live context:", e);
4448
3093
  }
4449
3094
  // Add visible test IDs to context (only for Help agent)
4450
- const requestSettings = getPendingRequestSettings();
4451
- const currentProfileName = requestSettings.profileName;
3095
+ const currentProfileName = activeProfile?.name || profiles[0]?.name || "";
4452
3096
  effectiveContext = addVisibleTestIdsToContext(effectiveContext, currentProfileName);
4453
3097
  const request = {
4454
3098
  agentId: agentId,
4455
3099
  message: savedPrompt,
4456
3100
  sessionId: editContext.sessionId,
4457
- profileId: requestSettings.profileId,
3101
+ profileId: activeProfile?.id || profiles[0]?.id || "",
4458
3102
  profile: currentProfileName,
4459
- model: requestSettings.modelId,
4460
- mode: requestSettings.mode,
3103
+ model: selectedModelId,
3104
+ mode: mode,
4461
3105
  context: canonicalizeAgentMetadata(effectiveContext), // Use fresh live context when in live mode
3106
+ deterministic: deterministicFlags.deterministic,
3107
+ seed: deterministicFlags.seed,
4462
3108
  };
4463
3109
  console.log("[AgentTerminal] Calling startAgent API for agent:", agentId);
4464
3110
  const response = await startAgent(request);
4465
3111
  console.log("[AgentTerminal] startAgent response:", response);
4466
3112
  // Check if prompt was queued (agent was already running)
4467
- const wasQueued = response.message?.toLowerCase().includes("queued");
3113
+ const wasQueued = response.message?.toLowerCase().includes("queued") ||
3114
+ response.status === "Queued";
4468
3115
  if (wasQueued) {
4469
3116
  // Prompt was queued - show a brief notification but don't set waiting state
4470
3117
  // The prompt will be processed when the agent becomes idle
@@ -4474,7 +3121,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4474
3121
  isWaitingRef.current = false;
4475
3122
  }
4476
3123
  else {
4477
- clearSuppressedQueuedPrompt(suppressedQueuedPromptToken);
4478
3124
  // Normal submission - set waiting state to show dancing dots immediately
4479
3125
  setIsWaitingForResponse(true);
4480
3126
  isWaitingRef.current = true;
@@ -4489,13 +3135,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4489
3135
  ...prev.filter((p) => p !== savedPrompt).slice(0, 9),
4490
3136
  ]);
4491
3137
  setCurrentHistoryIndex(-1);
4492
- await onInteractionSubmitted?.();
4493
3138
  // WebSocket connection is already active via subscription - no need for SSE
4494
3139
  }
4495
3140
  catch (err) {
4496
3141
  console.error("[AgentTerminal] Failed to submit prompt:", err);
4497
- clearSuppressedQueuedPrompt(suppressedQueuedPromptToken);
4498
- setError(getSubmitErrorMessage(err));
3142
+ setError("Failed to submit prompt. Please try again.");
4499
3143
  setIsWaitingForResponse(false);
4500
3144
  isWaitingRef.current = false;
4501
3145
  // Remove the optimistic user message on API error
@@ -4705,29 +3349,29 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4705
3349
  console.warn("[AgentTerminal] Failed to compute live context for quick message:", e);
4706
3350
  }
4707
3351
  // Add visible test IDs to context (only for Help agent)
4708
- const requestSettings = getPendingRequestSettings();
4709
- const currentProfileName = requestSettings.profileName;
3352
+ const currentProfileName = activeProfile?.name || profiles[0]?.name || "";
4710
3353
  effectiveContext = addVisibleTestIdsToContext(effectiveContext, currentProfileName);
4711
3354
  const request = {
4712
3355
  agentId: agent.id,
4713
3356
  message: savedPrompt,
4714
3357
  sessionId: editContext.sessionId,
4715
- profileId: requestSettings.profileId,
3358
+ profileId: activeProfile?.id || profiles[0]?.id || "",
4716
3359
  profile: currentProfileName,
4717
- model: requestSettings.modelId,
4718
- mode: requestSettings.mode,
3360
+ model: selectedModelId,
3361
+ mode: mode,
4719
3362
  context: canonicalizeAgentMetadata(effectiveContext), // Use fresh live context when in live mode
3363
+ deterministic: deterministicFlags.deterministic,
3364
+ seed: deterministicFlags.seed,
4720
3365
  };
4721
3366
  console.log("[AgentTerminal] Calling startAgent API for quick message");
4722
3367
  await startAgent(request);
4723
3368
  // If user changed mode/model while the agent was new, persist them now
4724
3369
  await persistPendingSettingsIfNeeded();
4725
- await onInteractionSubmitted?.();
4726
3370
  // WebSocket connection is already active via subscription - no need for SSE
4727
3371
  }
4728
3372
  catch (err) {
4729
3373
  console.error("[AgentTerminal] Failed to submit quick message:", err);
4730
- setError(getSubmitErrorMessage(err));
3374
+ setError("Failed to submit prompt. Please try again.");
4731
3375
  setIsWaitingForResponse(false);
4732
3376
  isWaitingRef.current = false;
4733
3377
  // Remove the optimistic user message on API error
@@ -4905,90 +3549,66 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4905
3549
  }
4906
3550
  return context;
4907
3551
  }, [collectVisibleTestIds]);
4908
- const buildPageContextItem = useCallback((item) => ({
4909
- id: item.id,
4910
- language: item.language,
4911
- version: item.version,
4912
- name: editContext?.item?.name,
4913
- path: editContext?.item?.path,
4914
- }), [editContext?.item?.name, editContext?.item?.path]);
4915
- const buildComponentContext = useCallback((item) => {
4916
- if (!editContext?.selection?.length)
4917
- return undefined;
4918
- return editContext.selection.map((componentId) => {
4919
- let componentName;
4920
- let componentType;
4921
- let renderingItemId;
4922
- if (editContext.page) {
4923
- try {
4924
- const component = getComponentById(componentId, editContext.page);
4925
- componentName =
4926
- component?.datasourceItem?.name || component?.name || undefined;
4927
- componentType = component?.type || undefined;
4928
- renderingItemId = component?.rendering?.id || undefined;
4929
- }
4930
- catch { }
4931
- }
4932
- return {
4933
- componentId,
4934
- componentName,
4935
- componentType,
4936
- renderingItemId,
4937
- pageItem: buildPageContextItem(item),
4938
- };
4939
- });
4940
- }, [buildPageContextItem, editContext?.page, editContext?.selection]);
4941
- const buildFieldContext = useCallback(() => {
4942
- const focusedField = fieldsContext?.focusedField;
4943
- if (!focusedField?.fieldId || !focusedField?.item?.id)
4944
- return undefined;
4945
- const fieldItem = focusedField.item;
4946
- const currentItem = editContext?.currentItemDescriptor;
4947
- let fieldItemName = fieldItem.id === currentItem?.id ? editContext?.item?.name : undefined;
4948
- if (!fieldItemName && editContext?.page) {
4949
- try {
4950
- const component = getComponentById(fieldItem.id, editContext.page);
4951
- fieldItemName =
4952
- component?.datasourceItem?.name || component?.name || undefined;
4953
- }
4954
- catch { }
4955
- }
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;
4956
3556
  return {
4957
- fieldId: focusedField.fieldId,
4958
- fieldName: focusedField.fieldName,
4959
- item: {
4960
- id: fieldItem.id,
4961
- language: fieldItem.language || currentItem?.language || "en",
4962
- version: fieldItem.version ?? currentItem?.version ?? 0,
4963
- name: fieldItem.name || fieldItemName,
4964
- },
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,
4965
3602
  };
4966
3603
  }, [
4967
3604
  editContext?.currentItemDescriptor,
3605
+ editContext?.selection,
3606
+ editContext?.workspaceId,
4968
3607
  editContext?.item?.name,
4969
- editContext?.page,
3608
+ editContext?.activeSlotId,
3609
+ editContext?.openSidebars,
4970
3610
  fieldsContext?.focusedField,
4971
3611
  ]);
4972
- const buildEditorContextPayload = useCallback((item) => ({
4973
- items: item ? [buildPageContextItem(item)] : undefined,
4974
- currentItemId: item?.id,
4975
- components: item ? buildComponentContext(item) : undefined,
4976
- field: buildFieldContext(),
4977
- activeWorkspace: editContext?.workspaceId,
4978
- hasPageLoaded: !!editContext?.getActiveSlotContext()?.primaryPageViewContext?.page,
4979
- openSidebars: editContext?.openSidebars,
4980
- }), [
4981
- buildComponentContext,
4982
- buildFieldContext,
4983
- buildPageContextItem,
4984
- editContext,
4985
- ]);
4986
- // Helper function to build current context from editor state
4987
- const buildCurrentContext = useCallback(() => {
4988
- // Return context even without item - we want view info regardless
4989
- const item = editContext?.currentItemDescriptor;
4990
- return buildEditorContextPayload(item);
4991
- }, [buildEditorContextPayload, editContext?.currentItemDescriptor]);
4992
3612
  // Live context updates: watch for changes and update agent context when in "live" mode
4993
3613
  const previousContextRef = useRef("");
4994
3614
  useEffect(() => {
@@ -5088,15 +3708,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5088
3708
  const handleRefreshContext = useCallback(async () => {
5089
3709
  if (!agent?.id)
5090
3710
  return;
5091
- const normalizedAgentProfileId = agent?.profileId?.toLowerCase();
5092
- const refreshProfile = activeProfile ||
5093
- profiles.find((p) => p.id?.toLowerCase() === normalizedAgentProfileId);
5094
- if (refreshProfile?.editorContextMode === "none") {
5095
- editContext?.showInfoToast({
5096
- summary: "This profile excludes editor context.",
5097
- });
5098
- return;
5099
- }
5100
3711
  try {
5101
3712
  const currentCtx = buildCurrentContext();
5102
3713
  if (!currentCtx) {
@@ -5137,73 +3748,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5137
3748
  buildCurrentContext,
5138
3749
  sanitizeAgentMetadata,
5139
3750
  editContext,
5140
- activeProfile,
5141
- profiles,
5142
3751
  ]);
5143
- const browserCaptureClaim = useMemo(() => getBrowserCaptureClaim(agentMetadata), [agentMetadata]);
5144
- const isPendingBrowserCaptureWait = pendingBrowserCaptureDialogType === DIALOG_TYPES.CAPTURE_PAGE_DOM ||
5145
- pendingBrowserCaptureDialogType === DIALOG_TYPES.CAPTURE_PAGE_SCREENSHOT;
5146
- const currentSessionId = editContext?.sessionId?.trim() || "";
5147
- const claimedSessionId = browserCaptureClaim?.sessionId?.trim() || "";
5148
- const isClaimedByCurrentSession = !!currentSessionId &&
5149
- !!claimedSessionId &&
5150
- currentSessionId.toLowerCase() === claimedSessionId.toLowerCase();
5151
- const isClaimedByAnotherBrowser = !!claimedSessionId && !isClaimedByCurrentSession;
5152
- const handleClaimBrowser = useCallback(async (takeOver) => {
5153
- if (!agent?.id || !editContext?.sessionId)
5154
- return;
5155
- setIsBrowserClaimMutationPending(true);
5156
- try {
5157
- const response = await claimAgentBrowser({
5158
- agentId: agent.id,
5159
- sessionId: editContext.sessionId,
5160
- takeOver,
5161
- terminalInstanceId: dialogTerminalInstanceIdRef.current,
5162
- });
5163
- setAgentMetadata((prev) => sanitizeAgentMetadata(setBrowserCaptureClaim(prev, response.claim || null)));
5164
- }
5165
- catch (err) {
5166
- console.error("[AgentTerminal] Failed to claim browser:", err);
5167
- editContext.showErrorToast(err);
5168
- }
5169
- finally {
5170
- setIsBrowserClaimMutationPending(false);
5171
- }
5172
- }, [agent?.id, editContext, sanitizeAgentMetadata]);
5173
- const handleReleaseBrowser = useCallback(async () => {
5174
- if (!agent?.id || !editContext?.sessionId)
5175
- return;
5176
- setIsBrowserClaimMutationPending(true);
5177
- try {
5178
- const response = await releaseAgentBrowser({
5179
- agentId: agent.id,
5180
- sessionId: editContext.sessionId,
5181
- terminalInstanceId: dialogTerminalInstanceIdRef.current,
5182
- });
5183
- setAgentMetadata((prev) => sanitizeAgentMetadata(setBrowserCaptureClaim(prev, response.claim || null)));
5184
- }
5185
- catch (err) {
5186
- console.error("[AgentTerminal] Failed to release browser:", err);
5187
- editContext.showErrorToast(err);
5188
- }
5189
- finally {
5190
- setIsBrowserClaimMutationPending(false);
5191
- }
5192
- }, [agent?.id, editContext, sanitizeAgentMetadata]);
5193
- useEffect(() => {
5194
- return () => {
5195
- if (!agent?.id || !editContext?.sessionId || !isClaimedByCurrentSession) {
5196
- return;
5197
- }
5198
- void releaseAgentBrowser({
5199
- agentId: agent.id,
5200
- sessionId: editContext.sessionId,
5201
- terminalInstanceId: dialogTerminalInstanceIdRef.current,
5202
- }).catch((error) => {
5203
- console.warn("[AgentTerminal] Failed to release browser on unmount:", error);
5204
- });
5205
- };
5206
- }, [agent?.id, editContext?.sessionId, isClaimedByCurrentSession]);
5207
3752
  // Stop current execution/stream safely
5208
3753
  const handleStop = useCallback(async () => {
5209
3754
  try {
@@ -5267,81 +3812,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5267
3812
  if (effectiveCostLimit === undefined) {
5268
3813
  effectiveCostLimit = undefined;
5269
3814
  }
5270
- // Calculate total token usage for cost display.
5271
- // Message rows do not currently persist imageCost, so on refresh we fall back
5272
- // to the persisted agent aggregate for that single field.
5273
- const totalTokens = (() => {
5274
- const totals = calculateTotalTokens(messages);
5275
- return {
5276
- ...totals,
5277
- imageCost: totals.imageCost || Number(agent?.totalImageCost) || 0,
5278
- };
5279
- })();
3815
+ // Calculate total token usage for cost display
3816
+ const totalTokens = calculateTotalTokens(messages);
5280
3817
  // Determine if the agent is actively executing (submitting, connecting, waiting, or streaming)
5281
3818
  const isExecuting = isSubmitting ||
5282
3819
  isConnecting ||
5283
3820
  isWaitingForResponse ||
5284
3821
  hasActiveStreaming();
5285
- const assistantMessageCount = useMemo(() => messages.filter((message) => message.role === "assistant").length, [messages]);
5286
- const messagesWithToolCallsCount = useMemo(() => messages.filter((message) => (message.toolCalls || []).length > 0).length, [messages]);
5287
- const totalToolCallCount = useMemo(() => messages.reduce((sum, message) => sum + (message.toolCalls?.length || 0), 0), [messages]);
5288
- const incompleteToolCallCount = useMemo(() => messages.reduce((sum, message) => sum +
5289
- (message.toolCalls?.filter((toolCall) => !toolCall.isCompleted).length || 0), 0), [messages]);
5290
- const assistantGroupsWithRenderableToolCalls = useMemo(() => {
5291
- const groups = groupConsecutiveMessages(messages);
5292
- return groups.filter((group) => {
5293
- if (group.type !== "assistant-group")
5294
- return false;
5295
- const convertedMessages = convertAgentMessagesToAiFormat(group.messages);
5296
- return convertedMessages.some((message) => (message.tool_calls?.length || 0) > 0);
5297
- }).length;
5298
- }, [messages]);
5299
- const assistantGroupCount = useMemo(() => groupConsecutiveMessages(messages).filter((group) => group.type === "assistant-group").length, [messages]);
5300
- const runDiagnosticsSnapshot = useMemo(() => {
5301
- const lastEvent = recentAgentRunEvents[recentAgentRunEvents.length - 1];
5302
- return {
5303
- agentId: currentAgentId,
5304
- isSubmitting,
5305
- isConnecting,
5306
- isWaitingForResponse,
5307
- isAgentThinking,
5308
- isExecuting,
5309
- hasActiveStreaming: hasActiveStreaming(),
5310
- isSubscribed: normalizeDialogAgentId(subscribedAgentIdRef.current) ===
5311
- normalizeDialogAgentId(currentAgentId),
5312
- lastSeq: lastSeqRef.current,
5313
- lastEventType: lastEvent?.type ?? null,
5314
- lastEventAt: lastEvent?.timestamp ?? null,
5315
- recentEvents: recentAgentRunEvents,
5316
- assistantMessageCount,
5317
- assistantGroupCount,
5318
- assistantGroupsWithRenderableToolCalls,
5319
- messagesWithToolCalls: messagesWithToolCallsCount,
5320
- totalToolCallCount,
5321
- incompleteToolCallCount,
5322
- recentToolUiEvents,
5323
- };
5324
- }, [
5325
- assistantGroupCount,
5326
- assistantGroupsWithRenderableToolCalls,
5327
- assistantMessageCount,
5328
- currentAgentId,
5329
- hasActiveStreaming,
5330
- incompleteToolCallCount,
5331
- isAgentThinking,
5332
- isConnecting,
5333
- isExecuting,
5334
- isSubmitting,
5335
- isWaitingForResponse,
5336
- messagesWithToolCallsCount,
5337
- recentAgentRunEvents,
5338
- recentToolUiEvents,
5339
- totalToolCallCount,
5340
- ]);
5341
- const showInitialThinkingSplash = messages.length === 0 &&
5342
- !error &&
5343
- hideGreeting &&
5344
- (isSubmitting || isConnecting);
5345
3822
  // Compute dots visibility: only show BEFORE any assistant message exists
5346
3823
  // This prevents duplicate headers - the dots indicator has its own header,
5347
3824
  // and we don't want to show a second header below existing messages
@@ -5356,20 +3833,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5356
3833
  // The message with the pending approval will display its own UI for approval
5357
3834
  if (allPendingApprovals.length > 0)
5358
3835
  return false;
5359
- // The hidden-greeting startup splash already renders its own bouncing dots.
5360
- // Suppress the generic indicator so reopening/running terminals don't show two.
5361
- if (showInitialThinkingSplash)
5362
- return false;
5363
3836
  // IMPORTANT: If the last message is an assistant message and we're still executing,
5364
3837
  // the AiResponseMessage for that message will show its own activity indicator.
5365
3838
  // We only want these global thinking dots if the last message was from the user
5366
3839
  // or if no messages exist yet (waiting for initial response).
5367
3840
  const lastMessage = messages.length > 0 ? messages[messages.length - 1] : null;
5368
- if (isExecuting &&
5369
- lastMessage?.role === "assistant" &&
5370
- !lastMessage.isCompleted) {
3841
+ if (isExecuting && lastMessage?.role === "assistant")
5371
3842
  return false;
5372
- }
5373
3843
  // Existing check for uncompleted assistant messages
5374
3844
  const hasActiveStreamingMessage = messages.some((m) => !m.isCompleted && m.role === "assistant");
5375
3845
  if (hasActiveStreamingMessage)
@@ -5385,22 +3855,20 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5385
3855
  messages,
5386
3856
  activeInlineDialog,
5387
3857
  allPendingApprovals,
5388
- showInitialThinkingSplash,
5389
3858
  ]);
5390
3859
  // Move useMemo hook before early return to comply with Rules of Hooks
5391
- const resolvedEditorContextMode = React.useMemo(() => {
3860
+ const isLiveEditorContextMode = React.useMemo(() => {
5392
3861
  try {
5393
3862
  const normalizedAgentProfileId = agent?.profileId?.toLowerCase();
5394
3863
  const profile = activeProfile ||
5395
3864
  profiles.find((p) => p.id?.toLowerCase() === normalizedAgentProfileId);
5396
- return profile?.editorContextMode ?? null;
3865
+ const mode = profile?.editorContextMode;
3866
+ return mode === "live";
5397
3867
  }
5398
3868
  catch {
5399
- return null;
3869
+ return false;
5400
3870
  }
5401
3871
  }, [activeProfile, profiles, agent?.profileId]);
5402
- const isLiveEditorContextMode = resolvedEditorContextMode === "live";
5403
- const omitsEditorContext = resolvedEditorContextMode === "none";
5404
3872
  // Get parent agent ID from agent or agentStub (handle both camelCase and PascalCase)
5405
3873
  const parentAgentId = agent?.parentAgentId ||
5406
3874
  agent?.ParentAgentId ||
@@ -5414,8 +3882,10 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5414
3882
  detail: { agentId: parentAgentId },
5415
3883
  }));
5416
3884
  }, [parentAgentId]);
5417
- 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;
5418
- 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 }));
5419
3889
  const renderCostLimitBanner = () => {
5420
3890
  if (!costLimitExceeded)
5421
3891
  return null;
@@ -5426,20 +3896,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5426
3896
  try {
5427
3897
  // Extend cost limit - backend will automatically resume the agent
5428
3898
  const result = await updateAgentCostLimit(agent.id, "extend");
5429
- // Update the agent's cost limit and clear the costLimitReached
5430
- // status in local state so the useEffect watcher doesn't
5431
- // immediately re-show the banner before the backend status
5432
- // update arrives.
3899
+ // Update the agent's cost limit in local state
5433
3900
  if (result.success && result.costLimit !== undefined) {
5434
- setAgent((prev) => prev
5435
- ? {
5436
- ...prev,
5437
- costLimit: result.costLimit,
5438
- status: prev.status === "costLimitReached"
5439
- ? "running"
5440
- : prev.status,
5441
- }
5442
- : prev);
3901
+ setAgent((prev) => prev ? { ...prev, costLimit: result.costLimit } : prev);
5443
3902
  }
5444
3903
  // Clear the banner and set waiting state
5445
3904
  // Agent will resume automatically via backend's ResumeAgentAsync
@@ -5459,257 +3918,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5459
3918
  };
5460
3919
  const renderErrorBanner = () => {
5461
3920
  const currentAgent = agent || agentStub;
5462
- const isErrorStatus = currentAgent?.status === "error";
5463
- const isWaitingForInputStatus = currentAgent?.status === "waitingForInput";
5464
- const isWaitingForApprovalStatus = currentAgent?.status === "waitingForApproval";
5465
- // Show error banner for error status, or for any terminal status that still
5466
- // carries a statusMessage (e.g. agent closed after an error).
5467
- const isTerminalWithError = !isErrorStatus &&
5468
- !!currentAgent?.statusMessage &&
5469
- currentAgent?.status !== "running" &&
5470
- currentAgent?.status !== "new" &&
5471
- !isWaitingForInputStatus &&
5472
- !isWaitingForApprovalStatus;
5473
- const rawErrorMessage = (isErrorStatus || isTerminalWithError
5474
- ? currentAgent?.statusMessage
5475
- : null) || error;
5476
- if (!rawErrorMessage)
3921
+ const isErrorStatus = currentAgent?.status === "error" || currentAgent?.status === 4;
3922
+ const errorMessage = currentAgent?.statusMessage;
3923
+ if (!isErrorStatus || !errorMessage)
5477
3924
  return null;
5478
- // Clean the error message (statusMessage from DB may contain raw JSON)
5479
- const errorMessage = toUserFacingAgentErrorMessage(rawErrorMessage) || rawErrorMessage;
5480
- 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 })] })] }) }));
5481
3926
  };
5482
- const renderBrowserClaimBanner = (variant = "inline") => {
5483
- if (!agent?.id || !editContext?.sessionId)
5484
- return null;
5485
- if (!isClaimedByCurrentSession && !isClaimedByAnotherBrowser) {
5486
- return null;
5487
- }
5488
- if (isPendingBrowserCaptureWait) {
5489
- return null;
5490
- }
5491
- const label = isClaimedByCurrentSession
5492
- ? "Attached to this browser"
5493
- : isClaimedByAnotherBrowser
5494
- ? "Attached in another browser"
5495
- : "No browser attached";
5496
- const description = isClaimedByCurrentSession
5497
- ? "This browser will handle page screenshot and DOM capture requests for the agent."
5498
- : isClaimedByAnotherBrowser
5499
- ? "Capture requests will stay with the other browser until you take over control here."
5500
- : "A page capture request is waiting for a browser attachment. Attach this browser to continue.";
5501
- 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");
5502
- 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: () => {
5503
- void handleReleaseBrowser();
5504
- }, 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: () => {
5505
- void handleClaimBrowser(isClaimedByAnotherBrowser);
5506
- }, children: isClaimedByAnotherBrowser
5507
- ? "Take over browser control"
5508
- : "Attach to this browser" })) })] }) }));
5509
- };
5510
- const fixedBrowserClaimBanner = renderBrowserClaimBanner("fixed");
5511
- const inlineBrowserClaimBanner = null;
5512
- const browserCaptureInlinePrompt = isPendingBrowserCaptureWait && !isClaimedByCurrentSession
5513
- ? {
5514
- toolNames: [
5515
- "capture-page-screenshot",
5516
- "capture-parhelia-ui-screenshot",
5517
- "capture-page-dom",
5518
- ],
5519
- label: isClaimedByAnotherBrowser
5520
- ? "Attached in another browser"
5521
- : "No browser attached",
5522
- description: isClaimedByAnotherBrowser
5523
- ? "This capture request is waiting in another browser. Take over browser control here to continue."
5524
- : "This capture request is waiting for a browser attachment. Attach this browser to continue.",
5525
- actionLabel: isClaimedByAnotherBrowser
5526
- ? "Take over browser control"
5527
- : "Attach to this browser",
5528
- isPending: isBrowserClaimMutationPending,
5529
- onAction: () => {
5530
- void handleClaimBrowser(isClaimedByAnotherBrowser);
5531
- },
5532
- }
5533
- : null;
5534
- useEffect(() => {
5535
- if (agent?.status !== "waitingForInput") {
5536
- setPendingBrowserCaptureDialogType(null);
5537
- }
5538
- }, [agent?.status]);
5539
- const renderInlineDialogContent = () => {
5540
- if (!activeInlineDialog)
5541
- return null;
5542
- if (activeInlineDialog.request.dialogType === "questionnaire") {
5543
- 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) => {
5544
- activeInlineDialog.onComplete(result);
5545
- setActiveInlineDialog(null);
5546
- void onInteractionSubmitted?.();
5547
- }, onCancel: () => {
5548
- activeInlineDialog.onCancel();
5549
- setActiveInlineDialog(null);
5550
- } }) }));
5551
- }
5552
- const dialogRegistration = editContext?.configuration?.editor?.agentDialogs?.find((d) => d.dialogType === activeInlineDialog.request.dialogType);
5553
- if (dialogRegistration) {
5554
- const DialogComponent = dialogRegistration.component;
5555
- return (_jsx("div", { className: "agent-inline-dialog", children: _jsx(DialogComponent, { title: activeInlineDialog.request.title, description: activeInlineDialog.request.description, parameters: activeInlineDialog.request.parameters, onClose: (result) => {
5556
- activeInlineDialog.onComplete(result);
5557
- setActiveInlineDialog(null);
5558
- if (activeInlineDialog.request.dialogType === "questionnaire") {
5559
- void onInteractionSubmitted?.();
5560
- }
5561
- }, onCancel: () => {
5562
- activeInlineDialog.onCancel();
5563
- setActiveInlineDialog(null);
5564
- } }) }));
5565
- }
5566
- 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] }) }));
5567
- };
5568
- const latestSummaryAssistantGroup = useMemo(() => {
5569
- if (hideSummaryMessages)
5570
- return null;
5571
- const groups = groupConsecutiveMessages(messages);
5572
- for (let groupIndex = groups.length - 1; groupIndex >= 0; groupIndex -= 1) {
5573
- const group = groups[groupIndex];
5574
- if (!group || group.type !== "assistant-group")
5575
- continue;
5576
- const filteredMessages = group.messages.filter((msg) => {
5577
- const content = msg.content || "";
5578
- return !content.startsWith("⚠️") || !content.includes("Cost limit");
5579
- });
5580
- if (filteredMessages.length === 0)
5581
- continue;
5582
- return {
5583
- messages: filteredMessages,
5584
- isLastGroup: groupIndex === groups.length - 1,
5585
- };
5586
- }
5587
- return null;
5588
- }, [messages, hideSummaryMessages]);
5589
- const summaryModeContent = displayMode === "summary"
5590
- ? (() => {
5591
- const inlineDialog = renderInlineDialogContent();
5592
- const summaryMessages = latestSummaryAssistantGroup
5593
- ? convertAgentMessagesToAiFormat(latestSummaryAssistantGroup.messages)
5594
- : [];
5595
- const summaryOperations = latestSummaryAssistantGroup
5596
- ? getOperationsForMessageGroup(summaryMessages, agentOperations)
5597
- : [];
5598
- 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 &&
5599
- !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: {
5600
- __html: sanitizeSvg(activeProfile.svgIcon),
5601
- } })) : (_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 ||
5602
- activeProfile?.displayTitle ||
5603
- activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, browserCaptureInlinePrompt: browserCaptureInlinePrompt, onQuickAction: (action) => {
5604
- const text = (action.prompt ||
5605
- action.value ||
5606
- action.label ||
5607
- "").trim();
5608
- if (!text)
5609
- return;
5610
- if (isExecuting) {
5611
- try {
5612
- handleStop();
5613
- }
5614
- catch { }
5615
- }
5616
- sendQuickMessage(text);
5617
- } }) })) : 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
5618
- ? "The agent is still working. The next update will appear here automatically."
5619
- : agent?.statusMessage ||
5620
- summaryPlaceholderMessage ||
5621
- "Waiting for the next agent update." }), summaryPlaceholderActions ? (_jsx("div", { className: `flex justify-center ${compact ? "mt-2" : "mt-3"}`, children: summaryPlaceholderActions })) : null] }) })), displayMode !== "summary" &&
5622
- shouldShowThinkingDots &&
5623
- !inlineDialog &&
5624
- !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: {
5625
- __html: sanitizeSvg(activeProfile.svgIcon),
5626
- } })) : (_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 ||
5627
- activeProfile?.displayTitle ||
5628
- activeProfile?.name ||
5629
- "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) => {
5630
- setActivePlaceholderInput(null);
5631
- setAllPlaceholdersFilled(false);
5632
- if (activePlaceholderInput.behavior === "compose" &&
5633
- !hideBottomControls) {
5634
- setPrompt(filledText);
5635
- setInputPlaceholder("Review and edit, then press Enter to send");
5636
- if (textareaRef.current) {
5637
- try {
5638
- textareaRef.current.focus();
5639
- const v = textareaRef.current.value || "";
5640
- textareaRef.current.selectionStart = v.length;
5641
- textareaRef.current.selectionEnd = v.length;
5642
- }
5643
- catch { }
5644
- }
5645
- }
5646
- else {
5647
- if (isExecuting) {
5648
- try {
5649
- handleStop();
5650
- }
5651
- catch { }
5652
- }
5653
- sendQuickMessage(filledText);
5654
- }
5655
- }, onCancel: () => {
5656
- setActivePlaceholderInput(null);
5657
- setAllPlaceholdersFilled(false);
5658
- } })) : prompt && /\{\{[^{}]+\}\}|<<[^<>]+>>/.test(prompt) ? (_jsx(PlaceholderInput, { ref: promptPlaceholderInputRef, text: prompt, showButtons: hideBottomControls, buttonsClassName: hideBottomControls ? "justify-end" : "", onFilledChange: setAllPlaceholdersFilled, onComplete: (filledText) => {
5659
- setPrompt(filledText);
5660
- setAllPlaceholdersFilled(false);
5661
- if (filledText.trim()) {
5662
- if (isExecuting) {
5663
- try {
5664
- handleStop();
5665
- }
5666
- catch { }
5667
- }
5668
- sendQuickMessage(filledText);
5669
- }
5670
- }, onCancel: () => {
5671
- setPrompt("");
5672
- setAllPlaceholdersFilled(false);
5673
- setInputPlaceholder("Type your message... (Enter to send, Shift+Enter or Ctrl+Enter for new line)");
5674
- } })) : (_jsx("div", { className: "flex items-stretch gap-2", children: _jsx(Textarea, { ref: textareaRef, style: { viewTransitionName: "assistant-chat-input" }, value: prompt, onChange: (e) => {
5675
- setPrompt(e.target.value);
5676
- if (!/\{\{[^{}]+\}\}|<<[^<>]+>>/.test(e.target.value)) {
5677
- setAllPlaceholdersFilled(false);
5678
- }
5679
- if (currentHistoryIndex !== -1) {
5680
- setCurrentHistoryIndex(-1);
5681
- }
5682
- }, onKeyDown: handleKeyPress, onPaste: handlePaste, onFocus: () => {
5683
- shouldMaintainFocusRef.current = true;
5684
- }, onBlur: () => {
5685
- shouldMaintainFocusRef.current = false;
5686
- }, 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 }) })), (() => {
5687
- const isInPlaceholderMode = activePlaceholderInput ||
5688
- (prompt && /\{\{[^{}]+\}\}|<<[^<>]+>>/.test(prompt));
5689
- const placeholderShowsOwnButtons = hideBottomControls && isInPlaceholderMode;
5690
- if (placeholderShowsOwnButtons)
5691
- return null;
5692
- return (_jsxs("div", { className: cn("mt-2 flex items-stretch gap-2", hideBottomControls ||
5693
- simpleMode ||
5694
- isInPlaceholderMode
5695
- ? "justify-end"
5696
- : "justify-between"), children: [!hideBottomControls &&
5697
- !simpleMode &&
5698
- !isInPlaceholderMode ? (_jsx("div", { className: "flex-1" })) : null, _jsx(Button, { type: "button", size: "sm", onClick: () => {
5699
- if (isExecuting) {
5700
- handleStop();
5701
- }
5702
- else {
5703
- handleSubmit();
5704
- }
5705
- }, disabled: !isExecuting &&
5706
- !activePlaceholderInput &&
5707
- (!prompt.trim() || isSubmitting), "data-testid": "agent-send-stop-button", children: isExecuting ? "Stop" : "Send" })] }));
5708
- })()] })) : null] }));
5709
- })()
5710
- : null;
5711
- const fullModeInlineDialog = displayMode === "full" ? renderInlineDialogContent() : null;
5712
- 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) => {
5713
3928
  setPrompt(p);
5714
3929
  // Use setTimeout to ensure state is updated before submission
5715
3930
  setTimeout(() => {
@@ -5722,9 +3937,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5722
3937
  handleSubmit();
5723
3938
  }
5724
3939
  }, 0);
5725
- } })) })), 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: {
5726
- __html: sanitizeSvg(activeProfile.svgIcon),
5727
- } })) : (_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: [(() => {
5728
3946
  const groups = groupConsecutiveMessages(messages);
5729
3947
  return groups.map((group, groupIndex) => {
5730
3948
  const isLastGroup = groupIndex === groups.length - 1;
@@ -5732,9 +3950,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5732
3950
  // Render user message
5733
3951
  return (_jsx(UserMessage, { message: group.messages[0] }, groupIndex));
5734
3952
  }
5735
- else if (group.type === "heartbeat" && group.messages[0]) {
5736
- return (_jsx(HeartbeatMessage, { message: group.messages[0] }, group.messages[0].id || groupIndex));
5737
- }
5738
3953
  else {
5739
3954
  // Render bundled assistant messages
5740
3955
  // Check if this group contains any streaming message
@@ -5751,9 +3966,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5751
3966
  }
5752
3967
  const convertedMessages = convertAgentMessagesToAiFormat(filteredMessages);
5753
3968
  const operationsForGroup = getOperationsForMessageGroup(convertedMessages, agentOperations);
5754
- 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 ||
5755
3970
  activeProfile?.displayTitle ||
5756
- activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, browserCaptureInlinePrompt: browserCaptureInlinePrompt, onQuickAction: (action) => {
3971
+ activeProfile?.name, allPendingApprovals: allPendingApprovals, onSwitchToAutonomous: handleSwitchToAutonomous, onQuickAction: (action) => {
5757
3972
  const text = (action.prompt ||
5758
3973
  action.value ||
5759
3974
  action.label ||
@@ -5797,13 +4012,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5797
4012
  }
5798
4013
  });
5799
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: {
5800
- __html: sanitizeSvg(activeProfile.svgIcon),
4015
+ __html: activeProfile.svgIcon,
5801
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 ||
5802
4017
  activeProfile?.displayTitle ||
5803
4018
  activeProfile?.name ||
5804
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 &&
5805
4020
  !simpleMode &&
5806
- (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: [
5807
4022
  {
5808
4023
  id: "context",
5809
4024
  label: "Context",
@@ -5851,49 +4066,28 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5851
4066
  hasTodoContent,
5852
4067
  hasSpawnedAgents,
5853
4068
  agent?.id && hasHistoryContent,
5854
- ].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) => {
5855
- let triggerName = "";
5856
- if (qp.data) {
5857
- try {
5858
- const parsed = JSON.parse(qp.data);
5859
- triggerName = parsed?.triggerName?.trim() || "";
5860
- }
5861
- catch {
5862
- // Ignore invalid JSON metadata and render as regular queued prompt.
5863
- }
5864
- }
5865
- const isTriggerQueuedPrompt = !qp.sourceAgentName && triggerName.length > 0;
5866
- const isTriggerExpanded = !!expandedQueuedTriggerIds[qp.id];
5867
- if (isTriggerQueuedPrompt) {
5868
- return (_jsxs("div", { className: "text-[11px]", "data-testid": "queued-prompt-item", children: [_jsxs("button", { type: "button", onClick: () => setExpandedQueuedTriggerIds((prev) => ({
5869
- ...prev,
5870
- [qp.id]: !prev[qp.id],
5871
- })), 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));
5872
- }
5873
- 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 &&
5874
- new Date(qp.scheduledFor).getTime() >
5875
- new Date(qp.createdDate || 0).getTime() +
5876
- 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() ===
5877
- new Date().toDateString()
5878
- ? formatTime(new Date(qp.scheduledFor))
5879
- : formatDateTime(new Date(qp.scheduledFor))] })] }))] })] }) }) }, qp.id));
5880
- }) })] }) }))] }));
5881
- const showQuestionnaireSplitter = isQuestionnaireDialogOpen && !!fullModeInlineDialog;
5882
- const fullModeContent = showQuestionnaireSplitter ? (_jsx(Splitter, { panels: [
5883
- {
5884
- name: "conversation",
5885
- defaultSize: 65,
5886
- content: fullModeUpperContent,
5887
- },
5888
- {
5889
- name: "questionnaire",
5890
- defaultSize: 35,
5891
- content: fullModeInlineDialog,
5892
- },
5893
- ], direction: "vertical", localStorageKey: compact
5894
- ? "agent-terminal-compact-questionnaire-splitter"
5895
- : "agent-terminal-questionnaire-splitter", className: "min-h-0 flex-1", splitterClassName: "bg-gray-200 hover:bg-gray-300" })) : (_jsxs(_Fragment, { children: [fullModeUpperContent, fullModeInlineDialog] }));
5896
- 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 ? (
5897
4091
  // Placeholder Input (from quick actions)
5898
4092
  // Show internal buttons only in splash mode (hideBottomControls) since external buttons won't be visible there
5899
4093
  _jsx(PlaceholderInput, { ref: placeholderInputRef, text: activePlaceholderInput.text, showButtons: hideBottomControls, buttonsClassName: hideBottomControls ? "justify-end" : "", onFilledChange: setAllPlaceholdersFilled, onComplete: (filledText) => {
@@ -5970,221 +4164,122 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5970
4164
  return null;
5971
4165
  return (_jsxs("div", { className: cn("mt-2 flex items-stretch gap-2", hideBottomControls || simpleMode || isInPlaceholderMode
5972
4166
  ? "justify-end"
5973
- : "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"
5974
4168
  ? "border-green-300 bg-green-50! text-green-700 hover:bg-green-100!"
5975
4169
  : mode === "supervised"
5976
4170
  ? "border-amber-300 bg-amber-50! text-amber-700 hover:bg-amber-100!"
5977
4171
  : "border-red-300 bg-red-50! text-red-700 hover:bg-red-100!"), value: mode, options: modeOptions, onValueChange: async (val) => {
5978
4172
  const nextMode = val || "supervised";
4173
+ // Optimistic UI update
4174
+ setMode(nextMode);
5979
4175
  const current = agentMetadata || {};
5980
4176
  const nextMeta = {
5981
4177
  ...current,
5982
4178
  mode: nextMode,
5983
4179
  };
5984
4180
  try {
5985
- if (!agent?.id || isLocalOnlyDraftAgent) {
5986
- setMode(nextMode);
4181
+ if (!agent?.id || agent.status === "new") {
5987
4182
  setAgentMetadata(nextMeta);
4183
+ // Cache until first start when agent is persisted
5988
4184
  pendingSettingsRef.current = {
5989
4185
  ...(pendingSettingsRef.current || {}),
5990
4186
  mode: nextMode,
5991
4187
  };
5992
4188
  return;
5993
4189
  }
5994
- const result = await updateAgentSettings(agent.id, {
4190
+ await updateAgentSettings(agent.id, {
5995
4191
  mode: nextMode,
5996
4192
  });
5997
- if (result.success === false ||
5998
- result.updates?.mode === false) {
5999
- throw new Error("Mode change was not applied");
6000
- }
6001
- setMode(nextMode);
6002
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
6003
4231
  setAgent((prev) => prev
6004
4232
  ? {
6005
4233
  ...prev,
6006
- mode: nextMode,
6007
- 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
+ }),
6008
4244
  }
6009
4245
  : prev);
6010
4246
  }
6011
- catch (e2) {
6012
- 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
+ }
6013
4270
  }
6014
- } }), _jsxs(Popover, { open: showAgentSettings, onOpenChange: (open) => {
6015
- setShowAgentSettings(open);
6016
- if (!open) {
6017
- setShowSkillPicker(false);
4271
+ catch (err) {
4272
+ console.error("Failed to persist agent model", err);
6018
4273
  }
6019
- }, 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) => {
6020
- const target = e.target;
6021
- if (target?.closest('[data-help-panel="true"]')) {
6022
- e.preventDefault();
6023
- }
6024
- }, onPointerDownOutside: (e) => {
6025
- const target = e.target;
6026
- if (target?.closest('[data-help-panel="true"]')) {
6027
- e.preventDefault();
6028
- }
6029
- }, onFocusOutside: (e) => {
6030
- const target = e.target;
6031
- if (target?.closest('[data-help-panel="true"]')) {
6032
- e.preventDefault();
6033
- }
6034
- }, 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) => {
6035
- const nextProfile = profiles.find((x) => x.id === val);
6036
- if (!nextProfile)
6037
- return;
6038
- setActiveProfile(nextProfile);
6039
- try {
6040
- if (agent?.id && !isLocalOnlyDraftAgent) {
6041
- await updateAgentSettings(agent.id, {
6042
- profileId: nextProfile.id,
6043
- profileName: nextProfile.name,
6044
- });
6045
- }
6046
- else {
6047
- pendingSettingsRef.current = {
6048
- ...(pendingSettingsRef.current || {}),
6049
- profileId: nextProfile.id,
6050
- profileName: nextProfile.name,
6051
- };
6052
- setAgentMetadata((current) => {
6053
- const next = {
6054
- ...(current || {}),
6055
- };
6056
- next.profile = nextProfile.name;
6057
- next.additionalData = {
6058
- ...(next.additionalData || {}),
6059
- profileId: nextProfile.id,
6060
- profileName: nextProfile.name,
6061
- };
6062
- return next;
6063
- });
6064
- }
6065
- setAgent((prev) => prev
6066
- ? {
6067
- ...prev,
6068
- profileId: nextProfile.id,
6069
- profileName: nextProfile.name,
6070
- metadata: JSON.stringify({
6071
- ...(agentMetadata || {}),
6072
- profile: nextProfile.name,
6073
- additionalData: {
6074
- ...(agentMetadata
6075
- ?.additionalData || {}),
6076
- profileId: nextProfile.id,
6077
- profileName: nextProfile.name,
6078
- },
6079
- }),
6080
- }
6081
- : prev);
6082
- }
6083
- catch (err) {
6084
- console.error("Failed to persist agent profile", err);
6085
- }
6086
- } }), 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: () => {
6087
- void handleEditProfileSideBySide();
6088
- setShowAgentSettings(false);
6089
- }, "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: () => {
6090
- void handleOpenProfileSettings();
6091
- setShowAgentSettings(false);
6092
- }, 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) => {
6093
- const nextId = val;
6094
- setSelectedModelId(nextId);
6095
- const modelName = activeProfile?.models?.find((m) => m.id === nextId)?.name || "";
6096
- setAgent((prev) => prev ? { ...prev, model: modelName } : prev);
6097
- try {
6098
- if (agent?.id && !isLocalOnlyDraftAgent) {
6099
- await updateAgentSettings(agent.id, {
6100
- model: modelName,
6101
- });
6102
- }
6103
- else {
6104
- pendingSettingsRef.current = {
6105
- ...(pendingSettingsRef.current || {}),
6106
- modelName,
6107
- };
6108
- }
6109
- }
6110
- catch (err) {
6111
- console.error("Failed to persist agent model", err);
6112
- }
6113
- } })] })) : 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) => {
6114
- setShowSkillPicker(open);
6115
- if (open) {
6116
- setSkillActionError(null);
6117
- }
6118
- }, 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) => {
6119
- const selected = selection[0];
6120
- if (!selected?.id)
6121
- return;
6122
- setSkillActionError(null);
6123
- if (selectableTemplateIdSet.size > 0 &&
6124
- (!selected.templateId ||
6125
- !selectableTemplateIdSet.has(selected.templateId.toLowerCase()))) {
6126
- return;
6127
- }
6128
- if (!manuallyAssignableSkillIdSet.has(selected.id.toLowerCase())) {
6129
- setSkillActionError("This skill cannot be added for the current agent profile.");
6130
- return;
6131
- }
6132
- void (async () => {
6133
- const added = await handleAddSkill(selected.id);
6134
- if (added) {
6135
- setShowSkillPicker(false);
6136
- }
6137
- })();
6138
- } }), skillsLoading && (_jsx("div", { className: "bg-background/70 absolute inset-0 flex items-center justify-center text-[10px] text-gray-500", children: "Loading skills..." }))] }), !skillsLoading &&
6139
- !skillsError &&
6140
- 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 &&
6141
- !skillsError &&
6142
- skillRootIds.length === 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: "No skill roots available." })), !skillsLoading &&
6143
- !skillsError &&
6144
- profileFilteredSkills.length === 0 && (_jsx("div", { className: "text-[10px] text-gray-500", children: selectedSkillIds.length > 0
6145
- ? "All addable skills are selected"
6146
- : "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) => {
6147
- const skill = selectedSkills.find((s) => s.id === skillId);
6148
- 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: () => {
6149
- void handleOpenSkillItem(skillId);
6150
- }, 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: () => {
6151
- void handleRemoveSkill(skillId);
6152
- }, title: "Remove skill", "aria-label": `Remove ${skill?.name || skillId}`, children: _jsx(X, { className: "h-2.5 w-2.5", strokeWidth: 1 }) }))] }, skillId));
6153
- }) }))] }), _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
6154
- ? "No available tools for this profile and mode"
6155
- : "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) => {
6156
- const sourceLabel = formatAllowanceSource(allowance.source);
6157
- const pathLabel = "itemPath" in allowance
6158
- ? allowance.itemPath
6159
- : allowance.normalizedPath;
6160
- 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 ||
6161
- "*" }), _jsx("div", { className: "truncate text-[9px] text-gray-500", title: formatAllowanceLabel(allowance), children: pathLabel })] }), (sourceLabel ||
6162
- allowance.grantedBy) && (_jsx("div", { className: "truncate pl-6 text-[9px] text-gray-400", children: [
6163
- sourceLabel,
6164
- allowance.grantedBy,
6165
- ]
6166
- .filter(Boolean)
6167
- .join(" · ") }))] }, `${group.key}-${allowance.operationType}-${pathLabel}-${index}`));
6168
- })] }, group.key)) : null) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isLocalOnlyDraftAgent
6169
- ? "Allowances are shown after the agent is created"
6170
- : "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) => {
6171
- const filterText = (sub.filter || "").trim();
6172
- 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));
6173
- }) })) : (_jsx("div", { className: "px-1 text-[10px] text-gray-500", children: isLocalOnlyDraftAgent
6174
- ? "Subscribed triggers are shown after the agent is created"
6175
- : "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: () => {
6176
4275
  setPrompt(p.prompt);
6177
4276
  setShowPredefined(false);
6178
4277
  if (textareaRef.current)
6179
4278
  textareaRef.current.focus();
6180
- }, children: p.title }, index))) }) })] })) : null, !hideBottomControls &&
6181
- !simpleMode &&
6182
- editContext?.isMobile && (_jsxs(Popover, { open: showCostAndAgent, onOpenChange: setShowCostAndAgent, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { onClick: () => {
6183
- 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)
6184
4281
  setShowCostAndAgent((prev) => !prev);
6185
- }, variant: "outline", size: "sm", className: "h-5.5 w-5.5 cursor-pointer rounded-full", "aria-expanded": editContext?.isMobile
6186
- ? showCostAndAgent
6187
- : 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
6188
4283
  ? {
6189
4284
  input: liveTotals.input,
6190
4285
  output: liveTotals.output,
@@ -6194,10 +4289,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
6194
4289
  outputCost: liveTotals.outputCost,
6195
4290
  cachedCost: liveTotals.cachedCost,
6196
4291
  cacheWriteCost: liveTotals.cacheWriteCost ?? 0,
6197
- imageCost: liveTotals.imageCost ?? 0,
6198
4292
  totalCost: liveTotals.totalCost,
6199
4293
  }
6200
- : 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
6201
4295
  ? "Your browser does not support Speech Recognition"
6202
4296
  : isListening
6203
4297
  ? "Stop voice input"
@@ -6218,10 +4312,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
6218
4312
  : allPendingApprovals.length > 0
6219
4313
  ? "Approve or reject pending tool calls first"
6220
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 })) })] })] }));
6221
- })(), !hideBottomControls &&
6222
- !simpleMode &&
6223
- editContext &&
6224
- !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
6225
4316
  ? {
6226
4317
  input: liveTotals.input,
6227
4318
  output: liveTotals.output,
@@ -6231,9 +4322,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
6231
4322
  outputCost: liveTotals.outputCost,
6232
4323
  cachedCost: liveTotals.cachedCost,
6233
4324
  cacheWriteCost: liveTotals.cacheWriteCost ?? 0,
6234
- imageCost: liveTotals.imageCost ?? 0,
6235
4325
  totalCost: liveTotals.totalCost,
6236
4326
  }
6237
- : totalTokens, effectiveCostLimit: effectiveCostLimit, messages: messages, showCompressionPopover: showCompressionPopover, setShowCompressionPopover: setShowCompressionPopover }))] })] }));
4327
+ : totalTokens, effectiveCostLimit: effectiveCostLimit, messages: messages, activeProfile: activeProfile, showCompressionPopover: showCompressionPopover, setShowCompressionPopover: setShowCompressionPopover }))] })] }));
6238
4328
  }
6239
4329
  //# sourceMappingURL=AgentTerminal.js.map