@datalayer/agent-runtimes 1.0.2 → 1.0.3

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 (615) hide show
  1. package/README.md +1 -1
  2. package/lib/Agent.js +2 -2
  3. package/lib/AgentLexical.d.ts +1 -12
  4. package/lib/AgentLexical.js +3 -14
  5. package/lib/AgentNotebook.js +3 -3
  6. package/lib/App.js +2 -2
  7. package/lib/{components/chat/components → agents}/AgentDetails.d.ts +13 -2
  8. package/lib/{components/chat/components → agents}/AgentDetails.js +6 -12
  9. package/lib/api/agents.d.ts +47 -0
  10. package/lib/api/agents.js +106 -0
  11. package/lib/api/context.d.ts +17 -0
  12. package/lib/api/context.js +45 -0
  13. package/lib/api/evals.d.ts +27 -0
  14. package/lib/api/evals.js +63 -0
  15. package/lib/api/events.d.ts +17 -0
  16. package/lib/api/events.js +93 -0
  17. package/lib/{components/chat → api}/handler.js +1 -1
  18. package/lib/api/index.d.ts +15 -0
  19. package/lib/api/index.js +20 -0
  20. package/lib/api/notifications.d.ts +39 -0
  21. package/lib/api/notifications.js +103 -0
  22. package/lib/api/output.d.ts +28 -0
  23. package/lib/api/output.js +64 -0
  24. package/lib/api/tool-approvals.d.ts +62 -0
  25. package/lib/api/tool-approvals.js +145 -0
  26. package/lib/{components/chat/components → chat}/Chat.d.ts +24 -15
  27. package/lib/{components/chat/components → chat}/Chat.js +16 -16
  28. package/lib/{components/chat/components → chat}/ChatFloating.d.ts +17 -9
  29. package/lib/{components/chat/components → chat}/ChatFloating.js +17 -9
  30. package/lib/{components/chat/components → chat}/ChatInline.d.ts +2 -2
  31. package/lib/{components/chat/components → chat}/ChatInline.js +8 -6
  32. package/lib/{components/chat/components → chat}/ChatPopupStandalone.d.ts +3 -4
  33. package/lib/{components/chat/components → chat}/ChatPopupStandalone.js +6 -6
  34. package/lib/{components/chat/components → chat}/ChatSidebar.d.ts +14 -5
  35. package/lib/{components/chat/components → chat}/ChatSidebar.js +48 -26
  36. package/lib/{components/chat/components → chat}/ChatStandalone.d.ts +3 -4
  37. package/lib/{components/chat/components → chat}/ChatStandalone.js +5 -5
  38. package/lib/chat/base/ChatBase.d.ts +6 -0
  39. package/lib/chat/base/ChatBase.js +1287 -0
  40. package/lib/chat/display/EmptyState.d.ts +27 -0
  41. package/lib/chat/display/EmptyState.js +41 -0
  42. package/lib/{components/chat/components/elements → chat/display}/FloatingBrandButton.d.ts +1 -1
  43. package/lib/{components/chat/components/elements → chat/display}/FloatingBrandButton.js +1 -1
  44. package/lib/{components/chat/components/elements → chat/display}/PoweredByTag.d.ts +1 -1
  45. package/lib/{components/chat/components/elements → chat/header}/ChatHeader.d.ts +1 -4
  46. package/lib/{components/chat/components/elements → chat/header}/ChatHeader.js +1 -1
  47. package/lib/chat/header/ChatHeaderBase.d.ts +38 -0
  48. package/lib/chat/header/ChatHeaderBase.js +83 -0
  49. package/lib/chat/index.d.ts +66 -0
  50. package/lib/chat/index.js +74 -0
  51. package/lib/chat/indicators/McpStatusIndicator.d.ts +9 -0
  52. package/lib/chat/indicators/McpStatusIndicator.js +128 -0
  53. package/lib/chat/indicators/SandboxStatusIndicator.d.ts +10 -0
  54. package/lib/chat/indicators/SandboxStatusIndicator.js +175 -0
  55. package/lib/chat/indicators/index.d.ts +17 -0
  56. package/lib/chat/indicators/index.js +19 -0
  57. package/lib/chat/messages/ChatMessageList.d.ts +49 -0
  58. package/lib/chat/messages/ChatMessageList.js +319 -0
  59. package/lib/{components/chat/components/elements → chat/messages}/ChatMessages.d.ts +2 -2
  60. package/lib/{components/chat/components/elements → chat/messages}/ChatMessages.js +6 -4
  61. package/lib/{components/chat/components → chat}/parts/DynamicToolPart.d.ts +1 -1
  62. package/lib/{components/chat/components/elements → chat/parts}/MessagePart.d.ts +1 -1
  63. package/lib/{components/chat/components/elements → chat/parts}/MessagePart.js +4 -4
  64. package/lib/{components/chat/components → chat}/parts/ReasoningPart.js +1 -1
  65. package/lib/{components/chat/components → chat}/parts/TextPart.d.ts +1 -1
  66. package/lib/{components/chat/components → chat}/parts/ToolPart.js +1 -1
  67. package/lib/{components/chat/components → chat}/parts/index.d.ts +2 -1
  68. package/lib/{components/chat/components → chat}/parts/index.js +2 -1
  69. package/lib/chat/prompt/InputFooter.d.ts +43 -0
  70. package/lib/chat/prompt/InputFooter.js +135 -0
  71. package/lib/chat/prompt/InputPrompt.d.ts +60 -0
  72. package/lib/chat/prompt/InputPrompt.js +83 -0
  73. package/lib/chat/prompt/InputPromptFooter.d.ts +25 -0
  74. package/lib/chat/prompt/InputPromptFooter.js +15 -0
  75. package/lib/chat/prompt/InputPromptHeader.d.ts +15 -0
  76. package/lib/chat/prompt/InputPromptHeader.js +15 -0
  77. package/lib/chat/prompt/InputPromptLexical.d.ts +16 -0
  78. package/lib/chat/prompt/InputPromptLexical.js +122 -0
  79. package/lib/chat/prompt/InputPromptText.d.ts +24 -0
  80. package/lib/chat/prompt/InputPromptText.js +66 -0
  81. package/lib/chat/prompt/index.d.ts +11 -0
  82. package/lib/chat/prompt/index.js +15 -0
  83. package/lib/{components/chat/components → chat}/styles/streamdownStyles.d.ts +1 -1
  84. package/lib/chat/tools/ToolApprovalBanner.d.ts +31 -0
  85. package/lib/chat/tools/ToolApprovalBanner.js +62 -0
  86. package/lib/{components/chat/components/elements → chat/tools}/ToolApprovalDialog.js +1 -1
  87. package/lib/{components/chat/components/display → chat/tools}/ToolCallDisplay.d.ts +13 -4
  88. package/lib/{components/chat/components/display → chat/tools}/ToolCallDisplay.js +18 -8
  89. package/lib/chat/tools/index.d.ts +8 -0
  90. package/lib/{components/chat/components/display → chat/tools}/index.js +3 -1
  91. package/lib/chat/usage/TokenUsageBar.d.ts +8 -0
  92. package/lib/chat/usage/TokenUsageBar.js +213 -0
  93. package/lib/client/AgentsMixin.d.ts +169 -0
  94. package/lib/client/AgentsMixin.js +279 -0
  95. package/lib/client/index.d.ts +6 -0
  96. package/lib/client/index.js +10 -0
  97. package/lib/components/NotificationEventCard.d.ts +8 -0
  98. package/lib/components/NotificationEventCard.js +152 -0
  99. package/lib/components/OutputCard.d.ts +8 -0
  100. package/lib/components/OutputCard.js +80 -0
  101. package/lib/components/ToolApprovalCard.d.ts +33 -0
  102. package/lib/components/ToolApprovalCard.js +60 -0
  103. package/lib/components/index.d.ts +3 -7
  104. package/lib/components/index.js +3 -4
  105. package/lib/{components → config}/AgentConfiguration.d.ts +43 -21
  106. package/lib/{components → config}/AgentConfiguration.js +48 -28
  107. package/lib/config/index.d.ts +2 -0
  108. package/lib/config/index.js +1 -0
  109. package/lib/{components/chat/components → context}/ContextInspector.js +38 -8
  110. package/lib/{components/chat/components → context}/ContextPanel.js +27 -6
  111. package/lib/context/CostTracker.d.ts +37 -0
  112. package/lib/context/CostTracker.js +124 -0
  113. package/lib/context/TokenUsageChart.d.ts +10 -0
  114. package/lib/context/TokenUsageChart.js +288 -0
  115. package/lib/examples/A2UiComponentGalleryExample.d.ts +20 -0
  116. package/lib/examples/A2UiComponentGalleryExample.js +568 -0
  117. package/lib/examples/A2UiContactCardExample.d.ts +21 -0
  118. package/lib/examples/A2UiContactCardExample.js +432 -0
  119. package/lib/examples/A2UiRestaurantExample.d.ts +11 -3
  120. package/lib/examples/A2UiRestaurantExample.js +63 -98
  121. package/lib/examples/A2UiViewerExample.d.ts +21 -0
  122. package/lib/examples/A2UiViewerExample.js +563 -0
  123. package/lib/examples/AgUiAgenticExample.js +3 -3
  124. package/lib/examples/AgUiBackendToolRenderingExample.js +3 -3
  125. package/lib/examples/{AgUiHaikuGenUIExample.d.ts → AgUiHaikuGenUiExample.d.ts} +4 -4
  126. package/lib/examples/{AgUiHaikuGenUIExample.js → AgUiHaikuGenUiExample.js} +7 -7
  127. package/lib/examples/AgUiHumanInTheLoopExample.js +3 -3
  128. package/lib/examples/AgUiSharedStateExample.js +3 -3
  129. package/lib/examples/{AgUiToolsBasedGenUIExample.d.ts → AgUiToolsBasedGenUiExample.d.ts} +4 -4
  130. package/lib/examples/{AgUiToolsBasedGenUIExample.js → AgUiToolsBasedGenUiExample.js} +7 -7
  131. package/lib/examples/AgentCheckpointsExample.d.ts +19 -0
  132. package/lib/examples/AgentCheckpointsExample.js +506 -0
  133. package/lib/examples/AgentCodemodeExample.d.ts +14 -0
  134. package/lib/examples/AgentCodemodeExample.js +262 -0
  135. package/lib/examples/AgentEvalsExample.d.ts +14 -0
  136. package/lib/examples/AgentEvalsExample.js +216 -0
  137. package/lib/examples/AgentGuardrailsExample.d.ts +14 -0
  138. package/lib/examples/AgentGuardrailsExample.js +218 -0
  139. package/lib/examples/AgentMemoryExample.d.ts +14 -0
  140. package/lib/examples/AgentMemoryExample.js +234 -0
  141. package/lib/examples/AgentMonitoringExample.d.ts +13 -0
  142. package/lib/examples/AgentMonitoringExample.js +311 -0
  143. package/lib/examples/AgentNotificationsExample.d.ts +14 -0
  144. package/lib/examples/AgentNotificationsExample.js +273 -0
  145. package/lib/examples/AgentOtelExample.d.ts +25 -0
  146. package/lib/examples/AgentOtelExample.js +281 -0
  147. package/lib/examples/AgentOutputsExample.d.ts +14 -0
  148. package/lib/examples/AgentOutputsExample.js +211 -0
  149. package/lib/examples/AgentSandboxExample.d.ts +17 -0
  150. package/lib/examples/AgentSandboxExample.js +496 -0
  151. package/lib/examples/AgentSkillsExample.d.ts +3 -0
  152. package/lib/examples/AgentSkillsExample.js +290 -0
  153. package/lib/examples/AgentToolApprovalsExample.d.ts +3 -0
  154. package/lib/examples/AgentToolApprovalsExample.js +672 -0
  155. package/lib/examples/AgentTriggersExample.d.ts +14 -0
  156. package/lib/examples/AgentTriggersExample.js +523 -0
  157. package/lib/examples/{AgentRuntimeFormExample.d.ts → AgentspecExample.d.ts} +3 -4
  158. package/lib/examples/{AgentRuntimeFormExample.js → AgentspecExample.js} +92 -34
  159. package/lib/examples/{JupyterCellExample.js → CellSimpleExample.js} +1 -1
  160. package/lib/examples/{AgentRuntimeCustomExample.js → ChatCustomExample.js} +3 -3
  161. package/lib/examples/{AgentRuntimeChatExample.js → ChatExample.js} +4 -4
  162. package/lib/examples/{AgentRuntimeStandaloneExample.js → ChatStandaloneExample.js} +2 -2
  163. package/lib/examples/CopilotKitLexicalExample.d.ts +3 -14
  164. package/lib/examples/CopilotKitLexicalExample.js +4 -15
  165. package/lib/examples/CopilotKitNotebookExample.js +4 -6
  166. package/lib/examples/DatalayerNotebookExample.js +2 -2
  167. package/lib/examples/{AgentRuntimeLexical2Example.d.ts → Lexical2Example.d.ts} +2 -13
  168. package/lib/examples/{AgentRuntimeLexical2Example.js → Lexical2Example.js} +8 -17
  169. package/lib/examples/{AgentRuntimeLexicalExample.d.ts → LexicalExample.d.ts} +1 -12
  170. package/lib/examples/{AgentRuntimeLexicalExample.js → LexicalExample.js} +28 -27
  171. package/lib/examples/{AgentRuntimeLexicalSidebarExample.d.ts → LexicalSidebarExample.d.ts} +2 -13
  172. package/lib/examples/{AgentRuntimeLexicalSidebarExample.js → LexicalSidebarExample.js} +6 -18
  173. package/lib/examples/{AgentRuntimeNotebookExample.js → NotebookExample.js} +14 -10
  174. package/lib/examples/{AgentRuntimeNotebookSidebarExample.js → NotebookSidebarExample.js} +5 -8
  175. package/lib/examples/{JupyterNotebookExample.js → NotebookSimpleExample.js} +2 -2
  176. package/lib/examples/ag-ui/weather/InlineWeatherCard.js +1 -1
  177. package/lib/examples/components/ExampleWrapper.d.ts +12 -0
  178. package/lib/examples/components/ExampleWrapper.js +16 -0
  179. package/lib/examples/components/Header.d.ts +2 -2
  180. package/lib/examples/components/HeaderControls.js +1 -1
  181. package/lib/examples/components/LexicalEditor.d.ts +1 -12
  182. package/lib/examples/components/LexicalEditor.js +1 -12
  183. package/lib/examples/components/MainContent.d.ts +4 -11
  184. package/lib/examples/components/MainContent.js +6 -60
  185. package/lib/examples/components/index.d.ts +1 -0
  186. package/lib/examples/components/index.js +1 -0
  187. package/lib/examples/example-selector.d.ts +1 -1
  188. package/lib/examples/example-selector.js +35 -22
  189. package/lib/examples/index.d.ts +26 -13
  190. package/lib/examples/index.js +26 -12
  191. package/lib/examples/main.d.ts +6 -0
  192. package/lib/examples/main.js +20 -43
  193. package/lib/examples/utils/examplesStore.d.ts +4 -0
  194. package/lib/examples/{stores → utils}/examplesStore.js +1 -1
  195. package/lib/examples/utils/notebooks/Empty.ipynb.json +33 -0
  196. package/lib/examples/utils/notebooks/NotebookExample2.ipynb.json +48 -0
  197. package/lib/examples/utils/themeStore.d.ts +8 -0
  198. package/lib/examples/utils/themeStore.js +14 -0
  199. package/lib/extensions/A2UIExtension.d.ts +65 -0
  200. package/lib/extensions/A2UIExtension.js +202 -0
  201. package/lib/{components/chat/extensions → extensions}/ExtensionRegistry.d.ts +2 -3
  202. package/lib/{components/chat/extensions → extensions}/ExtensionRegistry.js +0 -2
  203. package/lib/{components/chat/extensions → extensions}/MCPUIExtension.d.ts +2 -2
  204. package/lib/{components/chat/extensions → extensions}/MCPUIExtension.js +1 -1
  205. package/lib/extensions/index.d.ts +9 -0
  206. package/lib/{components/chat/extensions → extensions}/index.js +2 -2
  207. package/lib/hooks/index.d.ts +33 -16
  208. package/lib/hooks/index.js +33 -16
  209. package/lib/hooks/useAIAgentsWebSocket.d.ts +29 -0
  210. package/lib/hooks/useAIAgentsWebSocket.js +136 -0
  211. package/lib/hooks/{useAGUI.d.ts → useAgUi.d.ts} +2 -2
  212. package/lib/hooks/{useAGUI.js → useAgUi.js} +2 -2
  213. package/lib/hooks/useAgents.d.ts +150 -11
  214. package/lib/hooks/useAgents.js +623 -61
  215. package/lib/hooks/{useAgentStore.d.ts → useAgentsCatalog.d.ts} +3 -8
  216. package/lib/hooks/{useAgentStore.js → useAgentsCatalog.js} +9 -3
  217. package/lib/hooks/useAgentsRegistry.d.ts +10 -0
  218. package/lib/hooks/useAgentsRegistry.js +20 -0
  219. package/lib/hooks/useAgentsService.d.ts +22 -0
  220. package/lib/hooks/useAgentsService.js +146 -0
  221. package/lib/hooks/useChat.d.ts +2 -2
  222. package/lib/hooks/useChat.js +14 -8
  223. package/lib/hooks/useCheckpoints.d.ts +176 -0
  224. package/lib/hooks/useCheckpoints.js +466 -0
  225. package/lib/hooks/useConfig.d.ts +11 -0
  226. package/lib/hooks/useConfig.js +46 -0
  227. package/lib/hooks/useContextSnapshot.d.ts +11 -0
  228. package/lib/hooks/useContextSnapshot.js +44 -0
  229. package/lib/hooks/useMonitoring.d.ts +24 -0
  230. package/lib/hooks/useMonitoring.js +111 -0
  231. package/lib/hooks/useNotifications.d.ts +67 -0
  232. package/lib/hooks/useNotifications.js +208 -0
  233. package/lib/hooks/useSandbox.d.ts +12 -0
  234. package/lib/hooks/useSandbox.js +49 -0
  235. package/lib/hooks/useSkills.d.ts +13 -0
  236. package/lib/hooks/useSkills.js +46 -0
  237. package/lib/hooks/useToolApprovals.d.ts +45 -0
  238. package/lib/hooks/useToolApprovals.js +126 -0
  239. package/lib/hooks/useTools.d.ts +4 -4
  240. package/lib/hooks/useTools.js +2 -2
  241. package/lib/hooks/{useVercelChat.d.ts → useVercelAI.d.ts} +3 -3
  242. package/lib/hooks/{useVercelChat.js → useVercelAI.js} +2 -2
  243. package/lib/{components/chat/components → identity}/AgentIdentity.d.ts +1 -1
  244. package/lib/{components/chat/components → identity}/AgentIdentity.js +4 -3
  245. package/lib/identity/index.d.ts +1 -0
  246. package/lib/identity/index.js +2 -0
  247. package/lib/index.d.ts +4 -3
  248. package/lib/index.js +3 -2
  249. package/lib/{components/chat/inference → inference}/BaseInferenceProvider.d.ts +3 -3
  250. package/lib/{components/chat/inference → inference}/DatalayerInferenceProvider.d.ts +3 -3
  251. package/lib/{components/chat/inference → inference}/DatalayerInferenceProvider.js +1 -1
  252. package/lib/{components/chat/inference → inference}/SelfHostedInferenceProvider.d.ts +2 -2
  253. package/lib/{components/chat/inference → inference}/SelfHostedInferenceProvider.js +1 -1
  254. package/lib/{components/chat/inference → inference}/index.d.ts +1 -1
  255. package/lib/{components/chat/inference → inference}/index.js +1 -1
  256. package/lib/lexical/ChatInlinePlugin.d.ts +1 -1
  257. package/lib/lexical/ChatInlinePlugin.js +1 -1
  258. package/lib/{components → mcp}/McpServerManager.d.ts +1 -2
  259. package/lib/mcp/index.d.ts +1 -0
  260. package/lib/{specs/agents/codeai → mcp}/index.js +1 -1
  261. package/lib/{components/chat/middleware → middleware}/MiddlewarePipeline.d.ts +3 -3
  262. package/lib/{components/chat/middleware → middleware}/index.d.ts +1 -1
  263. package/lib/{components/chat/middleware → middleware}/index.js +1 -1
  264. package/lib/{components/chat/protocols → protocols}/A2AAdapter.d.ts +6 -6
  265. package/lib/{components/chat/protocols → protocols}/A2AAdapter.js +3 -3
  266. package/lib/{components/chat/protocols → protocols}/ACPAdapter.d.ts +6 -6
  267. package/lib/{components/chat/protocols → protocols}/ACPAdapter.js +4 -4
  268. package/lib/{components/chat/protocols → protocols}/AGUIAdapter.d.ts +14 -6
  269. package/lib/{components/chat/protocols → protocols}/AGUIAdapter.js +72 -10
  270. package/lib/{components/chat/protocols → protocols}/BaseProtocolAdapter.d.ts +6 -6
  271. package/lib/{components/chat/protocols → protocols}/BaseProtocolAdapter.js +1 -1
  272. package/lib/{components/chat/protocols → protocols}/VercelAIAdapter.d.ts +31 -7
  273. package/lib/protocols/VercelAIAdapter.js +682 -0
  274. package/lib/{components/chat/protocols → protocols}/index.d.ts +1 -2
  275. package/lib/{components/chat/protocols → protocols}/index.js +1 -1
  276. package/lib/specs/agents/agents.d.ts +54 -0
  277. package/lib/specs/agents/{mocks/agents.js → agents.js} +1144 -799
  278. package/lib/specs/agents/index.js +14 -9
  279. package/lib/specs/envvars.d.ts +11 -19
  280. package/lib/specs/envvars.js +42 -21
  281. package/lib/specs/evals.d.ts +20 -0
  282. package/lib/specs/evals.js +133 -0
  283. package/lib/specs/events.d.ts +18 -0
  284. package/lib/specs/events.js +182 -0
  285. package/lib/specs/frontendTools.d.ts +14 -0
  286. package/lib/specs/frontendTools.js +53 -0
  287. package/lib/specs/guardrails.d.ts +22 -0
  288. package/lib/specs/guardrails.js +391 -0
  289. package/lib/specs/index.d.ts +15 -2
  290. package/lib/specs/index.js +15 -2
  291. package/lib/specs/mcpServers.d.ts +13 -11
  292. package/lib/specs/mcpServers.js +82 -28
  293. package/lib/specs/memory.d.ts +34 -0
  294. package/lib/specs/memory.js +99 -0
  295. package/lib/specs/models.d.ts +21 -34
  296. package/lib/specs/models.js +61 -41
  297. package/lib/specs/notifications.d.ts +17 -0
  298. package/lib/specs/notifications.js +187 -0
  299. package/lib/specs/outputs.d.ts +19 -0
  300. package/lib/specs/outputs.js +112 -0
  301. package/lib/specs/skills.d.ts +7 -16
  302. package/lib/specs/skills.js +89 -12
  303. package/lib/specs/teams/index.d.ts +17 -0
  304. package/lib/specs/teams/index.js +37 -0
  305. package/lib/specs/teams/teams.d.ts +27 -0
  306. package/lib/specs/teams/teams.js +1120 -0
  307. package/lib/specs/tools.d.ts +15 -0
  308. package/lib/specs/tools.js +83 -0
  309. package/lib/specs/triggers.d.ts +15 -0
  310. package/lib/specs/triggers.js +117 -0
  311. package/lib/stores/agentsStore.d.ts +123 -0
  312. package/lib/stores/agentsStore.js +270 -0
  313. package/lib/{components/chat/store → stores}/chatStore.d.ts +3 -2
  314. package/lib/{components/chat/store → stores}/chatStore.js +2 -2
  315. package/lib/{components/chat/store → stores}/conversationStore.d.ts +1 -1
  316. package/lib/{components/chat/store → stores}/conversationStore.js +1 -1
  317. package/lib/{components/chat/store → stores}/index.d.ts +3 -2
  318. package/lib/{components/chat/store → stores}/index.js +3 -2
  319. package/lib/{components/chat/tools → tools}/ToolExecutor.d.ts +2 -2
  320. package/lib/{components/chat/tools → tools}/ToolExecutor.js +1 -1
  321. package/lib/tools/adapters/agent-runtimes/AgentRuntimesToolAdapter.d.ts +1 -1
  322. package/lib/tools/adapters/agent-runtimes/lexicalHooks.d.ts +14 -10
  323. package/lib/tools/adapters/agent-runtimes/lexicalHooks.js +31 -21
  324. package/lib/tools/adapters/agent-runtimes/notebookHooks.d.ts +1 -1
  325. package/lib/tools/index.d.ts +3 -0
  326. package/lib/tools/index.js +3 -7
  327. package/lib/types/a2a.d.ts +39 -0
  328. package/lib/types/acp.d.ts +21 -0
  329. package/lib/types/ag-ui.d.ts +25 -0
  330. package/lib/types/agents-lifecycle.d.ts +36 -0
  331. package/lib/types/agents.d.ts +80 -0
  332. package/lib/types/agents.js +22 -0
  333. package/lib/types/agentspecs.d.ts +90 -0
  334. package/lib/{components/chat/components/base/ChatBase.d.ts → types/chat.d.ts} +59 -99
  335. package/lib/types/checkpoints.d.ts +32 -0
  336. package/lib/types/checkpoints.js +5 -0
  337. package/lib/types/config.d.ts +67 -0
  338. package/lib/{runtime/types.js → types/config.js} +2 -2
  339. package/lib/types/connection.d.ts +31 -0
  340. package/lib/types/connection.js +5 -0
  341. package/lib/types/context.d.ts +67 -0
  342. package/lib/types/context.js +5 -0
  343. package/lib/types/cost.d.ts +42 -0
  344. package/lib/types/cost.js +5 -0
  345. package/lib/types/envvars.d.ts +21 -0
  346. package/lib/types/envvars.js +5 -0
  347. package/lib/types/evals.d.ts +66 -0
  348. package/lib/types/evals.js +5 -0
  349. package/lib/types/events.d.ts +49 -0
  350. package/lib/types/events.js +5 -0
  351. package/lib/types/eventspecs.d.ts +39 -0
  352. package/lib/types/eventspecs.js +5 -0
  353. package/lib/types/examples.d.ts +31 -0
  354. package/lib/types/examples.js +5 -0
  355. package/lib/{components/chat/types → types}/execution.d.ts +10 -1
  356. package/lib/{components/chat/types/extension.d.ts → types/extensions.d.ts} +3 -3
  357. package/lib/types/guardrails.d.ts +106 -0
  358. package/lib/types/guardrails.js +5 -0
  359. package/lib/types/index.d.ts +36 -2
  360. package/lib/types/index.js +35 -2
  361. package/lib/{components/chat/types → types}/inference.d.ts +3 -3
  362. package/lib/types/inference.js +5 -0
  363. package/lib/types/mcp.d.ts +117 -0
  364. package/lib/types/mcp.js +27 -0
  365. package/lib/types/memory.d.ts +23 -0
  366. package/lib/types/memory.js +5 -0
  367. package/lib/{components/chat/types/message.d.ts → types/messages.d.ts} +20 -1
  368. package/lib/{components/chat/types → types}/middleware.d.ts +3 -3
  369. package/lib/types/models.d.ts +63 -0
  370. package/lib/types/models.js +5 -0
  371. package/lib/types/notifications.d.ts +85 -0
  372. package/lib/types/notifications.js +5 -0
  373. package/lib/types/outputs.d.ts +51 -0
  374. package/lib/types/outputs.js +5 -0
  375. package/lib/{components/chat/types → types}/protocol.d.ts +37 -99
  376. package/lib/types/protocol.js +5 -0
  377. package/lib/types/sandbox.d.ts +27 -0
  378. package/lib/types/sandbox.js +24 -0
  379. package/lib/types/skills.d.ts +74 -0
  380. package/lib/types/skills.js +5 -0
  381. package/lib/types/teams.d.ts +133 -0
  382. package/lib/types/teams.js +5 -0
  383. package/lib/types/tool-approvals.d.ts +39 -0
  384. package/lib/types/tool-approvals.js +5 -0
  385. package/lib/{components/chat/types/tool.d.ts → types/tools.d.ts} +59 -4
  386. package/lib/types/triggers.d.ts +48 -0
  387. package/lib/types/triggers.js +5 -0
  388. package/lib/types/usage.d.ts +36 -0
  389. package/lib/types/usage.js +5 -0
  390. package/lib/utils/index.d.ts +1 -0
  391. package/lib/utils/index.js +5 -0
  392. package/lib/utils/utils.d.ts +60 -0
  393. package/lib/utils/utils.js +205 -0
  394. package/package.json +13 -14
  395. package/scripts/codegen/__pycache__/generate_agents.cpython-313.pyc +0 -0
  396. package/scripts/codegen/__pycache__/generate_envvars.cpython-313.pyc +0 -0
  397. package/scripts/codegen/__pycache__/generate_evals.cpython-313.pyc +0 -0
  398. package/scripts/codegen/__pycache__/generate_guardrails.cpython-313.pyc +0 -0
  399. package/scripts/codegen/__pycache__/generate_mcp_servers.cpython-313.pyc +0 -0
  400. package/scripts/codegen/__pycache__/generate_memory.cpython-313.pyc +0 -0
  401. package/scripts/codegen/__pycache__/generate_models.cpython-313.pyc +0 -0
  402. package/scripts/codegen/__pycache__/generate_notifications.cpython-313.pyc +0 -0
  403. package/scripts/codegen/__pycache__/generate_outputs.cpython-313.pyc +0 -0
  404. package/scripts/codegen/__pycache__/generate_skills.cpython-313.pyc +0 -0
  405. package/scripts/codegen/__pycache__/generate_teams.cpython-313.pyc +0 -0
  406. package/scripts/codegen/__pycache__/generate_tools.cpython-313.pyc +0 -0
  407. package/scripts/codegen/__pycache__/generate_triggers.cpython-313.pyc +0 -0
  408. package/scripts/codegen/__pycache__/versioning.cpython-313.pyc +0 -0
  409. package/scripts/codegen/generate_agents.py +373 -60
  410. package/scripts/codegen/generate_envvars.py +36 -35
  411. package/scripts/codegen/generate_evals.py +279 -0
  412. package/scripts/codegen/generate_events.py +312 -0
  413. package/scripts/codegen/generate_frontend_tools.py +266 -0
  414. package/scripts/codegen/generate_guardrails.py +475 -0
  415. package/scripts/codegen/generate_mcp_servers.py +36 -9
  416. package/scripts/codegen/generate_memory.py +468 -0
  417. package/scripts/codegen/generate_models.py +22 -46
  418. package/scripts/codegen/generate_notifications.py +309 -0
  419. package/scripts/codegen/generate_outputs.py +267 -0
  420. package/scripts/codegen/generate_skills.py +108 -51
  421. package/scripts/codegen/generate_teams.py +922 -0
  422. package/scripts/codegen/generate_tools.py +326 -0
  423. package/scripts/codegen/generate_triggers.py +295 -0
  424. package/scripts/codegen/versioning.py +53 -0
  425. package/lib/components/chat/components/base/ChatBase.js +0 -2242
  426. package/lib/components/chat/components/base/InputPrompt.d.ts +0 -42
  427. package/lib/components/chat/components/base/InputPrompt.js +0 -131
  428. package/lib/components/chat/components/display/index.d.ts +0 -6
  429. package/lib/components/chat/components/index.d.ts +0 -26
  430. package/lib/components/chat/components/index.js +0 -39
  431. package/lib/components/chat/extensions/A2UIExtension.d.ts +0 -87
  432. package/lib/components/chat/extensions/A2UIExtension.js +0 -312
  433. package/lib/components/chat/extensions/index.d.ts +0 -10
  434. package/lib/components/chat/index.d.ts +0 -61
  435. package/lib/components/chat/index.js +0 -76
  436. package/lib/components/chat/protocols/VercelAIAdapter.js +0 -315
  437. package/lib/components/chat/tools/index.d.ts +0 -8
  438. package/lib/components/chat/tools/index.js +0 -11
  439. package/lib/components/chat/types/index.d.ts +0 -12
  440. package/lib/components/chat/types/index.js +0 -17
  441. package/lib/components/sparklines/Sparklines.d.ts +0 -16
  442. package/lib/components/sparklines/Sparklines.js +0 -65
  443. package/lib/components/sparklines/SparklinesLine.d.ts +0 -8
  444. package/lib/components/sparklines/SparklinesLine.js +0 -37
  445. package/lib/components/sparklines/dataProcessing.d.ts +0 -25
  446. package/lib/components/sparklines/dataProcessing.js +0 -35
  447. package/lib/components/sparklines/index.d.ts +0 -4
  448. package/lib/components/sparklines/index.js +0 -7
  449. package/lib/components/sparklines/types.d.ts +0 -36
  450. package/lib/examples/stores/examplesStore.d.ts +0 -5
  451. package/lib/examples/stores/notebooks/Empty.ipynb.json +0 -33
  452. package/lib/examples/stores/notebooks/NotebookExample2.ipynb.json +0 -48
  453. package/lib/examples/stores/themeStore.d.ts +0 -33
  454. package/lib/examples/stores/themeStore.js +0 -38
  455. package/lib/hooks/useAgentRuntimes.d.ts +0 -350
  456. package/lib/hooks/useAgentRuntimes.js +0 -78
  457. package/lib/hooks/useKeyboardShortcuts.d.ts +0 -47
  458. package/lib/hooks/useKeyboardShortcuts.js +0 -153
  459. package/lib/hooks/useMobile.d.ts +0 -1
  460. package/lib/hooks/useMobile.js +0 -19
  461. package/lib/hooks/useNotebookAIAgent.d.ts +0 -8
  462. package/lib/hooks/useNotebookAIAgent.js +0 -73
  463. package/lib/renderers/a2ui/components/A2UIRenderer.d.ts +0 -7
  464. package/lib/renderers/a2ui/components/A2UIRenderer.js +0 -102
  465. package/lib/renderers/a2ui/components/SurfaceRenderer.d.ts +0 -7
  466. package/lib/renderers/a2ui/components/SurfaceRenderer.js +0 -101
  467. package/lib/renderers/a2ui/components/content/AudioPlayer.d.ts +0 -9
  468. package/lib/renderers/a2ui/components/content/AudioPlayer.js +0 -38
  469. package/lib/renderers/a2ui/components/content/Divider.d.ts +0 -9
  470. package/lib/renderers/a2ui/components/content/Divider.js +0 -35
  471. package/lib/renderers/a2ui/components/content/Icon.d.ts +0 -9
  472. package/lib/renderers/a2ui/components/content/Icon.js +0 -110
  473. package/lib/renderers/a2ui/components/content/Image.d.ts +0 -9
  474. package/lib/renderers/a2ui/components/content/Image.js +0 -61
  475. package/lib/renderers/a2ui/components/content/Text.d.ts +0 -9
  476. package/lib/renderers/a2ui/components/content/Text.js +0 -64
  477. package/lib/renderers/a2ui/components/content/Video.d.ts +0 -9
  478. package/lib/renderers/a2ui/components/content/Video.js +0 -37
  479. package/lib/renderers/a2ui/components/content/index.d.ts +0 -6
  480. package/lib/renderers/a2ui/components/content/index.js +0 -25
  481. package/lib/renderers/a2ui/components/index.d.ts +0 -5
  482. package/lib/renderers/a2ui/components/index.js +0 -24
  483. package/lib/renderers/a2ui/components/interactive/Button.d.ts +0 -11
  484. package/lib/renderers/a2ui/components/interactive/Button.js +0 -71
  485. package/lib/renderers/a2ui/components/interactive/CheckBox.d.ts +0 -9
  486. package/lib/renderers/a2ui/components/interactive/CheckBox.js +0 -48
  487. package/lib/renderers/a2ui/components/interactive/DateTimeInput.d.ts +0 -9
  488. package/lib/renderers/a2ui/components/interactive/DateTimeInput.js +0 -62
  489. package/lib/renderers/a2ui/components/interactive/MultipleChoice.d.ts +0 -9
  490. package/lib/renderers/a2ui/components/interactive/MultipleChoice.js +0 -73
  491. package/lib/renderers/a2ui/components/interactive/Slider.d.ts +0 -9
  492. package/lib/renderers/a2ui/components/interactive/Slider.js +0 -53
  493. package/lib/renderers/a2ui/components/interactive/TextField.d.ts +0 -9
  494. package/lib/renderers/a2ui/components/interactive/TextField.js +0 -72
  495. package/lib/renderers/a2ui/components/interactive/index.d.ts +0 -6
  496. package/lib/renderers/a2ui/components/interactive/index.js +0 -25
  497. package/lib/renderers/a2ui/components/layout/Card.d.ts +0 -11
  498. package/lib/renderers/a2ui/components/layout/Card.js +0 -30
  499. package/lib/renderers/a2ui/components/layout/Column.d.ts +0 -11
  500. package/lib/renderers/a2ui/components/layout/Column.js +0 -65
  501. package/lib/renderers/a2ui/components/layout/List.d.ts +0 -11
  502. package/lib/renderers/a2ui/components/layout/List.js +0 -55
  503. package/lib/renderers/a2ui/components/layout/Modal.d.ts +0 -11
  504. package/lib/renderers/a2ui/components/layout/Modal.js +0 -58
  505. package/lib/renderers/a2ui/components/layout/Row.d.ts +0 -11
  506. package/lib/renderers/a2ui/components/layout/Row.js +0 -65
  507. package/lib/renderers/a2ui/components/layout/Tabs.d.ts +0 -11
  508. package/lib/renderers/a2ui/components/layout/Tabs.js +0 -48
  509. package/lib/renderers/a2ui/components/layout/index.d.ts +0 -6
  510. package/lib/renderers/a2ui/components/layout/index.js +0 -25
  511. package/lib/renderers/a2ui/context/A2UIContext.d.ts +0 -17
  512. package/lib/renderers/a2ui/context/A2UIContext.js +0 -54
  513. package/lib/renderers/a2ui/context/ThemeContext.d.ts +0 -20
  514. package/lib/renderers/a2ui/context/ThemeContext.js +0 -333
  515. package/lib/renderers/a2ui/hooks/useA2UI.d.ts +0 -36
  516. package/lib/renderers/a2ui/hooks/useA2UI.js +0 -62
  517. package/lib/renderers/a2ui/hooks/useDataBinding.d.ts +0 -8
  518. package/lib/renderers/a2ui/hooks/useDataBinding.js +0 -83
  519. package/lib/renderers/a2ui/index.d.ts +0 -9
  520. package/lib/renderers/a2ui/index.js +0 -28
  521. package/lib/renderers/a2ui/lib/utils.d.ts +0 -11
  522. package/lib/renderers/a2ui/lib/utils.js +0 -38
  523. package/lib/renderers/a2ui/types/index.d.ts +0 -17
  524. package/lib/runtime/index.d.ts +0 -38
  525. package/lib/runtime/index.js +0 -40
  526. package/lib/runtime/runtimeStore.d.ts +0 -76
  527. package/lib/runtime/runtimeStore.js +0 -184
  528. package/lib/runtime/types.d.ts +0 -84
  529. package/lib/runtime/useAgentConnection.d.ts +0 -45
  530. package/lib/runtime/useAgentConnection.js +0 -112
  531. package/lib/runtime/useAgentRuntime.d.ts +0 -93
  532. package/lib/runtime/useAgentRuntime.js +0 -125
  533. package/lib/specs/agents/codeai/agents.d.ts +0 -28
  534. package/lib/specs/agents/codeai/agents.js +0 -179
  535. package/lib/specs/agents/codeai/index.d.ts +0 -1
  536. package/lib/specs/agents/codemode-paper/agents.d.ts +0 -31
  537. package/lib/specs/agents/codemode-paper/agents.js +0 -378
  538. package/lib/specs/agents/codemode-paper/index.d.ts +0 -1
  539. package/lib/specs/agents/codemode-paper/index.js +0 -5
  540. package/lib/specs/agents/datalayer-ai/agents.d.ts +0 -31
  541. package/lib/specs/agents/datalayer-ai/agents.js +0 -352
  542. package/lib/specs/agents/datalayer-ai/index.d.ts +0 -1
  543. package/lib/specs/agents/datalayer-ai/index.js +0 -5
  544. package/lib/specs/agents/mocks/agents.d.ts +0 -43
  545. package/lib/specs/agents/mocks/index.d.ts +0 -1
  546. package/lib/specs/agents/mocks/index.js +0 -5
  547. package/lib/state/index.d.ts +0 -1
  548. package/lib/state/index.js +0 -5
  549. package/lib/state/substates/AIAgentState.d.ts +0 -80
  550. package/lib/state/substates/AIAgentState.js +0 -108
  551. package/lib/state/substates/index.d.ts +0 -1
  552. package/lib/state/substates/index.js +0 -5
  553. package/lib/types/AIAgent.d.ts +0 -17
  554. package/lib/types/Types.d.ts +0 -217
  555. /package/lib/{components/chat → api}/handler.d.ts +0 -0
  556. /package/lib/{renderers/index.d.ts → api/utils.d.ts} +0 -0
  557. /package/lib/{renderers/index.js → api/utils.js} +0 -0
  558. /package/lib/{components/chat/components/elements → chat/display}/PoweredByTag.js +0 -0
  559. /package/lib/{components/chat/components → chat}/parts/DynamicToolPart.js +0 -0
  560. /package/lib/{components/chat/components → chat}/parts/ReasoningPart.d.ts +0 -0
  561. /package/lib/{components/chat/components → chat}/parts/TextPart.js +0 -0
  562. /package/lib/{components/chat/components → chat}/parts/ToolPart.d.ts +0 -0
  563. /package/lib/{components/chat/components → chat}/styles/streamdownStyles.js +0 -0
  564. /package/lib/{components/chat/components/elements → chat/tools}/ToolApprovalDialog.d.ts +0 -0
  565. /package/lib/{components/chat/components → context}/ContextDistribution.d.ts +0 -0
  566. /package/lib/{components/chat/components → context}/ContextDistribution.js +0 -0
  567. /package/lib/{components/chat/components → context}/ContextInspector.d.ts +0 -0
  568. /package/lib/{components/chat/components → context}/ContextPanel.d.ts +0 -0
  569. /package/lib/{components/chat/components → context}/ContextUsage.d.ts +0 -0
  570. /package/lib/{components/chat/components → context}/ContextUsage.js +0 -0
  571. /package/lib/examples/{JupyterCellExample.d.ts → CellSimpleExample.d.ts} +0 -0
  572. /package/lib/examples/{AgentRuntimeCustomExample.d.ts → ChatCustomExample.d.ts} +0 -0
  573. /package/lib/examples/{AgentRuntimeChatExample.d.ts → ChatExample.d.ts} +0 -0
  574. /package/lib/examples/{AgentRuntimeStandaloneExample.d.ts → ChatStandaloneExample.d.ts} +0 -0
  575. /package/lib/examples/{AgentRuntimeNotebookExample.d.ts → NotebookExample.d.ts} +0 -0
  576. /package/lib/examples/{AgentRuntimeNotebookSidebarExample.d.ts → NotebookSidebarExample.d.ts} +0 -0
  577. /package/lib/examples/{JupyterNotebookExample.d.ts → NotebookSimpleExample.d.ts} +0 -0
  578. /package/lib/examples/{stores → utils}/agents/earthquake-detector.ipynb.json +0 -0
  579. /package/lib/examples/{stores → utils}/agents/earthquake-detector.json +0 -0
  580. /package/lib/examples/{stores → utils}/agents/earthquake-detector.lexical.json +0 -0
  581. /package/lib/examples/{stores → utils}/agents/sales-forecaster.ipynb.json +0 -0
  582. /package/lib/examples/{stores → utils}/agents/sales-forecaster.json +0 -0
  583. /package/lib/examples/{stores → utils}/agents/sales-forecaster.lexical.json +0 -0
  584. /package/lib/examples/{stores → utils}/agents/social-post-generator.ipynb.json +0 -0
  585. /package/lib/examples/{stores → utils}/agents/social-post-generator.json +0 -0
  586. /package/lib/examples/{stores → utils}/agents/social-post-generator.lexical.json +0 -0
  587. /package/lib/examples/{stores → utils}/agents/stock-market.ipynb.json +0 -0
  588. /package/lib/examples/{stores → utils}/agents/stock-market.json +0 -0
  589. /package/lib/examples/{stores → utils}/agents/stock-market.lexical.json +0 -0
  590. /package/lib/examples/{stores → utils}/notebooks/IPyWidgetsExample.ipynb.json +0 -0
  591. /package/lib/examples/{stores → utils}/notebooks/IPyWidgetsExampleWithState.ipynb.json +0 -0
  592. /package/lib/examples/{stores → utils}/notebooks/Lite.ipynb.json +0 -0
  593. /package/lib/examples/{stores → utils}/notebooks/Matplotlib.ipynb.json +0 -0
  594. /package/lib/examples/{stores → utils}/notebooks/NotebookExample1.ipynb.json +0 -0
  595. /package/lib/examples/{stores → utils}/notebooks/NotebookOutputs.ipynb.json +0 -0
  596. /package/lib/examples/{stores → utils}/notebooks/NotebookToCExample.ipynb.json +0 -0
  597. /package/lib/examples/{stores → utils}/notebooks/OutputIPyWidgetsExample.d.ts +0 -0
  598. /package/lib/examples/{stores → utils}/notebooks/OutputIPyWidgetsExample.js +0 -0
  599. /package/lib/examples/{stores → utils}/notebooks/PyGWalker.ipynb.json +0 -0
  600. /package/lib/examples/{stores → utils}/themedProvider.d.ts +0 -0
  601. /package/lib/examples/{stores → utils}/themedProvider.js +0 -0
  602. /package/lib/{components/chat/inference → inference}/BaseInferenceProvider.js +0 -0
  603. /package/lib/{components → mcp}/McpServerManager.js +0 -0
  604. /package/lib/{components/chat/middleware → middleware}/MiddlewarePipeline.js +0 -0
  605. /package/lib/{components/chat/types/inference.js → types/a2a.js} +0 -0
  606. /package/lib/{components/chat/types/protocol.js → types/acp.js} +0 -0
  607. /package/lib/{components/sparklines/types.js → types/ag-ui.js} +0 -0
  608. /package/lib/{renderers/a2ui/types/index.js → types/agents-lifecycle.js} +0 -0
  609. /package/lib/types/{AIAgent.js → agentspecs.js} +0 -0
  610. /package/lib/types/{Types.js → chat.js} +0 -0
  611. /package/lib/{components/chat/types → types}/execution.js +0 -0
  612. /package/lib/{components/chat/types/extension.js → types/extensions.js} +0 -0
  613. /package/lib/{components/chat/types/message.js → types/messages.js} +0 -0
  614. /package/lib/{components/chat/types → types}/middleware.js +0 -0
  615. /package/lib/{components/chat/types/tool.js → types/tools.js} +0 -0
@@ -1,2242 +0,0 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- /*
3
- * Copyright (c) 2025-2026 Datalayer, Inc.
4
- * Distributed under the terms of the Modified BSD License.
5
- */
6
- /**
7
- * Main ChatBase component.
8
- * Provides a full chat interface with messages and input.
9
- * This is the base component used by all other chat container components.
10
- *
11
- * Supports multiple modes:
12
- * 1. Store mode: Uses Zustand store for state management (default)
13
- * 2. Protocol mode: Connects to backend via AG-UI, A2A, ACP, or Vercel AI protocols
14
- * 3. Custom mode: Uses onSendMessage prop for custom message handling
15
- *
16
- * @module components/chat/components/ChatBase
17
- */
18
- import { useContext } from 'react';
19
- import { useCallback, useEffect, useRef, useState, } from 'react';
20
- import { Heading, Text, Spinner, IconButton, Button, ActionMenu, ActionList, LabelGroup, Label, ToggleSwitch, } from '@primer/react';
21
- import { Box } from '@datalayer/primer-addons';
22
- import { AlertIcon, PlusIcon, TrashIcon, GearIcon, PersonIcon, ToolsIcon, AiModelIcon, BriefcaseIcon, CircleIcon, SquareFillIcon, CommentDiscussionIcon, DeviceMobileIcon, SidebarExpandIcon, InfoIcon, } from '@primer/octicons-react';
23
- import { AiAgentIcon } from '@datalayer/icons-react';
24
- import ReactECharts from 'echarts-for-react';
25
- import { useQuery, QueryClient, QueryClientProvider, QueryClientContext, } from '@tanstack/react-query';
26
- import { Streamdown } from 'streamdown';
27
- import { streamdownMarkdownStyles, streamdownCodeBlockStyles, } from '../styles/streamdownStyles';
28
- import { PoweredByTag } from '../elements/PoweredByTag';
29
- import { requestAPI } from '../../handler';
30
- import { useChatStore } from '../../store/chatStore';
31
- import { useConversationStore } from '../../store/conversationStore';
32
- import { generateMessageId, createUserMessage, createAssistantMessage, } from '../../types/message';
33
- import { AGUIAdapter, A2AAdapter, VercelAIAdapter, ACPAdapter, } from '../../protocols';
34
- import { ToolCallDisplay } from '../display/ToolCallDisplay';
35
- import { InputPrompt } from './InputPrompt';
36
- // Singleton QueryClient for ChatBase instances without external QueryClientProvider
37
- const internalQueryClient = new QueryClient({
38
- defaultOptions: {
39
- queries: {
40
- staleTime: 5 * 60 * 1000, // 5 minutes
41
- refetchOnWindowFocus: false,
42
- },
43
- },
44
- });
45
- // Primer's default portal root ID
46
- const PRIMER_PORTAL_ROOT_ID = '__primerPortalRoot__';
47
- /**
48
- * Hook to ensure Primer's default portal root has a high z-index.
49
- * This ensures dropdown menus appear above floating chat panels.
50
- */
51
- function useHighZIndexPortal() {
52
- useEffect(() => {
53
- // Set up a MutationObserver to watch for the portal root being added
54
- const setPortalZIndex = () => {
55
- const portalRoot = document.getElementById(PRIMER_PORTAL_ROOT_ID);
56
- if (portalRoot) {
57
- portalRoot.style.zIndex = '9999';
58
- return true;
59
- }
60
- return false;
61
- };
62
- // Try immediately
63
- if (setPortalZIndex()) {
64
- return;
65
- }
66
- // If not found yet, observe for it
67
- const observer = new MutationObserver(() => {
68
- if (setPortalZIndex()) {
69
- observer.disconnect();
70
- }
71
- });
72
- observer.observe(document.body, {
73
- childList: true,
74
- subtree: true,
75
- });
76
- return () => {
77
- observer.disconnect();
78
- };
79
- }, []);
80
- }
81
- /**
82
- * Check if an item is a tool call message
83
- */
84
- function isToolCallMessage(item) {
85
- return 'type' in item && item.type === 'tool-call';
86
- }
87
- /**
88
- * Extract text content from a ChatMessage
89
- */
90
- function getMessageText(message) {
91
- if (typeof message.content === 'string') {
92
- return message.content;
93
- }
94
- // Array of ContentPart - extract text parts
95
- return message.content
96
- .filter((part) => part.type === 'text')
97
- .map(part => part.text)
98
- .join('');
99
- }
100
- /**
101
- * Convert history messages to display items.
102
- *
103
- * History returns:
104
- * - Assistant messages with `toolCalls` array (tool invocations)
105
- * - Tool messages (role='tool') with content (tool results)
106
- *
107
- * For display, we need to:
108
- * 1. Keep user/assistant text messages as ChatMessage
109
- * 2. Convert each toolCall from assistant messages into a ToolCallMessage
110
- * 3. Match tool result messages (role='tool') to their ToolCallMessage and update result
111
- * 4. Filter out raw tool messages (role='tool') from display — they're merged into ToolCallMessage
112
- */
113
- function convertHistoryToDisplayItems(messages) {
114
- const displayItems = [];
115
- const toolCallMap = new Map();
116
- // First pass: collect all tool calls and build the initial display list
117
- for (const msg of messages) {
118
- if (msg.role === 'tool') {
119
- // Tool result messages — will be merged later
120
- const toolCallId = msg.metadata?.toolCallId;
121
- if (toolCallId && toolCallMap.has(toolCallId)) {
122
- // Update the existing tool call with the result
123
- const toolCall = toolCallMap.get(toolCallId);
124
- toolCall.result = msg.content;
125
- toolCall.status = 'complete';
126
- }
127
- // Don't add tool messages to display — they're represented by ToolCallMessage
128
- continue;
129
- }
130
- if (msg.role === 'assistant' && msg.toolCalls && msg.toolCalls.length > 0) {
131
- // Assistant message with tool calls
132
- // First add any text content as a regular message
133
- const textContent = typeof msg.content === 'string' ? msg.content.trim() : '';
134
- if (textContent) {
135
- displayItems.push({
136
- ...msg,
137
- toolCalls: undefined, // Remove toolCalls from the text message
138
- });
139
- }
140
- // Then add each tool call as a ToolCallMessage
141
- // Map from message.ts ToolCallStatus to ChatBase ToolCallStatus
142
- for (const tc of msg.toolCalls) {
143
- let status = 'complete';
144
- if (tc.status === 'pending' || tc.status === 'awaiting-approval') {
145
- status = 'inProgress';
146
- }
147
- else if (tc.status === 'executing') {
148
- status = 'executing';
149
- }
150
- else if (tc.status === 'failed') {
151
- status = 'error';
152
- }
153
- const toolCallMsg = {
154
- id: `tc-${tc.toolCallId}`,
155
- type: 'tool-call',
156
- toolCallId: tc.toolCallId,
157
- toolName: tc.toolName,
158
- args: tc.args || {},
159
- status,
160
- result: tc.result,
161
- };
162
- toolCallMap.set(tc.toolCallId, toolCallMsg);
163
- displayItems.push(toolCallMsg);
164
- }
165
- }
166
- else {
167
- // Regular user/assistant/system message
168
- displayItems.push(msg);
169
- }
170
- }
171
- return displayItems;
172
- }
173
- /**
174
- * Create protocol adapter based on configuration
175
- */
176
- function createProtocolAdapter(config) {
177
- const adapterConfig = {
178
- type: config.type,
179
- baseUrl: config.endpoint,
180
- authToken: config.authToken,
181
- agentId: config.agentId,
182
- ...config.options,
183
- };
184
- switch (config.type) {
185
- case 'ag-ui':
186
- return new AGUIAdapter(adapterConfig);
187
- case 'a2a':
188
- return new A2AAdapter(adapterConfig);
189
- case 'vercel-ai':
190
- return new VercelAIAdapter(adapterConfig);
191
- case 'acp':
192
- return new ACPAdapter(adapterConfig);
193
- default:
194
- console.warn(`[ChatBase] Unknown protocol type: ${config.type}`);
195
- return null;
196
- }
197
- }
198
- /**
199
- * Hook to safely use query when QueryClient is available
200
- * Returns null if no QueryClientProvider is present
201
- */
202
- function useConfigQuery(enabled, configEndpoint, authToken) {
203
- const queryClient = useContext(QueryClientContext);
204
- // If no QueryClient is available, return a mock result
205
- if (!queryClient) {
206
- return {
207
- data: undefined,
208
- isLoading: false,
209
- isError: false,
210
- error: null,
211
- };
212
- }
213
- // eslint-disable-next-line react-hooks/rules-of-hooks
214
- return useQuery({
215
- queryFn: async () => {
216
- // If configEndpoint is provided, use direct fetch (for FastAPI)
217
- if (configEndpoint) {
218
- const headers = {
219
- 'Content-Type': 'application/json',
220
- };
221
- if (authToken) {
222
- headers['Authorization'] = `Bearer ${authToken}`;
223
- }
224
- const response = await fetch(configEndpoint, { headers });
225
- if (!response.ok) {
226
- throw new Error(`Config fetch failed: ${response.statusText}`);
227
- }
228
- return response.json();
229
- }
230
- // Otherwise use Jupyter requestAPI
231
- return requestAPI('configure');
232
- },
233
- queryKey: ['models', configEndpoint || 'jupyter'],
234
- enabled,
235
- retry: 1,
236
- });
237
- }
238
- /**
239
- * Hook to fetch available skills from backend
240
- */
241
- function useSkillsQuery(enabled, baseEndpoint, authToken) {
242
- const queryClient = useContext(QueryClientContext);
243
- // If no QueryClient is available, return a mock result
244
- if (!queryClient) {
245
- return {
246
- data: undefined,
247
- isLoading: false,
248
- isError: false,
249
- error: null,
250
- refetch: () => Promise.resolve({ data: undefined }),
251
- };
252
- }
253
- // eslint-disable-next-line react-hooks/rules-of-hooks
254
- return useQuery({
255
- queryFn: async () => {
256
- if (!baseEndpoint) {
257
- return { skills: [], total: 0 };
258
- }
259
- // Derive skills endpoint from config endpoint
260
- const skillsEndpoint = baseEndpoint.replace('/configure', '/skills');
261
- const headers = {
262
- 'Content-Type': 'application/json',
263
- };
264
- if (authToken) {
265
- headers['Authorization'] = `Bearer ${authToken}`;
266
- }
267
- const response = await fetch(skillsEndpoint, { headers });
268
- if (!response.ok) {
269
- throw new Error(`Skills fetch failed: ${response.statusText}`);
270
- }
271
- return response.json();
272
- },
273
- queryKey: ['skills', baseEndpoint || 'jupyter'],
274
- enabled,
275
- staleTime: 5 * 60 * 1000, // 5 minutes
276
- retry: 1,
277
- });
278
- }
279
- /**
280
- * Format token count for compact display
281
- */
282
- function formatTokenCount(tokens) {
283
- if (tokens >= 1_000_000)
284
- return `${(tokens / 1_000_000).toFixed(1)}M`;
285
- if (tokens >= 1_000)
286
- return `${(tokens / 1_000).toFixed(1)}K`;
287
- return tokens.toString();
288
- }
289
- /**
290
- * Derive the API base URL from a configEndpoint.
291
- * configEndpoint may look like:
292
- * "http://host:port/api/v1/config" (from agentRuntimeConfig)
293
- * "http://host:port/api/v1/configure" (from Chat component)
294
- * We strip the trailing path segment to get "http://host:port/api/v1".
295
- */
296
- function getApiBaseFromConfig(configEndpoint) {
297
- return configEndpoint.replace(/\/(config|configure)\/?$/, '');
298
- }
299
- /**
300
- * Hook to poll agent context-snapshot from the backend.
301
- * Returns cumulative token usage (input/output breakdown) tracked by the agent server.
302
- * Uses the same endpoint as codeai: GET /api/v1/configure/agents/{agentId}/context-snapshot
303
- */
304
- function useContextSnapshotQuery(enabled, configEndpoint, agentId, authToken) {
305
- const queryClient = useContext(QueryClientContext);
306
- if (!queryClient) {
307
- return { data: undefined, isLoading: false, isError: false, error: null };
308
- }
309
- const snapshotUrl = configEndpoint && agentId
310
- ? `${getApiBaseFromConfig(configEndpoint)}/configure/agents/${encodeURIComponent(agentId)}/context-snapshot`
311
- : undefined;
312
- // eslint-disable-next-line react-hooks/rules-of-hooks
313
- const result = useQuery({
314
- queryKey: ['context-snapshot-header', agentId, snapshotUrl],
315
- queryFn: async () => {
316
- if (!snapshotUrl) {
317
- throw new Error('No context-snapshot URL available');
318
- }
319
- const headers = { 'Content-Type': 'application/json' };
320
- if (authToken) {
321
- headers['Authorization'] = `Bearer ${authToken}`;
322
- }
323
- const response = await fetch(snapshotUrl, { headers });
324
- if (!response.ok) {
325
- throw new Error(`Context snapshot fetch failed: ${response.statusText}`);
326
- }
327
- return response.json();
328
- },
329
- enabled: enabled && !!snapshotUrl,
330
- // Poll every 10 seconds, but stop polling once the query has errored (e.g. runtime terminated)
331
- refetchInterval: query => (query.state.status === 'error' ? false : 10_000),
332
- refetchOnMount: 'always',
333
- staleTime: 0,
334
- retry: 1,
335
- });
336
- return result;
337
- }
338
- /**
339
- * Hook to poll sandbox execution status from the backend.
340
- * Returns whether a sandbox is available and if code is currently executing.
341
- */
342
- function useSandboxStatusQuery(enabled, configEndpoint, authToken) {
343
- const queryClient = useContext(QueryClientContext);
344
- if (!queryClient) {
345
- return {
346
- data: undefined,
347
- isLoading: false,
348
- isError: false,
349
- error: null,
350
- refetch: () => Promise.resolve({}),
351
- };
352
- }
353
- const statusUrl = configEndpoint
354
- ? `${getApiBaseFromConfig(configEndpoint)}/configure/sandbox-status`
355
- : undefined;
356
- // eslint-disable-next-line react-hooks/rules-of-hooks
357
- const result = useQuery({
358
- queryKey: ['sandbox-status', statusUrl],
359
- queryFn: async () => {
360
- if (!statusUrl) {
361
- throw new Error('No sandbox status URL available');
362
- }
363
- const headers = { 'Content-Type': 'application/json' };
364
- if (authToken) {
365
- headers['Authorization'] = `Bearer ${authToken}`;
366
- }
367
- const response = await fetch(statusUrl, { headers });
368
- if (!response.ok) {
369
- throw new Error(`Sandbox status fetch failed: ${response.statusText}`);
370
- }
371
- return response.json();
372
- },
373
- enabled: enabled && !!statusUrl,
374
- refetchInterval: query => (query.state.status === 'error' ? false : 2_000),
375
- refetchOnMount: 'always',
376
- staleTime: 0,
377
- retry: 1,
378
- });
379
- return result;
380
- }
381
- /**
382
- * ChatBase component - Universal chat panel supporting store, protocol, and custom modes
383
- */
384
- export function ChatBase({ title, showHeader = false, showTokenUsage = true, showLoadingIndicator = true, showErrors = true, showInput = true, showModelSelector = false, showToolsMenu = false, showSkillsMenu = false, codemodeEnabled = false, initialModel, availableModels, mcpServers, initialSkills, className, loadingState, headerActions, chatViewMode, onChatViewModeChange,
385
- // Mode selection
386
- useStore: useStoreMode = true, protocol: protocolProp, agentRuntimeConfig, onSendMessage, enableStreaming = false,
387
- // Extended props
388
- brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, emptyState, renderToolResult, footerContent, showInformation = false, onInformationClick, headerContent, children, borderRadius, backgroundColor, border, boxShadow, compact = false, placeholder, description = 'Start a conversation with the AI agent.', onStateUpdate, onNewChat, onClear, onMessagesChange, autoFocus = false, suggestions, submitOnSuggestionClick = true, hideMessagesAfterToolUI = false, focusTrigger, frontendTools,
389
- // Identity/Authorization props
390
- onAuthorizationRequired, connectedIdentities,
391
- // Conversation persistence
392
- runtimeId, historyEndpoint, historyAuthToken,
393
- // Pending prompt
394
- pendingPrompt, }) {
395
- const protocol = agentRuntimeConfig
396
- ? {
397
- type: agentRuntimeConfig.protocol || 'ag-ui',
398
- endpoint: agentRuntimeConfig.url,
399
- authToken: agentRuntimeConfig.authToken,
400
- agentId: agentRuntimeConfig.agentId,
401
- enableConfigQuery: true,
402
- configEndpoint: `${agentRuntimeConfig.url}/api/v1/config`,
403
- }
404
- : protocolProp;
405
- // If agentRuntimeConfig is provided, force protocol mode
406
- const effectiveUseStoreMode = agentRuntimeConfig ? false : useStoreMode;
407
- // Check if QueryClientProvider is already available
408
- const existingQueryClient = useContext(QueryClientContext);
409
- // If no QueryClient is available, wrap with our internal provider
410
- if (!existingQueryClient) {
411
- return (_jsx(QueryClientProvider, { client: internalQueryClient, children: _jsx(ChatBaseInner, { title: title, showHeader: showHeader, showTokenUsage: showTokenUsage, showLoadingIndicator: showLoadingIndicator, showErrors: showErrors, showInput: showInput, showModelSelector: showModelSelector, showToolsMenu: showToolsMenu, showSkillsMenu: showSkillsMenu, codemodeEnabled: codemodeEnabled, initialModel: initialModel, availableModels: availableModels, mcpServers: mcpServers, initialSkills: initialSkills, className: className, loadingState: loadingState, headerActions: headerActions, chatViewMode: chatViewMode, onChatViewModeChange: onChatViewModeChange, useStore: effectiveUseStoreMode, protocol: protocol, onSendMessage: onSendMessage, enableStreaming: enableStreaming, brandIcon: brandIcon, avatarConfig: avatarConfig, headerButtons: headerButtons, showPoweredBy: showPoweredBy, poweredByProps: poweredByProps, emptyState: emptyState, renderToolResult: renderToolResult, footerContent: footerContent, showInformation: showInformation, onInformationClick: onInformationClick, headerContent: headerContent, children: children, borderRadius: borderRadius, backgroundColor: backgroundColor, border: border, boxShadow: boxShadow, compact: compact, placeholder: placeholder, description: description, onStateUpdate: onStateUpdate, onNewChat: onNewChat, onClear: onClear, onMessagesChange: onMessagesChange, autoFocus: autoFocus, suggestions: suggestions, submitOnSuggestionClick: submitOnSuggestionClick, hideMessagesAfterToolUI: hideMessagesAfterToolUI, focusTrigger: focusTrigger, frontendTools: frontendTools, onAuthorizationRequired: onAuthorizationRequired, connectedIdentities: connectedIdentities, runtimeId: runtimeId, historyEndpoint: historyEndpoint, historyAuthToken: historyAuthToken, pendingPrompt: pendingPrompt }) }));
412
- }
413
- // QueryClient already available, render inner component directly
414
- return (_jsx(ChatBaseInner, { title: title, showHeader: showHeader, showTokenUsage: showTokenUsage, showLoadingIndicator: showLoadingIndicator, showErrors: showErrors, showInput: showInput, showModelSelector: showModelSelector, showToolsMenu: showToolsMenu, showSkillsMenu: showSkillsMenu, codemodeEnabled: codemodeEnabled, initialModel: initialModel, availableModels: availableModels, mcpServers: mcpServers, initialSkills: initialSkills, className: className, loadingState: loadingState, headerActions: headerActions, chatViewMode: chatViewMode, onChatViewModeChange: onChatViewModeChange, useStore: effectiveUseStoreMode, protocol: protocol, onSendMessage: onSendMessage, enableStreaming: enableStreaming, brandIcon: brandIcon, avatarConfig: avatarConfig, headerButtons: headerButtons, showPoweredBy: showPoweredBy, poweredByProps: poweredByProps, emptyState: emptyState, renderToolResult: renderToolResult, footerContent: footerContent, showInformation: showInformation, onInformationClick: onInformationClick, headerContent: headerContent, children: children, borderRadius: borderRadius, backgroundColor: backgroundColor, border: border, boxShadow: boxShadow, compact: compact, placeholder: placeholder, description: description, onStateUpdate: onStateUpdate, onNewChat: onNewChat, onClear: onClear, onMessagesChange: onMessagesChange, autoFocus: autoFocus, suggestions: suggestions, submitOnSuggestionClick: submitOnSuggestionClick, hideMessagesAfterToolUI: hideMessagesAfterToolUI, focusTrigger: focusTrigger, frontendTools: frontendTools, onAuthorizationRequired: onAuthorizationRequired, connectedIdentities: connectedIdentities, runtimeId: runtimeId, historyEndpoint: historyEndpoint, historyAuthToken: historyAuthToken, pendingPrompt: pendingPrompt }));
415
- }
416
- /**
417
- * Inner ChatBase component - contains all the actual logic
418
- */
419
- function ChatBaseInner({ title, showHeader = false, showTokenUsage = true, showLoadingIndicator = true, showErrors = true, showInput = true, showModelSelector = false, showToolsMenu = false, showSkillsMenu = false, codemodeEnabled = false, initialModel, availableModels, mcpServers, initialSkills, className, loadingState, headerActions, chatViewMode, onChatViewModeChange,
420
- // Mode selection
421
- useStore: useStoreMode = true, protocol, onSendMessage, enableStreaming = false,
422
- // Extended props
423
- brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, emptyState, renderToolResult, footerContent, showInformation = false, onInformationClick, headerContent, children, borderRadius, backgroundColor, border, boxShadow, compact = false, placeholder, description = 'Start a conversation with the AI agent.', onStateUpdate, onNewChat, onClear, onMessagesChange, autoFocus = false, suggestions, submitOnSuggestionClick = true, hideMessagesAfterToolUI = false, focusTrigger, frontendTools,
424
- // Identity/Authorization props
425
- onAuthorizationRequired, connectedIdentities,
426
- // Conversation persistence
427
- runtimeId, historyEndpoint, historyAuthToken,
428
- // Pending prompt
429
- pendingPrompt, }) {
430
- useHighZIndexPortal();
431
- // Stabilize the protocol reference so that the adapter-init effect only
432
- // re-runs when the protocol *contents* actually change, not just when the
433
- // parent re-renders with a new object literal that has the same values.
434
- const protocolKey = protocol ? JSON.stringify(protocol) : '';
435
- // Store (optional for message persistence)
436
- const clearStoreMessages = useChatStore(state => state.clearMessages);
437
- // Check if protocol is A2A (doesn't support per-request model override)
438
- const isA2AProtocol = protocol?.type === 'a2a';
439
- // Component state
440
- const [displayItems, setDisplayItems] = useState([]);
441
- const [isLoading, setIsLoading] = useState(false);
442
- const [isStreaming, setIsStreaming] = useState(false);
443
- const [error, setError] = useState(null);
444
- const [input, setInput] = useState('');
445
- // History-loaded flag — true immediately when there is nothing to fetch
446
- const [historyLoaded, setHistoryLoaded] = useState(!runtimeId);
447
- // Adapter-ready flag — flipped to true once the protocol adapter is initialised
448
- const [adapterReady, setAdapterReady] = useState(false);
449
- // Guard so the pending prompt is sent at most once
450
- const pendingPromptSentRef = useRef(false);
451
- const [selectedModel, setSelectedModel] = useState('');
452
- // enabledTools tracks which MCP server tools are enabled
453
- // Format: Map<serverId, Set<toolName>>
454
- const [enabledMcpTools, setEnabledMcpTools] = useState(new Map());
455
- // Note: legacy _enabledTools for backend-defined tools from config query
456
- // Frontend tools are passed via frontendTools prop
457
- const [_enabledTools, setEnabledTools] = useState([]);
458
- // Skills state - tracks which skills are enabled
459
- const [enabledSkills, setEnabledSkills] = useState(new Set());
460
- // Config query (for protocols that support it)
461
- // Safely handles missing QueryClientProvider
462
- const configQuery = useConfigQuery(Boolean(protocol?.enableConfigQuery), protocol?.configEndpoint, protocol?.authToken);
463
- // Skills query (for protocols that support it)
464
- const skillsQuery = useSkillsQuery(Boolean(protocol?.enableConfigQuery) && showSkillsMenu, protocol?.configEndpoint, protocol?.authToken);
465
- // Context snapshot query — polls agent-level cumulative token usage (input/output breakdown)
466
- // Gated by showTokenUsage (not showHeader), so usage is visible even without a title bar.
467
- const contextSnapshotQuery = useContextSnapshotQuery(Boolean(protocol?.enableConfigQuery) && showTokenUsage, protocol?.configEndpoint, protocol?.agentId, protocol?.authToken);
468
- const agentUsage = contextSnapshotQuery.data;
469
- // Sandbox status query — polls sandbox execution state for the header indicator.
470
- // Only active when codemode is enabled and there's a config endpoint.
471
- const sandboxStatusQuery = useSandboxStatusQuery(Boolean(protocol?.enableConfigQuery) && codemodeEnabled && showHeader, protocol?.configEndpoint, protocol?.authToken);
472
- const sandboxStatus = sandboxStatusQuery.data;
473
- // Refs
474
- const adapterRef = useRef(null);
475
- const unsubscribeRef = useRef(null);
476
- const toolCallsRef = useRef(new Map());
477
- // Track the number of in-flight frontend tool executions.
478
- // While > 0, isLoading must stay true (the agent turn is not finished).
479
- const pendingToolExecutionsRef = useRef(0);
480
- const currentAssistantMessageRef = useRef(null);
481
- const threadIdRef = useRef(generateMessageId());
482
- const messagesEndRef = useRef(null);
483
- const inputRef = useRef(null);
484
- const abortControllerRef = useRef(null);
485
- // State for context pie chart overlay
486
- const [contextOverlayOpen, setContextOverlayOpen] = useState(false);
487
- const contextAnchorRef = useRef(null);
488
- const hoverTimeoutRef = useRef(null);
489
- // Use a ref for connectedIdentities to avoid infinite loops in useCallback
490
- // (the array reference changes on every render even if contents are the same)
491
- const connectedIdentitiesRef = useRef(connectedIdentities);
492
- connectedIdentitiesRef.current = connectedIdentities;
493
- const isServerSelected = useCallback((server) => {
494
- if (!mcpServers)
495
- return true;
496
- const origin = server.isConfig === false ? 'catalog' : 'config';
497
- return mcpServers.some(s => s.id === server.id && s.origin === origin);
498
- }, [mcpServers]);
499
- // Auto-focus input on mount
500
- useEffect(() => {
501
- if (autoFocus && inputRef.current) {
502
- // Small delay to ensure the component is fully rendered
503
- const timeoutId = setTimeout(() => {
504
- inputRef.current?.focus();
505
- }, 100);
506
- return () => clearTimeout(timeoutId);
507
- }
508
- }, [autoFocus]);
509
- // Refocus input when focusTrigger changes
510
- useEffect(() => {
511
- if (focusTrigger !== undefined && focusTrigger > 0 && inputRef.current) {
512
- // Small delay to ensure any layout changes have completed
513
- const timeoutId = setTimeout(() => {
514
- inputRef.current?.focus();
515
- }, 150);
516
- return () => clearTimeout(timeoutId);
517
- }
518
- }, [focusTrigger]);
519
- // Track previous loading state to detect when loading completes
520
- const wasLoadingRef = useRef(false);
521
- // Refocus input when loading completes
522
- useEffect(() => {
523
- if (wasLoadingRef.current && !isLoading && inputRef.current) {
524
- // Small delay to ensure the input is fully enabled
525
- const timeoutId = setTimeout(() => {
526
- inputRef.current?.focus();
527
- }, 50);
528
- return () => clearTimeout(timeoutId);
529
- }
530
- wasLoadingRef.current = isLoading;
531
- }, [isLoading]);
532
- // Auto-resize textarea based on content
533
- const adjustTextareaHeight = useCallback(() => {
534
- const textarea = inputRef.current;
535
- if (textarea) {
536
- // Reset height to auto to get proper scrollHeight
537
- textarea.style.height = 'auto';
538
- // Set height to scrollHeight, capped at maxHeight (120px)
539
- const maxHeight = 120;
540
- const minHeight = 40;
541
- const newHeight = Math.min(Math.max(textarea.scrollHeight, minHeight), maxHeight);
542
- textarea.style.height = `${newHeight}px`;
543
- // Add overflow if content exceeds maxHeight
544
- textarea.style.overflowY =
545
- textarea.scrollHeight > maxHeight ? 'auto' : 'hidden';
546
- }
547
- }, []);
548
- // Adjust textarea height when input changes
549
- useEffect(() => {
550
- adjustTextareaHeight();
551
- }, [input, adjustTextareaHeight]);
552
- // Ensure textarea has a minimum height on mount
553
- useEffect(() => {
554
- const timer = setTimeout(adjustTextareaHeight, 0);
555
- return () => clearTimeout(timer);
556
- }, [adjustTextareaHeight]);
557
- // Initialize model and tools when config is available
558
- useEffect(() => {
559
- if ((configQuery.data || availableModels) && !selectedModel) {
560
- // Use availableModels override if provided, otherwise use config models
561
- const modelsList = availableModels || configQuery.data?.models || [];
562
- // Priority: initialModel prop > defaultModel from config > first available model
563
- const preferredModel = initialModel || configQuery.data?.defaultModel;
564
- if (preferredModel) {
565
- // Check if the preferred model exists in the models list
566
- const modelExists = modelsList.some(m => m.id === preferredModel);
567
- if (modelExists) {
568
- setSelectedModel(preferredModel);
569
- }
570
- else {
571
- // Fallback to first available model if preferred model not found
572
- const firstAvailableModel = modelsList.find(m => m.isAvailable !== false);
573
- const firstModel = firstAvailableModel || modelsList[0];
574
- if (firstModel) {
575
- setSelectedModel(firstModel.id);
576
- }
577
- }
578
- }
579
- else {
580
- // No preferred model, select first available model
581
- const firstAvailableModel = modelsList.find(m => m.isAvailable !== false);
582
- const firstModel = firstAvailableModel || modelsList[0];
583
- if (firstModel) {
584
- setSelectedModel(firstModel.id);
585
- }
586
- }
587
- const allToolIds = configQuery.data?.builtinTools?.map(tool => tool.id) || [];
588
- setEnabledTools(allToolIds);
589
- // Initialize MCP server tools
590
- if (configQuery.data?.mcpServers) {
591
- const newEnabledMcpTools = new Map();
592
- for (const server of configQuery.data.mcpServers) {
593
- if (server.isAvailable && server.enabled) {
594
- // If mcpServers is provided, only enable those servers
595
- // If not provided, enable all available servers
596
- const shouldEnableServer = isServerSelected(server);
597
- if (shouldEnableServer) {
598
- const enabledToolNames = new Set(server.tools.filter(t => t.enabled).map(t => t.name));
599
- newEnabledMcpTools.set(server.id, enabledToolNames);
600
- }
601
- }
602
- }
603
- setEnabledMcpTools(newEnabledMcpTools);
604
- }
605
- }
606
- }, [
607
- configQuery.data,
608
- selectedModel,
609
- initialModel,
610
- availableModels,
611
- mcpServers,
612
- isServerSelected,
613
- ]);
614
- // Update enabled MCP servers when mcpServers prop changes (e.g., server removed)
615
- useEffect(() => {
616
- if (!configQuery.data?.mcpServers || !mcpServers)
617
- return;
618
- setEnabledMcpTools(prev => {
619
- const newMap = new Map();
620
- // Only keep servers that are in mcpServers
621
- for (const server of configQuery.data?.mcpServers ?? []) {
622
- if (isServerSelected(server) && prev.has(server.id)) {
623
- // Keep existing tool selection for this server
624
- newMap.set(server.id, prev.get(server.id));
625
- }
626
- else if (isServerSelected(server) &&
627
- server.isAvailable &&
628
- server.enabled) {
629
- // Newly added server - enable all tools
630
- const enabledToolNames = new Set(server.tools.filter(t => t.enabled).map(t => t.name));
631
- newMap.set(server.id, enabledToolNames);
632
- }
633
- // Servers not in mcpServers are excluded
634
- }
635
- return newMap;
636
- });
637
- }, [mcpServers, configQuery.data?.mcpServers, isServerSelected]);
638
- // Initialize enabled skills from initialSkills prop
639
- useEffect(() => {
640
- if (initialSkills && initialSkills.length > 0) {
641
- setEnabledSkills(new Set(initialSkills));
642
- }
643
- }, [initialSkills]);
644
- // Helper to toggle MCP tool enabled state
645
- const toggleMcpTool = useCallback((serverId, toolName) => {
646
- setEnabledMcpTools(prev => {
647
- const newMap = new Map(prev);
648
- const serverTools = new Set(prev.get(serverId) || []);
649
- if (serverTools.has(toolName)) {
650
- serverTools.delete(toolName);
651
- }
652
- else {
653
- serverTools.add(toolName);
654
- }
655
- newMap.set(serverId, serverTools);
656
- return newMap;
657
- });
658
- }, []);
659
- // Helper to toggle all tools for a MCP server
660
- const toggleAllMcpServerTools = useCallback((serverId, allToolNames, enable) => {
661
- setEnabledMcpTools(prev => {
662
- const newMap = new Map(prev);
663
- if (enable) {
664
- newMap.set(serverId, new Set(allToolNames));
665
- }
666
- else {
667
- newMap.set(serverId, new Set());
668
- }
669
- return newMap;
670
- });
671
- }, []);
672
- // Helper to toggle skill enabled state
673
- const toggleSkill = useCallback((skillId) => {
674
- setEnabledSkills(prev => {
675
- const newSet = new Set(prev);
676
- if (newSet.has(skillId)) {
677
- newSet.delete(skillId);
678
- }
679
- else {
680
- newSet.add(skillId);
681
- }
682
- return newSet;
683
- });
684
- }, []);
685
- // Helper to toggle all skills
686
- const toggleAllSkills = useCallback((allSkillIds, enable) => {
687
- setEnabledSkills(enable ? new Set(allSkillIds) : new Set());
688
- }, []);
689
- // Get all enabled MCP tool names (for sending with requests)
690
- // Only counts tools from servers that are in mcpServers (if provided)
691
- const getEnabledMcpToolNames = useCallback(() => {
692
- const toolNames = [];
693
- enabledMcpTools.forEach((tools, serverId) => {
694
- // Filter by mcpServers if provided
695
- if (!mcpServers || mcpServers.some(s => s.id === serverId)) {
696
- tools.forEach(toolName => toolNames.push(toolName));
697
- }
698
- });
699
- return toolNames;
700
- }, [enabledMcpTools, mcpServers]);
701
- // Get all enabled skill IDs (for sending with requests)
702
- const getEnabledSkillIds = useCallback(() => {
703
- return Array.from(enabledSkills);
704
- }, [enabledSkills]);
705
- // Load messages from store on mount when useStoreMode is enabled
706
- useEffect(() => {
707
- if (useStoreMode) {
708
- const storeMessages = useChatStore.getState().messages;
709
- if (storeMessages.length > 0) {
710
- setDisplayItems(storeMessages);
711
- }
712
- }
713
- }, [useStoreMode]);
714
- // Track previous runtimeId to detect changes
715
- const prevRuntimeIdRef = useRef(undefined);
716
- // Clear displayItems and load messages when runtimeId changes
717
- // This ensures each agent runtime has isolated conversation history
718
- useEffect(() => {
719
- // If runtimeId changed, clear displayItems first
720
- if (runtimeId !== prevRuntimeIdRef.current) {
721
- prevRuntimeIdRef.current = runtimeId;
722
- // Clear current display items when switching runtime
723
- setDisplayItems([]);
724
- toolCallsRef.current.clear();
725
- // If no runtimeId, nothing more to do
726
- if (!runtimeId)
727
- return;
728
- }
729
- else {
730
- // runtimeId didn't change, skip
731
- return;
732
- }
733
- const store = useConversationStore.getState();
734
- // Check if we need to fetch (not already fetched and not currently fetching)
735
- if (!store.needsFetch(runtimeId)) {
736
- // Already fetched - load from in-memory store
737
- const storedMessages = store.getMessages(runtimeId);
738
- if (storedMessages.length > 0) {
739
- setDisplayItems(storedMessages);
740
- }
741
- setHistoryLoaded(true);
742
- return;
743
- }
744
- // Mark as fetching to prevent duplicate requests
745
- store.setFetching(runtimeId, true);
746
- // Build the history endpoint URL
747
- let endpoint = historyEndpoint ||
748
- (protocol?.endpoint ? `${protocol.endpoint}/api/v1/history` : null);
749
- if (!endpoint) {
750
- console.warn('[ChatBase] No history endpoint available for runtimeId:', runtimeId);
751
- store.markFetched(runtimeId);
752
- setHistoryLoaded(true);
753
- return;
754
- }
755
- // Append agent_id query param if the protocol has an agentId
756
- // and the URL doesn't already include one
757
- if (protocol?.agentId && !endpoint.includes('agent_id=')) {
758
- const separator = endpoint.includes('?') ? '&' : '?';
759
- endpoint = `${endpoint}${separator}agent_id=${encodeURIComponent(protocol.agentId)}`;
760
- }
761
- // Fetch conversation history from server
762
- const fetchHistory = async () => {
763
- try {
764
- const authToken = historyAuthToken || protocol?.authToken;
765
- const headers = {
766
- 'Content-Type': 'application/json',
767
- };
768
- if (authToken) {
769
- headers['Authorization'] = `Bearer ${authToken}`;
770
- }
771
- const response = await fetch(endpoint, {
772
- method: 'GET',
773
- headers,
774
- credentials: 'include',
775
- });
776
- if (!response.ok) {
777
- throw new Error(`Failed to fetch history: ${response.status} ${response.statusText}`);
778
- }
779
- const data = await response.json();
780
- // Map server history messages to ChatMessage format.
781
- // The server may return toolCalls in either the legacy {id, name, arguments}
782
- // shape or the correct ToolCallContentPart {toolCallId, toolName, args} shape.
783
- // Normalize both to ToolCallContentPart so the AG-UI adapter can serialize them.
784
- const messages = (data.messages || []).map((msg) => {
785
- if (msg.toolCalls && Array.isArray(msg.toolCalls)) {
786
- msg.toolCalls = msg.toolCalls.map((tc) => {
787
- // If already in ToolCallContentPart format, keep as-is
788
- if (tc.toolCallId && tc.toolName)
789
- return tc;
790
- // Legacy format: {id, name, arguments} → ToolCallContentPart
791
- let parsedArgs = tc.args ?? tc.arguments ?? {};
792
- if (typeof parsedArgs === 'string') {
793
- try {
794
- parsedArgs = JSON.parse(parsedArgs);
795
- }
796
- catch {
797
- parsedArgs = {};
798
- }
799
- }
800
- return {
801
- type: 'tool-call',
802
- toolCallId: tc.toolCallId ?? tc.id ?? tc.tool_call_id ?? '',
803
- toolName: tc.toolName ?? tc.name ?? tc.tool_name ?? '',
804
- args: parsedArgs,
805
- status: tc.status ?? 'completed',
806
- };
807
- });
808
- }
809
- return msg;
810
- });
811
- // Store in memory and convert to display items
812
- if (messages.length > 0) {
813
- store.setMessages(runtimeId, messages);
814
- // Convert to display items: expand tool calls, merge tool results
815
- const items = convertHistoryToDisplayItems(messages);
816
- setDisplayItems(items);
817
- }
818
- store.markFetched(runtimeId);
819
- setHistoryLoaded(true);
820
- }
821
- catch (err) {
822
- console.error('[ChatBase] Failed to fetch conversation history:', err);
823
- store.markFetched(runtimeId);
824
- setHistoryLoaded(true);
825
- }
826
- };
827
- fetchHistory();
828
- }, [
829
- runtimeId,
830
- historyEndpoint,
831
- historyAuthToken,
832
- protocol?.endpoint,
833
- protocol?.authToken,
834
- ]);
835
- // Keep in-memory store in sync with displayItems (for session persistence)
836
- useEffect(() => {
837
- if (runtimeId && displayItems.length > 0) {
838
- // Filter to only save ChatMessage items (not tool call items)
839
- const messagesToSave = displayItems.filter((item) => !isToolCallMessage(item));
840
- if (messagesToSave.length > 0) {
841
- useConversationStore.getState().setMessages(runtimeId, messagesToSave);
842
- }
843
- }
844
- }, [runtimeId, displayItems]);
845
- // Derived state
846
- const messages = displayItems.filter((item) => !isToolCallMessage(item));
847
- const ready = true;
848
- // Track previous message count to avoid unnecessary callbacks
849
- const prevMessageCountRef = useRef(0);
850
- // Notify parent when messages change (only when count actually changes)
851
- useEffect(() => {
852
- const currentCount = messages.length;
853
- if (currentCount !== prevMessageCountRef.current) {
854
- prevMessageCountRef.current = currentCount;
855
- onMessagesChange?.(messages);
856
- }
857
- }, [displayItems, onMessagesChange]); // Use displayItems instead of messages to avoid infinite loop
858
- // Padding based on compact mode
859
- const padding = compact ? 2 : 3;
860
- // Default avatar config
861
- const defaultAvatarConfig = {
862
- userAvatar: _jsx(PersonIcon, { size: 16 }),
863
- assistantAvatar: _jsx(AiAgentIcon, { colored: true, size: 16 }),
864
- showAvatars: true,
865
- avatarSize: 32,
866
- userAvatarBg: 'neutral.muted',
867
- assistantAvatarBg: 'accent.emphasis',
868
- ...avatarConfig,
869
- };
870
- // Initialize protocol adapter
871
- useEffect(() => {
872
- if (!protocol)
873
- return;
874
- const adapter = createProtocolAdapter(protocol);
875
- if (!adapter)
876
- return;
877
- adapterRef.current = adapter;
878
- setAdapterReady(true);
879
- // Subscribe to protocol events
880
- unsubscribeRef.current = adapter.subscribe((event) => {
881
- switch (event.type) {
882
- case 'message':
883
- // Update or create assistant message
884
- if (event.message) {
885
- const incomingId = event.message.id;
886
- const currentId = currentAssistantMessageRef.current?.id;
887
- const isNewMessage = !currentId || (incomingId && incomingId !== currentId);
888
- if (currentAssistantMessageRef.current && !isNewMessage) {
889
- // Update existing message (same ID)
890
- setDisplayItems(prev => {
891
- const newItems = [...prev];
892
- const idx = newItems.findIndex(item => !isToolCallMessage(item) &&
893
- item.id === currentAssistantMessageRef.current?.id);
894
- if (idx >= 0 && !isToolCallMessage(newItems[idx])) {
895
- newItems[idx] = {
896
- ...newItems[idx],
897
- content: event.message?.content ?? '',
898
- };
899
- }
900
- return newItems;
901
- });
902
- // Update message in store
903
- if (useStoreMode && currentAssistantMessageRef.current) {
904
- useChatStore
905
- .getState()
906
- .updateMessage(currentAssistantMessageRef.current.id, {
907
- content: event.message?.content ?? '',
908
- });
909
- }
910
- }
911
- else {
912
- // Create new assistant message (new ID or first message)
913
- const content = event.message.content;
914
- const contentStr = typeof content === 'string' ? content : (content ?? '');
915
- const newMessage = createAssistantMessage(typeof contentStr === 'string' ? contentStr : '');
916
- newMessage.id = event.message.id || newMessage.id;
917
- currentAssistantMessageRef.current = newMessage;
918
- // Guard against duplicates: if an item with the same ID
919
- // already exists (e.g. continuation arriving after
920
- // handleSend's finally cleared currentAssistantMessageRef),
921
- // update it in place instead of appending a second copy.
922
- setDisplayItems(prev => {
923
- const existingIdx = prev.findIndex(item => !isToolCallMessage(item) && item.id === newMessage.id);
924
- if (existingIdx >= 0) {
925
- const newItems = [...prev];
926
- newItems[existingIdx] = {
927
- ...newItems[existingIdx],
928
- content: event.message?.content ?? '',
929
- };
930
- return newItems;
931
- }
932
- return [...prev, newMessage];
933
- });
934
- // Add message to store (only if truly new)
935
- if (useStoreMode) {
936
- const existingInStore = useChatStore
937
- .getState()
938
- .messages.find(m => m.id === newMessage.id);
939
- if (existingInStore) {
940
- useChatStore.getState().updateMessage(newMessage.id, {
941
- content: event.message?.content ?? '',
942
- });
943
- }
944
- else {
945
- useChatStore.getState().addMessage(newMessage);
946
- }
947
- }
948
- }
949
- }
950
- break;
951
- case 'tool-call':
952
- // Handle tool call start or update
953
- if (event.toolCall) {
954
- const toolCallId = event.toolCall.toolCallId || generateMessageId();
955
- const toolName = event.toolCall.toolName;
956
- const args = event.toolCall.args || {};
957
- // Check if this tool call already exists (update args)
958
- if (toolCallsRef.current.has(toolCallId)) {
959
- const existingToolCall = toolCallsRef.current.get(toolCallId);
960
- if (existingToolCall) {
961
- // Merge new args with existing (update from TOOL_CALL_END)
962
- const updatedToolCall = {
963
- ...existingToolCall,
964
- args: {
965
- ...existingToolCall.args,
966
- ...args,
967
- },
968
- };
969
- toolCallsRef.current.set(toolCallId, updatedToolCall);
970
- // Always update displayItems (default ToolCallDisplay will be used if no custom renderer)
971
- setDisplayItems(prev => prev.map(item => isToolCallMessage(item) && item.toolCallId === toolCallId
972
- ? updatedToolCall
973
- : item));
974
- // Check if this is a frontend tool and we have complete args
975
- // Execute the tool handler if available
976
- const frontendTool = frontendTools?.find(t => t.name === toolName);
977
- const toolHandler = frontendTool?.handler;
978
- if (toolHandler && Object.keys(args).length > 0) {
979
- // Execute frontend tool
980
- pendingToolExecutionsRef.current++;
981
- (async () => {
982
- try {
983
- const result = await toolHandler(updatedToolCall.args);
984
- // Send result back to adapter
985
- if (adapterRef.current) {
986
- await adapterRef.current.sendToolResult(toolCallId, {
987
- toolCallId,
988
- success: true,
989
- result,
990
- });
991
- }
992
- // Update tool call with result
993
- const completedToolCall = {
994
- ...updatedToolCall,
995
- result,
996
- status: 'complete',
997
- };
998
- toolCallsRef.current.set(toolCallId, completedToolCall);
999
- setDisplayItems(prev => prev.map(item => isToolCallMessage(item) &&
1000
- item.toolCallId === toolCallId
1001
- ? completedToolCall
1002
- : item));
1003
- }
1004
- catch (err) {
1005
- console.error('[ChatBase] Frontend tool execution error:', err);
1006
- const errorToolCall = {
1007
- ...updatedToolCall,
1008
- status: 'error',
1009
- error: err.message,
1010
- };
1011
- toolCallsRef.current.set(toolCallId, errorToolCall);
1012
- setDisplayItems(prev => prev.map(item => isToolCallMessage(item) &&
1013
- item.toolCallId === toolCallId
1014
- ? errorToolCall
1015
- : item));
1016
- }
1017
- finally {
1018
- pendingToolExecutionsRef.current--;
1019
- if (pendingToolExecutionsRef.current <= 0) {
1020
- pendingToolExecutionsRef.current = 0;
1021
- setIsLoading(false);
1022
- setIsStreaming(false);
1023
- }
1024
- }
1025
- })();
1026
- }
1027
- }
1028
- }
1029
- else {
1030
- // New tool call - add it
1031
- const toolCallMsg = {
1032
- id: `tool-${toolCallId}`,
1033
- type: 'tool-call',
1034
- toolCallId,
1035
- toolName,
1036
- args,
1037
- status: 'executing',
1038
- };
1039
- toolCallsRef.current.set(toolCallId, toolCallMsg);
1040
- // Always add to displayItems (default ToolCallDisplay will be used if no custom renderer)
1041
- setDisplayItems(prev => [...prev, toolCallMsg]);
1042
- // Execute frontend tool if available and args are present
1043
- const frontendTool = frontendTools?.find(t => t.name === toolName);
1044
- const toolHandler = frontendTool?.handler;
1045
- if (toolHandler && Object.keys(args).length > 0) {
1046
- pendingToolExecutionsRef.current++;
1047
- (async () => {
1048
- try {
1049
- const result = await toolHandler(args);
1050
- // Send result back to adapter
1051
- if (adapterRef.current) {
1052
- await adapterRef.current.sendToolResult(toolCallId, {
1053
- toolCallId,
1054
- success: true,
1055
- result,
1056
- });
1057
- }
1058
- // Update tool call with result
1059
- const completedToolCall = {
1060
- ...toolCallMsg,
1061
- result,
1062
- status: 'complete',
1063
- };
1064
- toolCallsRef.current.set(toolCallId, completedToolCall);
1065
- setDisplayItems(prev => prev.map(item => isToolCallMessage(item) &&
1066
- item.toolCallId === toolCallId
1067
- ? completedToolCall
1068
- : item));
1069
- }
1070
- catch (err) {
1071
- console.error('[ChatBase] Frontend tool execution error:', err);
1072
- const errorToolCall = {
1073
- ...toolCallMsg,
1074
- status: 'error',
1075
- error: err.message,
1076
- };
1077
- toolCallsRef.current.set(toolCallId, errorToolCall);
1078
- setDisplayItems(prev => prev.map(item => isToolCallMessage(item) &&
1079
- item.toolCallId === toolCallId
1080
- ? errorToolCall
1081
- : item));
1082
- }
1083
- finally {
1084
- pendingToolExecutionsRef.current--;
1085
- if (pendingToolExecutionsRef.current <= 0) {
1086
- pendingToolExecutionsRef.current = 0;
1087
- setIsLoading(false);
1088
- setIsStreaming(false);
1089
- }
1090
- }
1091
- })();
1092
- }
1093
- }
1094
- }
1095
- break;
1096
- case 'tool-result':
1097
- // Handle tool result - always update status even without custom renderToolResult
1098
- if (event.toolResult) {
1099
- const toolCallId = event.toolResult.toolCallId;
1100
- if (toolCallId && toolCallsRef.current.has(toolCallId)) {
1101
- const existingToolCall = toolCallsRef.current.get(toolCallId);
1102
- if (existingToolCall) {
1103
- // Check if this is a human-in-the-loop tool (has steps in args)
1104
- // If so, keep status as 'executing' until user responds
1105
- const isHumanInTheLoop = existingToolCall.args &&
1106
- 'steps' in existingToolCall.args &&
1107
- Array.isArray(existingToolCall.args.steps);
1108
- // Extract rich error information from result if available
1109
- const resultData = event.toolResult.result;
1110
- let executionError;
1111
- let codeError;
1112
- let exitCode;
1113
- let hasError = !!event.toolResult.error;
1114
- if (resultData && typeof resultData === 'object') {
1115
- // Check for execution_error (infrastructure/sandbox errors)
1116
- if (resultData.execution_error &&
1117
- typeof resultData.execution_error === 'string') {
1118
- executionError = resultData.execution_error;
1119
- hasError = true;
1120
- }
1121
- // Check for code_error (Python exceptions)
1122
- if (resultData.code_error &&
1123
- typeof resultData.code_error === 'object') {
1124
- const ce = resultData.code_error;
1125
- codeError = {
1126
- name: ce.name || 'Error',
1127
- value: ce.value || 'Unknown error',
1128
- traceback: ce.traceback,
1129
- };
1130
- hasError = true;
1131
- }
1132
- // Check for exit_code (non-zero exit from sys.exit())
1133
- if ('exit_code' in resultData) {
1134
- const ec = resultData.exit_code;
1135
- exitCode = typeof ec === 'number' ? ec : null;
1136
- // Non-zero exit code counts as an error condition
1137
- if (exitCode != null && exitCode !== 0) {
1138
- hasError = true;
1139
- }
1140
- }
1141
- // Check for execution_ok flag
1142
- if ('execution_ok' in resultData &&
1143
- resultData.execution_ok === false) {
1144
- hasError = true;
1145
- }
1146
- }
1147
- const updatedToolCall = {
1148
- ...existingToolCall,
1149
- result: event.toolResult.result,
1150
- // Keep executing for HITL, otherwise mark complete/error
1151
- status: hasError
1152
- ? 'error'
1153
- : isHumanInTheLoop
1154
- ? 'executing'
1155
- : 'complete',
1156
- error: event.toolResult.error,
1157
- executionError,
1158
- codeError,
1159
- exitCode,
1160
- };
1161
- toolCallsRef.current.set(toolCallId, updatedToolCall);
1162
- setDisplayItems(prev => prev.map(item => isToolCallMessage(item) && item.toolCallId === toolCallId
1163
- ? updatedToolCall
1164
- : item));
1165
- }
1166
- }
1167
- }
1168
- break;
1169
- case 'state-update':
1170
- onStateUpdate?.(event.data);
1171
- // When we receive a state update, mark the last executing tool as complete
1172
- // This handles tools that return state events (STATE_SNAPSHOT/STATE_DELTA) instead of TOOL_CALL_RESULT
1173
- if (event.data) {
1174
- // Find any tool calls that are still in 'executing' status
1175
- const executingToolCalls = Array.from(toolCallsRef.current.entries()).filter(([_, tc]) => tc.status === 'executing');
1176
- // Mark the most recent executing tool as complete
1177
- if (executingToolCalls.length > 0) {
1178
- const [lastToolCallId, existingToolCall] = executingToolCalls[executingToolCalls.length - 1];
1179
- // Check if this is NOT a human-in-the-loop tool
1180
- const isHumanInTheLoop = existingToolCall.args &&
1181
- 'steps' in existingToolCall.args &&
1182
- Array.isArray(existingToolCall.args.steps);
1183
- if (!isHumanInTheLoop) {
1184
- // Extract result from state data if available
1185
- const stateData = event.data;
1186
- const result = stateData.weather ??
1187
- stateData.result ??
1188
- stateData.toolResult ??
1189
- stateData;
1190
- const updatedToolCall = {
1191
- ...existingToolCall,
1192
- result,
1193
- status: 'complete',
1194
- };
1195
- toolCallsRef.current.set(lastToolCallId, updatedToolCall);
1196
- setDisplayItems(prev => prev.map(item => isToolCallMessage(item) &&
1197
- item.toolCallId === lastToolCallId
1198
- ? updatedToolCall
1199
- : item));
1200
- }
1201
- }
1202
- }
1203
- break;
1204
- case 'error':
1205
- console.error('[ChatBase] Protocol error:', event.error);
1206
- setError(event.error || new Error('Unknown error'));
1207
- setIsLoading(false);
1208
- setIsStreaming(false);
1209
- break;
1210
- }
1211
- });
1212
- // Connect to protocol
1213
- adapter.connect().catch(console.error);
1214
- return () => {
1215
- unsubscribeRef.current?.();
1216
- adapterRef.current?.disconnect();
1217
- };
1218
- // protocolKey (JSON-serialised) replaces the protocol object reference so
1219
- // that parent re-renders producing a new-but-identical protocol object
1220
- // don't tear down and re-create the adapter mid-request.
1221
- // frontendTools is accessed via ref-like closure, not as reactive dependency
1222
- // eslint-disable-next-line react-hooks/exhaustive-deps
1223
- }, [protocolKey, renderToolResult, onStateUpdate, useStoreMode]);
1224
- // Auto-scroll to bottom
1225
- useEffect(() => {
1226
- messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
1227
- }, [displayItems]);
1228
- // Handle sending message in protocol mode or custom mode.
1229
- // An optional messageOverride bypasses the input state (used by pendingPrompt).
1230
- const handleSend = useCallback(async (messageOverride) => {
1231
- const messageContent = (messageOverride ?? input).trim();
1232
- if (!messageContent || isLoading)
1233
- return;
1234
- // Need either an adapter (protocol mode) or onSendMessage handler (custom mode)
1235
- if (!adapterRef.current && !onSendMessage)
1236
- return;
1237
- const userMessage = createUserMessage(messageContent);
1238
- const currentMessages = displayItems.filter((item) => !isToolCallMessage(item));
1239
- const allMessages = [...currentMessages, userMessage];
1240
- setDisplayItems(prev => [...prev, userMessage]);
1241
- setInput('');
1242
- setIsLoading(true);
1243
- setIsStreaming(true);
1244
- setError(null);
1245
- currentAssistantMessageRef.current = null;
1246
- // Persist user message to store if enabled
1247
- if (useStoreMode) {
1248
- useChatStore.getState().addMessage(userMessage);
1249
- }
1250
- try {
1251
- if (onSendMessage) {
1252
- // Custom mode: use the provided message handler
1253
- if (enableStreaming) {
1254
- // Streaming mode: create assistant message placeholder and stream updates
1255
- const assistantMessageId = generateMessageId();
1256
- const assistantMessage = createAssistantMessage('');
1257
- assistantMessage.id = assistantMessageId;
1258
- setDisplayItems(prev => [...prev, assistantMessage]);
1259
- currentAssistantMessageRef.current = assistantMessage;
1260
- if (useStoreMode) {
1261
- useChatStore.getState().addMessage(assistantMessage);
1262
- useChatStore.getState().startStreaming(assistantMessageId);
1263
- }
1264
- // Create abort controller for cancellation
1265
- abortControllerRef.current = new AbortController();
1266
- await onSendMessage(messageContent, allMessages, {
1267
- onChunk: (chunk) => {
1268
- // Append chunk to the assistant message
1269
- setDisplayItems(prev => prev.map(item => item.id === assistantMessageId
1270
- ? {
1271
- ...item,
1272
- content: item.content + chunk,
1273
- }
1274
- : item));
1275
- if (useStoreMode) {
1276
- useChatStore
1277
- .getState()
1278
- .appendToStream(assistantMessageId, chunk);
1279
- }
1280
- },
1281
- onComplete: (fullResponse) => {
1282
- // Update assistant message with final content
1283
- setDisplayItems(prev => prev.map(item => item.id === assistantMessageId
1284
- ? { ...item, content: fullResponse }
1285
- : item));
1286
- if (useStoreMode) {
1287
- useChatStore.getState().updateMessage(assistantMessageId, {
1288
- content: fullResponse,
1289
- });
1290
- useChatStore.getState().stopStreaming();
1291
- }
1292
- },
1293
- onError: (error) => {
1294
- // Update assistant message with error
1295
- const errorContent = `Error: ${error.message}`;
1296
- setDisplayItems(prev => prev.map(item => item.id === assistantMessageId
1297
- ? { ...item, content: errorContent }
1298
- : item));
1299
- if (useStoreMode) {
1300
- useChatStore.getState().updateMessage(assistantMessageId, {
1301
- content: errorContent,
1302
- });
1303
- useChatStore.getState().stopStreaming();
1304
- }
1305
- setError(error);
1306
- },
1307
- signal: abortControllerRef.current.signal,
1308
- });
1309
- }
1310
- else {
1311
- // Non-streaming mode: wait for full response
1312
- const response = await onSendMessage(messageContent, allMessages);
1313
- if (response) {
1314
- const assistantMessage = createAssistantMessage(response);
1315
- setDisplayItems(prev => [...prev, assistantMessage]);
1316
- if (useStoreMode) {
1317
- useChatStore.getState().addMessage(assistantMessage);
1318
- }
1319
- }
1320
- }
1321
- }
1322
- else if (adapterRef.current) {
1323
- // Protocol mode: use the adapter
1324
- // Convert frontend tools to AG-UI format (only serializable properties)
1325
- const toolsForRequest = (frontendTools || []).map(tool => ({
1326
- name: tool.name,
1327
- description: tool.description,
1328
- parameters: tool.parameters || { type: 'object', properties: {} },
1329
- }));
1330
- // Get enabled MCP tool names and skill IDs
1331
- const enabledMcpToolNames = getEnabledMcpToolNames();
1332
- const enabledSkillIds = getEnabledSkillIds();
1333
- await adapterRef.current.sendMessage(userMessage, {
1334
- threadId: threadIdRef.current,
1335
- messages: allMessages,
1336
- ...(selectedModel && { model: selectedModel }),
1337
- tools: toolsForRequest,
1338
- // Include enabled MCP tools as builtin_tools for backend
1339
- builtinTools: enabledMcpToolNames,
1340
- // Include enabled skills for backend
1341
- skills: enabledSkillIds,
1342
- // Include connected identities with access tokens (use ref to avoid infinite loops)
1343
- identities: connectedIdentitiesRef.current,
1344
- });
1345
- }
1346
- }
1347
- catch (err) {
1348
- if (err.name !== 'AbortError') {
1349
- console.error('[ChatBase] Send error:', err);
1350
- const errorMessage = createAssistantMessage(`Error: ${err.message}`);
1351
- setDisplayItems(prev => [...prev, errorMessage]);
1352
- setError(err);
1353
- }
1354
- }
1355
- finally {
1356
- // Only clear loading state if no frontend tool executions are
1357
- // still in flight. When tools are pending, sendToolResult will
1358
- // trigger a continuation that eventually clears isLoading.
1359
- if (pendingToolExecutionsRef.current <= 0) {
1360
- setIsLoading(false);
1361
- setIsStreaming(false);
1362
- }
1363
- currentAssistantMessageRef.current = null;
1364
- abortControllerRef.current = null;
1365
- }
1366
- }, [
1367
- input,
1368
- isLoading,
1369
- displayItems,
1370
- selectedModel,
1371
- frontendTools,
1372
- useStoreMode,
1373
- onSendMessage,
1374
- enableStreaming,
1375
- getEnabledMcpToolNames,
1376
- getEnabledSkillIds,
1377
- ]);
1378
- // Send the pending prompt once history is loaded and the
1379
- // adapter (or custom handler) is available.
1380
- useEffect(() => {
1381
- if (!pendingPrompt || pendingPromptSentRef.current)
1382
- return;
1383
- if (!historyLoaded)
1384
- return;
1385
- if (!adapterReady && !onSendMessage)
1386
- return;
1387
- pendingPromptSentRef.current = true;
1388
- // Use a microtask to ensure the adapter subscription is fully wired.
1389
- queueMicrotask(() => handleSend(pendingPrompt));
1390
- }, [pendingPrompt, historyLoaded, adapterReady, handleSend, onSendMessage]);
1391
- // Handle stop
1392
- const handleStop = useCallback(() => {
1393
- // Abort custom mode request
1394
- abortControllerRef.current?.abort();
1395
- // Disconnect protocol adapter
1396
- adapterRef.current?.disconnect();
1397
- // Stop streaming in store
1398
- if (useStoreMode) {
1399
- useChatStore.getState().stopStreaming();
1400
- }
1401
- // Reset pending tool counter so loading can clear
1402
- pendingToolExecutionsRef.current = 0;
1403
- setIsLoading(false);
1404
- setIsStreaming(false);
1405
- }, [useStoreMode]);
1406
- // Handle new chat
1407
- const handleNewChat = useCallback(() => {
1408
- setDisplayItems([]);
1409
- toolCallsRef.current.clear();
1410
- pendingToolExecutionsRef.current = 0;
1411
- setInput('');
1412
- threadIdRef.current = generateMessageId();
1413
- if (useStoreMode) {
1414
- clearStoreMessages();
1415
- }
1416
- // Clear from conversation store if runtimeId is provided
1417
- if (runtimeId) {
1418
- useConversationStore.getState().clearMessages(runtimeId);
1419
- }
1420
- onNewChat?.();
1421
- headerButtons?.onNewChat?.();
1422
- }, [clearStoreMessages, onNewChat, headerButtons, useStoreMode, runtimeId]);
1423
- // Handle clear
1424
- const handleClear = useCallback(() => {
1425
- if (window.confirm('Clear all messages?')) {
1426
- setDisplayItems([]);
1427
- toolCallsRef.current.clear();
1428
- if (useStoreMode) {
1429
- clearStoreMessages();
1430
- }
1431
- // Clear from conversation store if runtimeId is provided
1432
- if (runtimeId) {
1433
- useConversationStore.getState().clearMessages(runtimeId);
1434
- }
1435
- onClear?.();
1436
- headerButtons?.onClear?.();
1437
- }
1438
- }, [clearStoreMessages, onClear, headerButtons, useStoreMode, runtimeId]);
1439
- // Handle sandbox interrupt
1440
- const handleSandboxInterrupt = useCallback(async () => {
1441
- if (!protocol?.configEndpoint)
1442
- return;
1443
- const interruptUrl = `${getApiBaseFromConfig(protocol.configEndpoint)}/configure/sandbox/interrupt`;
1444
- try {
1445
- const headers = { 'Content-Type': 'application/json' };
1446
- if (protocol.authToken) {
1447
- headers['Authorization'] = `Bearer ${protocol.authToken}`;
1448
- }
1449
- await fetch(interruptUrl, { method: 'POST', headers });
1450
- // Refetch status immediately so the icon updates
1451
- sandboxStatusQuery.refetch();
1452
- }
1453
- catch (e) {
1454
- // Interrupt is best-effort
1455
- }
1456
- }, [protocol?.configEndpoint, protocol?.authToken, sandboxStatusQuery]);
1457
- // Not ready yet (store mode only)
1458
- if (!ready) {
1459
- return (_jsx(Box, { className: className, sx: {
1460
- display: 'flex',
1461
- flexDirection: 'column',
1462
- alignItems: 'center',
1463
- justifyContent: 'center',
1464
- height: '100%',
1465
- p: 4,
1466
- borderRadius: borderRadius,
1467
- bg: backgroundColor || 'canvas.default',
1468
- border: border,
1469
- boxShadow: boxShadow,
1470
- }, children: loadingState || (_jsxs(_Fragment, { children: [_jsx(Spinner, { size: "large" }), _jsx(Text, { sx: { mt: 3, color: 'fg.muted' }, children: "Initializing chat..." })] })) }));
1471
- }
1472
- // Render header with buttons
1473
- const renderHeader = () => {
1474
- if (!showHeader)
1475
- return null;
1476
- return (_jsx(Box, { sx: {
1477
- display: 'flex',
1478
- flexDirection: 'column',
1479
- borderBottom: '1px solid',
1480
- borderColor: 'border.default',
1481
- }, children: _jsxs(Box, { sx: {
1482
- display: 'flex',
1483
- alignItems: 'center',
1484
- justifyContent: 'space-between',
1485
- p: padding,
1486
- }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [brandIcon || _jsx(AiAgentIcon, { colored: true, size: 20 }), title && (_jsx(Heading, { as: "h3", sx: { fontSize: 2, fontWeight: 'semibold' }, children: title })), headerContent, showInformation && (_jsx(IconButton, { icon: InfoIcon, "aria-label": "Information", variant: "invisible", size: "small", onClick: onInformationClick }))] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: [sandboxStatus?.available &&
1487
- sandboxStatus?.sandbox_running &&
1488
- (sandboxStatus.is_executing ? (_jsx(IconButton, { icon: SquareFillIcon, "aria-label": "Interrupt code execution", variant: "invisible", size: "small", sx: { color: 'danger.fg' }, onClick: handleSandboxInterrupt })) : (_jsx(Box, { sx: {
1489
- display: 'flex',
1490
- alignItems: 'center',
1491
- justifyContent: 'center',
1492
- width: 28,
1493
- height: 28,
1494
- color: 'fg.subtle',
1495
- }, title: "Code sandbox ready", children: _jsx(CircleIcon, { size: 12 }) }))), headerButtons?.showNewChat && (_jsx(IconButton, { icon: PlusIcon, "aria-label": "New chat", variant: "invisible", size: "small", onClick: handleNewChat })), headerButtons?.showClear && messages.length > 0 && (_jsx(IconButton, { icon: TrashIcon, "aria-label": "Clear messages", variant: "invisible", size: "small", onClick: handleClear })), headerButtons?.showSettings && (_jsx(IconButton, { icon: GearIcon, "aria-label": "Settings", variant: "invisible", size: "small", onClick: headerButtons.onSettings })), chatViewMode && onChatViewModeChange && (_jsx(Box, { sx: {
1496
- display: 'inline-flex',
1497
- alignItems: 'center',
1498
- bg: 'neutral.muted',
1499
- borderRadius: '6px',
1500
- p: '2px',
1501
- gap: '1px',
1502
- }, children: [
1503
- {
1504
- mode: 'floating',
1505
- icon: CommentDiscussionIcon,
1506
- label: 'Full-height popup',
1507
- },
1508
- {
1509
- mode: 'floating-small',
1510
- icon: DeviceMobileIcon,
1511
- label: 'Floating popup',
1512
- },
1513
- {
1514
- mode: 'sidebar',
1515
- icon: SidebarExpandIcon,
1516
- label: 'Sidebar panel',
1517
- },
1518
- ].map(({ mode, icon: ModeIcon, label }) => (_jsx(Box, { as: "button", "aria-label": label, title: label, onClick: () => onChatViewModeChange(mode), sx: {
1519
- display: 'inline-flex',
1520
- alignItems: 'center',
1521
- justifyContent: 'center',
1522
- width: 26,
1523
- height: 24,
1524
- borderRadius: '4px',
1525
- border: 'none',
1526
- cursor: 'pointer',
1527
- bg: chatViewMode === mode
1528
- ? 'canvas.default'
1529
- : 'transparent',
1530
- boxShadow: chatViewMode === mode ? 'shadow.small' : 'none',
1531
- color: chatViewMode === mode ? 'fg.default' : 'fg.muted',
1532
- transition: 'all 0.15s ease',
1533
- '&:hover': {
1534
- color: 'fg.default',
1535
- bg: chatViewMode === mode
1536
- ? 'canvas.default'
1537
- : 'neutral.subtle',
1538
- },
1539
- }, children: _jsx(ModeIcon, { size: 14 }) }, mode))) })), headerActions] })] }) }));
1540
- };
1541
- // Render token usage bar between input and selectors
1542
- const renderTokenUsage = () => {
1543
- if (!showTokenUsage)
1544
- return null;
1545
- // Show bar when we have any context data (totalTokens > 0 means agent is active)
1546
- const hasContext = agentUsage && !agentUsage.error && agentUsage.totalTokens > 0;
1547
- const hasTurn = agentUsage?.turnUsage &&
1548
- (agentUsage.turnUsage.inputTokens > 0 ||
1549
- agentUsage.turnUsage.outputTokens > 0);
1550
- const hasSession = agentUsage?.sessionUsage &&
1551
- (agentUsage.sessionUsage.inputTokens > 0 ||
1552
- agentUsage.sessionUsage.outputTokens > 0);
1553
- if (!hasContext)
1554
- return null;
1555
- // Build pie chart data from distribution or fallback to fields
1556
- const usedTokens = agentUsage.totalTokens;
1557
- const windowTokens = agentUsage.contextWindow;
1558
- const freeTokens = Math.max(0, windowTokens - usedTokens);
1559
- const pct = windowTokens > 0 ? (usedTokens / windowTokens) * 100 : 0;
1560
- // Build category breakdown from distribution or individual fields
1561
- const categories = [];
1562
- if (agentUsage.distribution?.children?.length) {
1563
- const colorMap = {
1564
- 'System Prompts': '#8250df',
1565
- 'Tool Definitions': '#bf8700',
1566
- 'User Messages': '#0969da',
1567
- 'Assistant Messages': '#1a7f37',
1568
- 'Tool Usage': '#cf222e',
1569
- };
1570
- for (const child of agentUsage.distribution.children) {
1571
- categories.push({
1572
- name: child.name,
1573
- value: child.value,
1574
- color: colorMap[child.name] || '#6e7781',
1575
- });
1576
- }
1577
- }
1578
- else {
1579
- // Fallback: build from individual fields
1580
- if (agentUsage.systemPromptTokens > 0) {
1581
- categories.push({
1582
- name: 'System Prompts',
1583
- value: agentUsage.systemPromptTokens,
1584
- color: '#8250df',
1585
- });
1586
- }
1587
- if (agentUsage.toolTokens > 0) {
1588
- categories.push({
1589
- name: 'Tool Definitions',
1590
- value: agentUsage.toolTokens,
1591
- color: '#bf8700',
1592
- });
1593
- }
1594
- const messageTokens = (agentUsage.userMessageTokens || 0) +
1595
- (agentUsage.assistantMessageTokens || 0);
1596
- if (messageTokens > 0) {
1597
- categories.push({
1598
- name: 'Messages',
1599
- value: messageTokens,
1600
- color: '#0969da',
1601
- });
1602
- }
1603
- const toolResultTokens = (agentUsage.toolCallTokens || 0) + (agentUsage.toolReturnTokens || 0);
1604
- if (toolResultTokens > 0) {
1605
- categories.push({
1606
- name: 'Tool Results',
1607
- value: toolResultTokens,
1608
- color: '#cf222e',
1609
- });
1610
- }
1611
- }
1612
- // Tiny filled pie chart options
1613
- const pieColor = pct > 90 ? '#cf222e' : pct > 70 ? '#bf8700' : '#0969da';
1614
- const freeSliceColor = 'var(--bgColor-muted, #f6f8fa)';
1615
- const freeSliceOverlayColor = 'var(--borderColor-default, #d1d9e0)';
1616
- const miniPieOption = {
1617
- animation: false,
1618
- series: [
1619
- {
1620
- type: 'pie',
1621
- radius: [0, '90%'],
1622
- center: ['50%', '50%'],
1623
- silent: true,
1624
- label: { show: false },
1625
- labelLine: { show: false },
1626
- data: [
1627
- { value: usedTokens, itemStyle: { color: pieColor } },
1628
- { value: freeTokens, itemStyle: { color: freeSliceColor } },
1629
- ],
1630
- },
1631
- ],
1632
- };
1633
- // Overlay detail pie options (donut for category breakdown)
1634
- const overlayPieOption = {
1635
- animation: false,
1636
- series: [
1637
- {
1638
- type: 'pie',
1639
- radius: ['45%', '80%'],
1640
- center: ['50%', '50%'],
1641
- silent: true,
1642
- label: { show: false },
1643
- labelLine: { show: false },
1644
- itemStyle: {
1645
- borderColor: 'var(--bgColor-default, #ffffff)',
1646
- borderWidth: 1,
1647
- },
1648
- data: [
1649
- ...categories.map(c => ({
1650
- value: c.value,
1651
- itemStyle: { color: c.color },
1652
- })),
1653
- { value: freeTokens, itemStyle: { color: freeSliceOverlayColor } },
1654
- ],
1655
- },
1656
- ],
1657
- };
1658
- return (_jsxs(Box, { sx: {
1659
- display: 'flex',
1660
- alignItems: 'center',
1661
- justifyContent: 'flex-start',
1662
- gap: 2,
1663
- py: 1,
1664
- px: padding,
1665
- bg: 'canvas.subtle',
1666
- flexWrap: 'nowrap',
1667
- overflow: 'visible',
1668
- whiteSpace: 'nowrap',
1669
- minWidth: 0,
1670
- }, children: [_jsxs(Box, { sx: { position: 'relative', flexShrink: 0 }, onMouseEnter: () => {
1671
- if (hoverTimeoutRef.current)
1672
- clearTimeout(hoverTimeoutRef.current);
1673
- hoverTimeoutRef.current = setTimeout(() => setContextOverlayOpen(true), 150);
1674
- }, onMouseLeave: () => {
1675
- if (hoverTimeoutRef.current)
1676
- clearTimeout(hoverTimeoutRef.current);
1677
- hoverTimeoutRef.current = setTimeout(() => setContextOverlayOpen(false), 250);
1678
- }, children: [_jsx(Box, { ref: contextAnchorRef, sx: {
1679
- cursor: 'pointer',
1680
- width: 20,
1681
- height: 20,
1682
- display: 'flex',
1683
- alignItems: 'center',
1684
- justifyContent: 'center',
1685
- border: '1px solid',
1686
- borderColor: 'border.default',
1687
- borderRadius: '50%',
1688
- }, children: _jsx(ReactECharts, { option: miniPieOption, style: { width: 18, height: 18 }, opts: { renderer: 'svg' } }) }), contextOverlayOpen && (_jsxs(Box, { sx: {
1689
- position: 'absolute',
1690
- bottom: '100%',
1691
- left: 0,
1692
- mb: 1,
1693
- p: 3,
1694
- width: 260,
1695
- bg: 'canvas.overlay',
1696
- borderRadius: 2,
1697
- boxShadow: 'shadow.large',
1698
- border: '1px solid',
1699
- borderColor: 'border.default',
1700
- zIndex: 100,
1701
- }, children: [_jsx(Text, { sx: {
1702
- fontWeight: 'bold',
1703
- fontSize: 1,
1704
- color: 'fg.default',
1705
- display: 'block',
1706
- mb: 2,
1707
- }, children: "Context Window" }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted', display: 'block', mb: 1 }, children: [_jsx(Text, { as: "span", sx: { fontWeight: 'semibold', color: 'fg.default' }, children: formatTokenCount(usedTokens) }), ' / ', formatTokenCount(windowTokens), ' tokens'] }), _jsxs(Text, { sx: {
1708
- fontSize: 0,
1709
- color: pct > 90
1710
- ? 'danger.fg'
1711
- : pct > 70
1712
- ? 'attention.fg'
1713
- : 'fg.muted',
1714
- fontWeight: 'semibold',
1715
- display: 'block',
1716
- mb: 2,
1717
- }, children: ['• ', pct.toFixed(0), "%"] }), _jsx(Box, { sx: { display: 'flex', justifyContent: 'center', mb: 2 }, children: _jsx(ReactECharts, { option: overlayPieOption, style: { width: 80, height: 80 }, opts: { renderer: 'svg' } }) }), _jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 1 }, children: categories.map(cat => {
1718
- const catPct = usedTokens > 0 ? (cat.value / usedTokens) * 100 : 0;
1719
- return (_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Box, { sx: {
1720
- width: 8,
1721
- height: 8,
1722
- borderRadius: '50%',
1723
- bg: cat.color,
1724
- flexShrink: 0,
1725
- } }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', flex: 1 }, children: cat.name }), _jsxs(Text, { sx: {
1726
- fontSize: 0,
1727
- color: 'fg.default',
1728
- fontWeight: 'semibold',
1729
- }, children: [catPct.toFixed(1), "%"] })] }, cat.name));
1730
- }) }), pct > 70 && (_jsx(Text, { sx: {
1731
- fontSize: 0,
1732
- color: 'attention.fg',
1733
- display: 'block',
1734
- mt: 2,
1735
- fontStyle: 'italic',
1736
- }, children: "Quality may decline as limit nears." }))] }))] }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted', flexShrink: 0 }, children: [_jsx(Text, { as: "span", sx: { fontWeight: 'semibold', color: 'fg.default', fontSize: 0 }, children: formatTokenCount(agentUsage.totalTokens) }), ' / ', formatTokenCount(agentUsage.contextWindow), ' ctx'] }), hasSession && (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted', flexShrink: 0 }, children: ['· ', formatTokenCount(agentUsage.sessionUsage.inputTokens), _jsx(Text, { as: "span", sx: { color: 'success.fg', fontSize: 0 }, children: '▲' }), ' ', formatTokenCount(agentUsage.sessionUsage.outputTokens), _jsx(Text, { as: "span", sx: { color: 'attention.fg', fontSize: 0 }, children: '▼' })] })), hasTurn && (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted', flexShrink: 0 }, children: ['· turn ', formatTokenCount(agentUsage.turnUsage.inputTokens), _jsx(Text, { as: "span", sx: { color: 'success.fg', fontSize: 0 }, children: '▲' }), ' ', formatTokenCount(agentUsage.turnUsage.outputTokens), _jsx(Text, { as: "span", sx: { color: 'attention.fg', fontSize: 0 }, children: '▼' })] }))] }));
1737
- };
1738
- // Render empty state
1739
- const renderEmptyState = () => {
1740
- if (emptyState?.render) {
1741
- return emptyState.render();
1742
- }
1743
- // Handler for suggestion clicks
1744
- const handleSuggestionClick = (suggestion) => {
1745
- if (submitOnSuggestionClick) {
1746
- // Auto-submit the suggestion message
1747
- const userMessage = {
1748
- id: generateMessageId(),
1749
- role: 'user',
1750
- content: suggestion.message,
1751
- createdAt: new Date(),
1752
- };
1753
- setDisplayItems(prev => [...prev, userMessage]);
1754
- setIsLoading(true);
1755
- setIsStreaming(true);
1756
- // Convert frontend tools to AG-UI format (same as regular message send)
1757
- const toolsForSuggestion = (frontendTools || []).map(tool => ({
1758
- name: tool.name,
1759
- description: tool.description,
1760
- parameters: tool.parameters || { type: 'object', properties: {} },
1761
- }));
1762
- adapterRef.current
1763
- ?.sendMessage(userMessage, {
1764
- threadId: threadIdRef.current,
1765
- messages: [userMessage],
1766
- tools: toolsForSuggestion,
1767
- })
1768
- .catch(err => {
1769
- console.error('[ChatBase] Suggestion send error:', err);
1770
- setError(err instanceof Error ? err : new Error(String(err)));
1771
- })
1772
- .finally(() => {
1773
- setIsLoading(false);
1774
- setIsStreaming(false);
1775
- });
1776
- }
1777
- else {
1778
- // Just fill the input without submitting
1779
- setInput(suggestion.message);
1780
- setTimeout(() => {
1781
- inputRef.current?.focus();
1782
- }, 0);
1783
- }
1784
- };
1785
- return (_jsxs(Box, { sx: {
1786
- display: 'flex',
1787
- flexDirection: 'column',
1788
- alignItems: 'center',
1789
- justifyContent: 'center',
1790
- height: '100%',
1791
- p: 4,
1792
- color: 'fg.muted',
1793
- textAlign: 'center',
1794
- gap: 2,
1795
- }, children: [emptyState?.icon || brandIcon || _jsx(AiAgentIcon, { colored: true, size: 48 }), _jsx(Text, { sx: { fontSize: 2 }, children: emptyState?.title || 'Start a conversation' }), (emptyState?.subtitle || description) && (_jsx(Text, { sx: { fontSize: 1 }, children: emptyState?.subtitle || description })), suggestions && suggestions.length > 0 && (_jsx(LabelGroup, { sx: { mt: 2, justifyContent: 'center' }, children: suggestions.map((suggestion, index) => (_jsx(Label, { variant: "accent", sx: {
1796
- cursor: 'pointer',
1797
- '&:hover': {
1798
- bg: 'accent.emphasis',
1799
- color: 'var(--button-primary-fgColor-rest)',
1800
- borderColor: 'accent.emphasis',
1801
- },
1802
- }, onClick: () => handleSuggestionClick(suggestion), children: suggestion.title }, index))) }))] }));
1803
- };
1804
- // Render protocol mode messages with tool call support
1805
- const renderProtocolMessages = () => {
1806
- if (displayItems.length === 0) {
1807
- return renderEmptyState();
1808
- }
1809
- // Create respond callback for a tool call (human-in-the-loop)
1810
- const createRespondCallback = (toolCallId) => {
1811
- return async (result) => {
1812
- // Update tool call status to complete with the user's response
1813
- const existingToolCall = toolCallsRef.current.get(toolCallId);
1814
- if (existingToolCall && existingToolCall.status === 'executing') {
1815
- const updatedToolCall = {
1816
- ...existingToolCall,
1817
- result,
1818
- status: 'complete',
1819
- };
1820
- toolCallsRef.current.set(toolCallId, updatedToolCall);
1821
- setDisplayItems(prev => prev.map(item => isToolCallMessage(item) && item.toolCallId === toolCallId
1822
- ? updatedToolCall
1823
- : item));
1824
- // Send the user's response back to the agent as a new message
1825
- // This continues the conversation with the HITL response
1826
- if (adapterRef.current) {
1827
- // Format the response as a clear text message the agent can understand
1828
- let responseText;
1829
- if (typeof result === 'string') {
1830
- responseText = result;
1831
- }
1832
- else if (result &&
1833
- typeof result === 'object' &&
1834
- 'accepted' in result) {
1835
- const hitlResult = result;
1836
- if (hitlResult.accepted) {
1837
- const stepDescriptions = hitlResult.steps?.map(s => s.description).join(', ') || '';
1838
- responseText = stepDescriptions
1839
- ? `I confirm and approve the following steps: ${stepDescriptions}`
1840
- : 'I confirm and approve the plan.';
1841
- }
1842
- else {
1843
- responseText =
1844
- 'I reject this plan. Please suggest something else.';
1845
- }
1846
- }
1847
- else {
1848
- responseText = JSON.stringify(result, null, 2);
1849
- }
1850
- // Create a user message with the HITL response
1851
- const userMessage = {
1852
- id: generateMessageId(),
1853
- role: 'user',
1854
- content: responseText,
1855
- createdAt: new Date(),
1856
- };
1857
- setIsLoading(true);
1858
- setIsStreaming(true);
1859
- try {
1860
- // Get all chat messages for context
1861
- const allMessages = displayItems.filter((item) => !isToolCallMessage(item));
1862
- await adapterRef.current.sendMessage(userMessage, {
1863
- threadId: threadIdRef.current,
1864
- messages: [...allMessages, userMessage],
1865
- });
1866
- }
1867
- catch (err) {
1868
- console.error('[ChatBase] HITL respond error:', err);
1869
- }
1870
- finally {
1871
- setIsLoading(false);
1872
- setIsStreaming(false);
1873
- }
1874
- }
1875
- }
1876
- };
1877
- };
1878
- // Check if there are tool calls being rendered
1879
- // This is used to hide duplicate assistant text when UI is shown
1880
- const renderedToolCallIds = new Set();
1881
- displayItems.forEach(item => {
1882
- if (isToolCallMessage(item)) {
1883
- if (renderToolResult) {
1884
- // Check if custom renderer produces a rendered UI
1885
- const rendered = renderToolResult({
1886
- toolCallId: item.toolCallId,
1887
- toolName: item.toolName,
1888
- name: item.toolName,
1889
- args: item.args,
1890
- result: item.result,
1891
- status: item.status,
1892
- error: item.error,
1893
- });
1894
- if (rendered !== null && rendered !== undefined) {
1895
- renderedToolCallIds.add(item.toolCallId);
1896
- }
1897
- }
1898
- else {
1899
- // Default display always renders tool calls
1900
- renderedToolCallIds.add(item.toolCallId);
1901
- }
1902
- }
1903
- });
1904
- const hasRenderedToolCall = renderedToolCallIds.size > 0;
1905
- return (_jsxs(_Fragment, { children: [displayItems.map((item, index) => {
1906
- // Render tool call
1907
- if (isToolCallMessage(item)) {
1908
- // Only provide respond callback when status is 'executing'
1909
- const respond = item.status === 'executing'
1910
- ? createRespondCallback(item.toolCallId)
1911
- : undefined;
1912
- // Use custom renderToolResult if provided, otherwise use default display
1913
- const toolUI = renderToolResult ? (renderToolResult({
1914
- toolCallId: item.toolCallId,
1915
- toolName: item.toolName,
1916
- name: item.toolName,
1917
- args: item.args,
1918
- result: item.result,
1919
- status: item.status,
1920
- error: item.error,
1921
- respond,
1922
- })) : (_jsx(ToolCallDisplay, { toolCallId: item.toolCallId, toolName: item.toolName, args: item.args, result: item.result, status: item.status, error: item.error, executionError: item.executionError, codeError: item.codeError, exitCode: item.exitCode }));
1923
- // Skip if custom render returns null/undefined
1924
- if (toolUI === null || toolUI === undefined)
1925
- return null;
1926
- return (_jsx(Box, { sx: {
1927
- display: 'flex',
1928
- flexDirection: 'column',
1929
- alignItems: 'flex-start',
1930
- maxWidth: '95%',
1931
- px: padding,
1932
- py: 1,
1933
- }, children: toolUI }, item.id));
1934
- }
1935
- // Render regular chat message
1936
- const message = item;
1937
- const isUser = message.role === 'user';
1938
- // Skip assistant messages when hideMessagesAfterToolUI is enabled and there's a rendered tool call
1939
- if (!isUser && hideMessagesAfterToolUI && hasRenderedToolCall) {
1940
- // Get message text safely
1941
- const messageText = getMessageText(message);
1942
- // Check if this assistant message follows a rendered tool call
1943
- const prevIndex = index - 1;
1944
- if (prevIndex >= 0) {
1945
- const prevItem = displayItems[prevIndex];
1946
- if (isToolCallMessage(prevItem) &&
1947
- renderedToolCallIds.has(prevItem.toolCallId)) {
1948
- // Skip this assistant message as it describes the tool result
1949
- return null;
1950
- }
1951
- }
1952
- // Also check for HITL-specific patterns (step descriptions)
1953
- const hitlToolCall = displayItems.find(item => isToolCallMessage(item) &&
1954
- renderedToolCallIds.has(item.toolCallId) &&
1955
- item.args &&
1956
- 'steps' in item.args &&
1957
- Array.isArray(item.args.steps));
1958
- if (hitlToolCall && messageText) {
1959
- const steps = hitlToolCall.args.steps;
1960
- // Check if message contains step descriptions or step-like patterns
1961
- const hasStepContent = steps.some(step => step.description &&
1962
- messageText
1963
- .toLowerCase()
1964
- .includes(step.description.toLowerCase().slice(0, 20))) ||
1965
- // Also hide if message contains numbered list patterns
1966
- /^\s*(\d+\.\s|[-*]\s|\*\*)/.test(messageText) ||
1967
- messageText.includes('**Enabled**') ||
1968
- messageText.includes('steps below');
1969
- if (hasStepContent) {
1970
- return null;
1971
- }
1972
- }
1973
- }
1974
- return (_jsx(Box, { sx: {
1975
- display: 'flex',
1976
- flexDirection: 'column',
1977
- alignItems: isUser ? 'flex-end' : 'flex-start',
1978
- px: padding,
1979
- py: 1,
1980
- }, children: _jsxs(Box, { sx: {
1981
- display: 'flex',
1982
- gap: 2,
1983
- flexDirection: isUser ? 'row-reverse' : 'row',
1984
- alignItems: 'flex-start',
1985
- }, children: [defaultAvatarConfig.showAvatars && (_jsx(Box, { sx: {
1986
- width: defaultAvatarConfig.avatarSize,
1987
- height: defaultAvatarConfig.avatarSize,
1988
- borderRadius: '50%',
1989
- bg: isUser
1990
- ? defaultAvatarConfig.userAvatarBg
1991
- : defaultAvatarConfig.assistantAvatarBg,
1992
- color: isUser ? 'fg.default' : 'fg.onEmphasis',
1993
- display: 'flex',
1994
- alignItems: 'center',
1995
- justifyContent: 'center',
1996
- flexShrink: 0,
1997
- }, children: isUser
1998
- ? defaultAvatarConfig.userAvatar
1999
- : defaultAvatarConfig.assistantAvatar })), _jsx(Box, { sx: {
2000
- maxWidth: '85%',
2001
- p: 2,
2002
- borderRadius: 2,
2003
- backgroundColor: isUser
2004
- ? 'accent.emphasis'
2005
- : 'canvas.subtle',
2006
- color: isUser ? 'fg.onEmphasis' : 'fg.default',
2007
- ...streamdownCodeBlockStyles,
2008
- }, children: isUser ? (_jsx(Text, { sx: {
2009
- fontSize: 1,
2010
- whiteSpace: 'pre-wrap',
2011
- wordBreak: 'break-word',
2012
- }, children: getMessageText(message) })) : (_jsx(Box, { sx: streamdownMarkdownStyles, children: _jsx(Streamdown, { children: getMessageText(message) || (isStreaming ? '...' : '') }) })) })] }) }, message.id));
2013
- }), showLoadingIndicator && (isLoading || isStreaming) && (_jsx(Box, { sx: {
2014
- display: 'flex',
2015
- alignItems: 'flex-start',
2016
- px: padding,
2017
- py: 1,
2018
- }, children: _jsxs(Box, { sx: {
2019
- display: 'flex',
2020
- gap: 2,
2021
- alignItems: 'flex-start',
2022
- }, children: [defaultAvatarConfig.showAvatars && (_jsx(Box, { sx: {
2023
- width: defaultAvatarConfig.avatarSize,
2024
- height: defaultAvatarConfig.avatarSize,
2025
- borderRadius: '50%',
2026
- bg: defaultAvatarConfig.assistantAvatarBg,
2027
- color: 'fg.onEmphasis',
2028
- display: 'flex',
2029
- alignItems: 'center',
2030
- justifyContent: 'center',
2031
- flexShrink: 0,
2032
- }, children: defaultAvatarConfig.assistantAvatar })), _jsxs(Box, { sx: {
2033
- display: 'flex',
2034
- alignItems: 'center',
2035
- gap: '4px',
2036
- p: 2,
2037
- borderRadius: 2,
2038
- bg: 'canvas.subtle',
2039
- minHeight: '32px',
2040
- }, children: [_jsx(Box, { sx: {
2041
- width: '8px',
2042
- height: '8px',
2043
- borderRadius: '50%',
2044
- bg: 'fg.muted',
2045
- animation: 'typingPulse 1.4s ease-in-out infinite',
2046
- '@keyframes typingPulse': {
2047
- '0%, 60%, 100%': {
2048
- transform: 'scale(0.6)',
2049
- opacity: 0.4,
2050
- },
2051
- '30%': {
2052
- transform: 'scale(1)',
2053
- opacity: 1,
2054
- },
2055
- },
2056
- } }), _jsx(Box, { sx: {
2057
- width: '8px',
2058
- height: '8px',
2059
- borderRadius: '50%',
2060
- bg: 'fg.muted',
2061
- animation: 'typingPulse 1.4s ease-in-out infinite',
2062
- animationDelay: '0.2s',
2063
- '@keyframes typingPulse': {
2064
- '0%, 60%, 100%': {
2065
- transform: 'scale(0.6)',
2066
- opacity: 0.4,
2067
- },
2068
- '30%': {
2069
- transform: 'scale(1)',
2070
- opacity: 1,
2071
- },
2072
- },
2073
- } }), _jsx(Box, { sx: {
2074
- width: '8px',
2075
- height: '8px',
2076
- borderRadius: '50%',
2077
- bg: 'fg.muted',
2078
- animation: 'typingPulse 1.4s ease-in-out infinite',
2079
- animationDelay: '0.4s',
2080
- '@keyframes typingPulse': {
2081
- '0%, 60%, 100%': {
2082
- transform: 'scale(0.6)',
2083
- opacity: 0.4,
2084
- },
2085
- '30%': {
2086
- transform: 'scale(1)',
2087
- opacity: 1,
2088
- },
2089
- },
2090
- } })] })] }) })), _jsx("div", { ref: messagesEndRef })] }));
2091
- };
2092
- // Render input prompt
2093
- const renderInputPrompt = () => {
2094
- const availableTools = configQuery.data?.builtinTools || [];
2095
- const models = availableModels || configQuery.data?.models || [];
2096
- return (_jsxs(Box, { children: [_jsx(InputPrompt, { placeholder: placeholder || 'Type a message...', isLoading: isLoading, onSend: () => handleSend(), onStop: handleStop, autoFocus: autoFocus, focusTrigger: focusTrigger, padding: padding, value: input, onChange: setInput }), renderTokenUsage(), (showModelSelector || showToolsMenu || showSkillsMenu) &&
2097
- (configQuery.data || skillsQuery.data) && (_jsxs(Box, { sx: {
2098
- display: 'flex',
2099
- gap: 2,
2100
- px: padding,
2101
- py: 1,
2102
- borderTop: '1px solid',
2103
- borderColor: 'border.default',
2104
- alignItems: 'center',
2105
- bg: 'canvas.subtle',
2106
- }, children: [showToolsMenu && (_jsxs(ActionMenu, { children: [_jsx(ActionMenu.Anchor, { children: _jsx(Button, { type: "button", variant: "invisible", size: "small", leadingVisual: ToolsIcon, children: _jsxs(Text, { sx: { fontSize: 0 }, children: ["Tools", getEnabledMcpToolNames().length > 0 &&
2107
- ` (${getEnabledMcpToolNames().length})`] }) }) }), _jsx(ActionMenu.Overlay, { side: "outside-top", align: "start", width: "large", children: _jsx(Box, { sx: {
2108
- maxHeight: '60vh',
2109
- overflowY: 'auto',
2110
- }, children: _jsxs(ActionList, { children: [codemodeEnabled && (_jsx(ActionList.Group, { title: "Codemode", children: _jsx(ActionList.Item, { disabled: true, children: _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "MCP tools are accessible via Codemode meta-tools (search_tools, list_tool_names, execute_code)." }) }) })), configQuery.data?.mcpServers &&
2111
- configQuery.data.mcpServers.length > 0 ? (
2112
- // Filter to only show selected servers (if mcpServers was provided)
2113
- // If mcpServers is empty array, show no servers
2114
- configQuery.data.mcpServers.filter(server => !mcpServers || isServerSelected(server)).length > 0 ? (configQuery.data.mcpServers
2115
- .filter(server => !mcpServers || isServerSelected(server))
2116
- .map(server => {
2117
- const serverTools = enabledMcpTools.get(server.id);
2118
- const allToolNames = server.tools.map(t => t.name);
2119
- const enabledCount = serverTools?.size ?? 0;
2120
- const allEnabled = enabledCount === allToolNames.length &&
2121
- allToolNames.length > 0;
2122
- return (_jsxs(ActionList.Group, { title: `${server.name}${server.isAvailable ? '' : ' (unavailable)'}`, children: [server.isAvailable &&
2123
- server.tools.length > 0 && (_jsxs(Box, { sx: {
2124
- display: 'flex',
2125
- alignItems: 'center',
2126
- justifyContent: 'space-between',
2127
- px: 3,
2128
- py: 2,
2129
- borderBottom: '1px solid',
2130
- borderColor: 'border.muted',
2131
- }, children: [_jsxs(Text, { id: `toggle-all-${server.id}`, sx: {
2132
- fontSize: 0,
2133
- fontWeight: 'semibold',
2134
- color: 'fg.muted',
2135
- }, children: ["Enable all (", enabledCount, "/", allToolNames.length, ")"] }), _jsx(ToggleSwitch, { size: "small", checked: allEnabled, onClick: () => toggleAllMcpServerTools(server.id, allToolNames, !allEnabled), "aria-labelledby": `toggle-all-${server.id}` })] })), server.isAvailable &&
2136
- server.tools.length > 0 ? (server.tools.map(tool => {
2137
- const isEnabled = serverTools?.has(tool.name) ?? false;
2138
- return (_jsxs(Box, { sx: {
2139
- display: 'flex',
2140
- alignItems: 'center',
2141
- justifyContent: 'space-between',
2142
- px: 3,
2143
- py: 2,
2144
- '&:hover': {
2145
- backgroundColor: 'canvas.subtle',
2146
- },
2147
- }, children: [_jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [_jsx(Text, { id: `toggle-tool-${server.id}-${tool.name}`, sx: { fontWeight: 'semibold' }, children: tool.name }), tool.description && (_jsx(Text, { sx: {
2148
- display: 'block',
2149
- fontSize: 0,
2150
- color: 'fg.muted',
2151
- overflow: 'hidden',
2152
- textOverflow: 'ellipsis',
2153
- whiteSpace: 'nowrap',
2154
- }, children: tool.description }))] }), _jsx(ToggleSwitch, { size: "small", checked: isEnabled, onClick: () => toggleMcpTool(server.id, tool.name), "aria-labelledby": `toggle-tool-${server.id}-${tool.name}` })] }, `${server.id}-${tool.name}`));
2155
- })) : server.isAvailable ? (_jsx(ActionList.Item, { disabled: true, children: _jsx(Text, { sx: {
2156
- color: 'fg.muted',
2157
- fontStyle: 'italic',
2158
- }, children: "No tools discovered" }) })) : (_jsx(ActionList.Item, { disabled: true, children: _jsx(Text, { sx: {
2159
- color: 'fg.muted',
2160
- fontStyle: 'italic',
2161
- }, children: "Server unavailable" }) }))] }, server.id));
2162
- })) : (_jsx(ActionList.Group, { title: "MCP Servers", children: _jsx(ActionList.Item, { disabled: true, children: _jsx(Text, { sx: {
2163
- color: 'fg.muted',
2164
- fontStyle: 'italic',
2165
- }, children: "No MCP servers selected" }) }) }))) : (_jsx(ActionList.Group, { title: "Available Tools", children: availableTools.length > 0 ? (availableTools.map(tool => (_jsxs(ActionList.Item, { disabled: true, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(Box, { sx: {
2166
- width: 8,
2167
- height: 8,
2168
- borderRadius: '50%',
2169
- backgroundColor: 'success.emphasis',
2170
- } }) }), tool.name] }, tool.id)))) : (_jsx(ActionList.Item, { disabled: true, children: _jsx(Text, { sx: {
2171
- color: 'fg.muted',
2172
- fontStyle: 'italic',
2173
- }, children: "No tools available" }) })) }))] }) }) })] })), showSkillsMenu && (_jsxs(ActionMenu, { children: [_jsx(ActionMenu.Anchor, { children: _jsx(Button, { type: "button", variant: "invisible", size: "small", leadingVisual: BriefcaseIcon, children: _jsxs(Text, { sx: { fontSize: 0 }, children: ["Skills", enabledSkills.size > 0 && ` (${enabledSkills.size})`] }) }) }), _jsx(ActionMenu.Overlay, { side: "outside-top", align: "start", width: "large", children: _jsx(Box, { sx: {
2174
- maxHeight: '60vh',
2175
- overflowY: 'auto',
2176
- }, children: _jsx(ActionList, { children: skillsQuery.isLoading ? (_jsx(ActionList.Item, { disabled: true, children: _jsx(Text, { sx: { color: 'fg.muted' }, children: "Loading skills..." }) })) : skillsQuery.data?.skills &&
2177
- skillsQuery.data.skills.length > 0 ? (_jsxs(_Fragment, { children: [_jsxs(Box, { sx: {
2178
- display: 'flex',
2179
- alignItems: 'center',
2180
- justifyContent: 'space-between',
2181
- px: 3,
2182
- py: 2,
2183
- borderBottom: '1px solid',
2184
- borderColor: 'border.muted',
2185
- }, children: [_jsxs(Text, { id: "toggle-all-skills", sx: {
2186
- fontSize: 0,
2187
- fontWeight: 'semibold',
2188
- color: 'fg.muted',
2189
- }, children: ["Enable all (", enabledSkills.size, "/", skillsQuery.data.skills.length, ")"] }), _jsx(ToggleSwitch, { size: "small", checked: enabledSkills.size ===
2190
- skillsQuery.data.skills.length, onClick: () => toggleAllSkills(skillsQuery.data.skills.map(s => s.id), enabledSkills.size !==
2191
- skillsQuery.data.skills.length), "aria-labelledby": "toggle-all-skills" })] }), skillsQuery.data.skills.map(skill => (_jsxs(Box, { sx: {
2192
- display: 'flex',
2193
- alignItems: 'center',
2194
- justifyContent: 'space-between',
2195
- px: 3,
2196
- py: 2,
2197
- '&:hover': {
2198
- backgroundColor: 'canvas.subtle',
2199
- },
2200
- }, children: [_jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [_jsx(Text, { id: `toggle-skill-${skill.id}`, sx: { fontWeight: 'semibold' }, children: skill.name }), skill.description && (_jsx(Text, { sx: {
2201
- display: 'block',
2202
- fontSize: 0,
2203
- color: 'fg.muted',
2204
- overflow: 'hidden',
2205
- textOverflow: 'ellipsis',
2206
- whiteSpace: 'nowrap',
2207
- }, children: skill.description }))] }), _jsx(ToggleSwitch, { size: "small", checked: enabledSkills.has(skill.id), onClick: () => toggleSkill(skill.id), "aria-labelledby": `toggle-skill-${skill.id}` })] }, skill.id)))] })) : (_jsx(ActionList.Item, { disabled: true, children: _jsx(Text, { sx: { color: 'fg.muted', fontStyle: 'italic' }, children: "No skills available" }) })) }) }) })] })), showModelSelector && models.length > 0 && selectedModel && (_jsxs(Box, { sx: {
2208
- display: 'flex',
2209
- flexDirection: 'column',
2210
- alignItems: 'flex-end',
2211
- }, children: [_jsxs(ActionMenu, { children: [_jsx(ActionMenu.Anchor, { children: _jsx(Button, { type: "button", variant: "invisible", size: "small", leadingVisual: AiModelIcon, disabled: isA2AProtocol, sx: isA2AProtocol
2212
- ? { opacity: 0.5, cursor: 'not-allowed' }
2213
- : undefined, children: _jsx(Text, { sx: { fontSize: 0 }, children: models.find(m => m.id === selectedModel)?.name ||
2214
- 'Select Model' }) }) }), _jsx(ActionMenu.Overlay, { side: "outside-top", align: "end", children: _jsx(ActionList, { selectionVariant: "single", children: models.map(modelItem => (_jsxs(ActionList.Item, { selected: selectedModel === modelItem.id, onSelect: () => setSelectedModel(modelItem.id), disabled: modelItem.isAvailable === false || isA2AProtocol, sx: modelItem.isAvailable === false
2215
- ? { color: 'fg.muted' }
2216
- : undefined, children: [modelItem.name, modelItem.isAvailable === false && (_jsx(ActionList.Description, { variant: "block", children: "Missing API key" }))] }, modelItem.id))) }) })] }), isA2AProtocol && (_jsx(Text, { sx: { fontSize: 0, color: 'attention.fg', mt: 1 }, children: "A2A: Model set by agent config" }))] }))] }))] }));
2217
- };
2218
- return (_jsxs(Box, { className: className, sx: {
2219
- display: 'flex',
2220
- flexDirection: 'column',
2221
- height: '100%',
2222
- bg: backgroundColor || 'canvas.default',
2223
- borderRadius: borderRadius,
2224
- border: border,
2225
- boxShadow: boxShadow,
2226
- overflow: 'hidden',
2227
- }, children: [renderHeader(), showErrors && error && (_jsxs(Box, { sx: {
2228
- display: 'flex',
2229
- alignItems: 'center',
2230
- gap: 2,
2231
- p: padding,
2232
- bg: 'danger.subtle',
2233
- borderBottom: '1px solid',
2234
- borderColor: 'danger.muted',
2235
- }, children: [_jsx(AlertIcon, { size: 16 }), _jsx(Text, { sx: { color: 'danger.fg', fontSize: 1 }, children: error.message })] })), _jsx(Box, { sx: { flex: 1, flexGrow: 1, overflow: 'auto', bg: 'canvas.default' }, children: children ? (children) : (_jsx(Box, { sx: {
2236
- display: 'flex',
2237
- flexDirection: 'column',
2238
- minHeight: '100%',
2239
- bg: 'canvas.default',
2240
- }, children: renderProtocolMessages() })) }), footerContent, showInput && renderInputPrompt(), showPoweredBy && _jsx(PoweredByTag, { ...poweredByProps })] }));
2241
- }
2242
- export default ChatBase;