@datalayer/agent-runtimes 0.0.5 → 0.0.8

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 (119) hide show
  1. package/README.md +150 -22
  2. package/lib/components/chat/components/AgentDetails.d.ts +15 -2
  3. package/lib/components/chat/components/AgentDetails.js +9 -93
  4. package/lib/components/chat/components/AgentIdentity.d.ts +92 -0
  5. package/lib/components/chat/components/AgentIdentity.js +318 -0
  6. package/lib/components/chat/components/Chat.d.ts +24 -1
  7. package/lib/components/chat/components/Chat.js +41 -19
  8. package/lib/components/chat/components/ChatFloating.d.ts +6 -1
  9. package/lib/components/chat/components/ChatFloating.js +12 -6
  10. package/lib/components/chat/components/ContextDistribution.d.ts +47 -0
  11. package/lib/components/chat/components/ContextDistribution.js +146 -0
  12. package/lib/components/chat/components/ContextUsage.d.ts +33 -0
  13. package/lib/components/chat/components/ContextUsage.js +127 -0
  14. package/lib/components/chat/components/base/ChatBase.d.ts +51 -1
  15. package/lib/components/chat/components/base/ChatBase.js +278 -74
  16. package/lib/components/chat/components/display/ToolCallDisplay.d.ts +16 -2
  17. package/lib/components/chat/components/display/ToolCallDisplay.js +148 -6
  18. package/lib/components/chat/components/display/index.d.ts +1 -1
  19. package/lib/components/chat/components/display/index.js +1 -1
  20. package/lib/components/chat/components/elements/ChatInputPrompt.d.ts +12 -1
  21. package/lib/components/chat/components/elements/ChatInputPrompt.js +8 -3
  22. package/lib/components/chat/components/index.d.ts +3 -0
  23. package/lib/components/chat/components/index.js +3 -0
  24. package/lib/components/chat/components/parts/ToolPart.d.ts +1 -1
  25. package/lib/components/chat/components/parts/ToolPart.js +142 -6
  26. package/lib/components/chat/index.d.ts +1 -1
  27. package/lib/components/chat/index.js +1 -1
  28. package/lib/components/chat/protocols/A2AAdapter.d.ts +9 -0
  29. package/lib/components/chat/protocols/A2AAdapter.js +13 -2
  30. package/lib/components/chat/protocols/ACPAdapter.d.ts +9 -0
  31. package/lib/components/chat/protocols/ACPAdapter.js +13 -2
  32. package/lib/components/chat/protocols/AGUIAdapter.d.ts +9 -0
  33. package/lib/components/chat/protocols/AGUIAdapter.js +19 -1
  34. package/lib/components/chat/protocols/VercelAIAdapter.d.ts +7 -0
  35. package/lib/components/chat/protocols/VercelAIAdapter.js +19 -0
  36. package/lib/components/chat/types/execution.d.ts +78 -0
  37. package/lib/components/chat/types/execution.js +64 -0
  38. package/lib/components/chat/types/index.d.ts +1 -0
  39. package/lib/components/chat/types/index.js +1 -0
  40. package/lib/components/chat/types/protocol.d.ts +9 -0
  41. package/lib/components/ui/pagination.d.ts +2 -2
  42. package/lib/components/ui/pagination.js +4 -4
  43. package/lib/components/ui/resizable.d.ts +4 -4
  44. package/lib/components/ui/resizable.js +4 -4
  45. package/lib/examples/A2UiRestaurantExample.js +2 -2
  46. package/lib/examples/AgUiAgenticExample.js +2 -2
  47. package/lib/examples/AgUiBackendToolRenderingExample.js +2 -2
  48. package/lib/examples/AgUiHaikuGenUIExample.js +2 -2
  49. package/lib/examples/AgUiHumanInTheLoopExample.js +2 -2
  50. package/lib/examples/AgUiSharedStateExample.js +2 -2
  51. package/lib/examples/AgUiToolsBasedGenUIExample.js +2 -2
  52. package/lib/examples/AgentRuntimeCustomExample.js +2 -2
  53. package/lib/examples/AgentRuntimeLexical2Example.js +2 -1
  54. package/lib/examples/AgentRuntimeLexicalExample.js +5 -2
  55. package/lib/examples/AgentRuntimeLexicalSidebarExample.js +4 -2
  56. package/lib/examples/AgentRuntimeNotebookExample.js +1 -1
  57. package/lib/examples/AgentRuntimeStandaloneExample.js +2 -2
  58. package/lib/examples/AgentSpaceFormExample.d.ts +70 -2
  59. package/lib/examples/AgentSpaceFormExample.js +204 -35
  60. package/lib/examples/CopilotKitLexicalExample.js +2 -1
  61. package/lib/examples/components/AgentConfiguration.d.ts +37 -0
  62. package/lib/examples/components/AgentConfiguration.js +239 -8
  63. package/lib/examples/components/Header.d.ts +0 -2
  64. package/lib/examples/components/Header.js +2 -16
  65. package/lib/examples/components/LexicalEditor.js +2 -1
  66. package/lib/examples/components/MockFileBrowser.js +6 -2
  67. package/lib/examples/components/index.d.ts +0 -1
  68. package/lib/examples/components/index.js +0 -1
  69. package/lib/examples/example-selector.js +0 -1
  70. package/lib/examples/index.d.ts +0 -1
  71. package/lib/examples/index.js +0 -1
  72. package/lib/examples/lexical/editorConfig.d.ts +3 -2
  73. package/lib/examples/lexical/editorConfig.js +7 -1
  74. package/lib/examples/lexical/initial-content.json +2210 -0
  75. package/lib/examples/main.js +15 -1
  76. package/lib/identity/IdentityConnect.d.ts +90 -0
  77. package/lib/identity/IdentityConnect.js +316 -0
  78. package/lib/identity/OAuthCallback.d.ts +58 -0
  79. package/lib/identity/OAuthCallback.js +223 -0
  80. package/lib/identity/dcr.d.ts +257 -0
  81. package/lib/identity/dcr.js +282 -0
  82. package/lib/identity/identityStore.d.ts +72 -0
  83. package/lib/identity/identityStore.js +529 -0
  84. package/lib/identity/index.d.ts +46 -0
  85. package/lib/identity/index.js +17 -0
  86. package/lib/identity/pkce.d.ts +30 -0
  87. package/lib/identity/pkce.js +65 -0
  88. package/lib/identity/types.d.ts +293 -0
  89. package/lib/identity/types.js +73 -0
  90. package/lib/identity/useIdentity.d.ts +108 -0
  91. package/lib/identity/useIdentity.js +323 -0
  92. package/lib/index.d.ts +2 -0
  93. package/lib/index.js +2 -0
  94. package/lib/lib/utils.js +1 -1
  95. package/lib/renderers/a2ui/lib/utils.js +1 -1
  96. package/lib/runtime/index.d.ts +35 -0
  97. package/lib/runtime/index.js +40 -0
  98. package/lib/runtime/runtimeStore.d.ts +77 -0
  99. package/lib/runtime/runtimeStore.js +184 -0
  100. package/lib/runtime/types.d.ts +84 -0
  101. package/lib/runtime/types.js +15 -0
  102. package/lib/runtime/useAgentConnection.d.ts +46 -0
  103. package/lib/runtime/useAgentConnection.js +112 -0
  104. package/lib/runtime/useAgentRuntime.d.ts +94 -0
  105. package/lib/runtime/useAgentRuntime.js +125 -0
  106. package/lib/test-setup.d.ts +1 -1
  107. package/lib/test-setup.js +1 -0
  108. package/lib/tools/adapters/agent-runtimes/AgentRuntimesToolAdapter.js +32 -1
  109. package/lib/tools/adapters/agent-runtimes/lexicalHooks.d.ts +6 -0
  110. package/lib/tools/adapters/agent-runtimes/lexicalHooks.js +16 -17
  111. package/package.json +20 -7
  112. package/patches/@datalayer+jupyter-lexical+1.0.8.patch +11628 -0
  113. package/patches/@datalayer+jupyter-react+2.0.2.patch +5338 -0
  114. package/lib/examples/AgentSpaceHomeExample.d.ts +0 -8
  115. package/lib/examples/AgentSpaceHomeExample.js +0 -171
  116. package/lib/examples/components/AgentsDataTable.d.ts +0 -13
  117. package/lib/examples/components/AgentsDataTable.js +0 -74
  118. package/lib/examples/components/Rating.d.ts +0 -14
  119. package/lib/examples/components/Rating.js +0 -12
@@ -19,7 +19,7 @@ import { useContext } from 'react';
19
19
  import { useCallback, useEffect, useRef, useState, } from 'react';
20
20
  import { Heading, Text, Spinner, IconButton, Textarea, Button, ActionMenu, ActionList, LabelGroup, Label, ToggleSwitch, } from '@primer/react';
21
21
  import { Box } from '@datalayer/primer-addons';
22
- import { AlertIcon, PlusIcon, TrashIcon, GearIcon, PersonIcon, PaperAirplaneIcon, SquareCircleIcon, ToolsIcon, AiModelIcon, } from '@primer/octicons-react';
22
+ import { AlertIcon, PlusIcon, TrashIcon, GearIcon, PersonIcon, PaperAirplaneIcon, SquareCircleIcon, ToolsIcon, AiModelIcon, BriefcaseIcon, } from '@primer/octicons-react';
23
23
  import { AiAgentIcon } from '@datalayer/icons-react';
24
24
  import { useQuery, QueryClient, QueryClientProvider, QueryClientContext, } from '@tanstack/react-query';
25
25
  import { Streamdown } from 'streamdown';
@@ -157,31 +157,75 @@ function useConfigQuery(enabled, configEndpoint, authToken) {
157
157
  enabled,
158
158
  });
159
159
  }
160
+ /**
161
+ * Hook to fetch available skills from backend
162
+ */
163
+ function useSkillsQuery(enabled, baseEndpoint, authToken) {
164
+ const queryClient = useContext(QueryClientContext);
165
+ // If no QueryClient is available, return a mock result
166
+ if (!queryClient) {
167
+ return {
168
+ data: undefined,
169
+ isLoading: false,
170
+ isError: false,
171
+ error: null,
172
+ refetch: () => Promise.resolve({ data: undefined }),
173
+ };
174
+ }
175
+ // eslint-disable-next-line react-hooks/rules-of-hooks
176
+ return useQuery({
177
+ queryFn: async () => {
178
+ if (!baseEndpoint) {
179
+ return { skills: [], total: 0 };
180
+ }
181
+ // Derive skills endpoint from config endpoint
182
+ const skillsEndpoint = baseEndpoint.replace('/configure', '/skills');
183
+ const headers = {
184
+ 'Content-Type': 'application/json',
185
+ };
186
+ if (authToken) {
187
+ headers['Authorization'] = `Bearer ${authToken}`;
188
+ }
189
+ const response = await fetch(skillsEndpoint, { headers });
190
+ if (!response.ok) {
191
+ throw new Error(`Skills fetch failed: ${response.statusText}`);
192
+ }
193
+ return response.json();
194
+ },
195
+ queryKey: ['skills', baseEndpoint || 'jupyter'],
196
+ enabled,
197
+ staleTime: 5 * 60 * 1000, // 5 minutes
198
+ });
199
+ }
160
200
  /**
161
201
  * ChatBase component - Universal chat panel supporting store, protocol, and custom modes
162
202
  */
163
- export function ChatBase({ title, showHeader = false, showLoadingIndicator = true, showErrors = true, showInput = true, showModelSelector = false, showToolsMenu = false, className, loadingState, headerActions,
203
+ export function ChatBase({ title, showHeader = false, showLoadingIndicator = true, showErrors = true, showInput = true, showModelSelector = false, showToolsMenu = false, showSkillsMenu = false, codemodeEnabled = false, initialModel, initialMcpServers, initialSkills, className, loadingState, headerActions,
164
204
  // Mode selection
165
205
  useStore: useStoreMode = true, protocol, onSendMessage, enableStreaming = false,
166
206
  // Extended props
167
- brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, emptyState, renderToolResult, footerContent, 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, }) {
207
+ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, emptyState, renderToolResult, footerContent, 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,
208
+ // Identity/Authorization props
209
+ onAuthorizationRequired, connectedIdentities, }) {
168
210
  // Check if QueryClientProvider is already available
169
211
  const existingQueryClient = useContext(QueryClientContext);
170
212
  // If no QueryClient is available, wrap with our internal provider
171
213
  if (!existingQueryClient) {
172
- return (_jsx(QueryClientProvider, { client: internalQueryClient, children: _jsx(ChatBaseInner, { title: title, showHeader: showHeader, showLoadingIndicator: showLoadingIndicator, showErrors: showErrors, showInput: showInput, showModelSelector: showModelSelector, showToolsMenu: showToolsMenu, className: className, loadingState: loadingState, headerActions: headerActions, useStore: useStoreMode, protocol: protocol, onSendMessage: onSendMessage, enableStreaming: enableStreaming, brandIcon: brandIcon, avatarConfig: avatarConfig, headerButtons: headerButtons, showPoweredBy: showPoweredBy, poweredByProps: poweredByProps, emptyState: emptyState, renderToolResult: renderToolResult, footerContent: footerContent, 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 }) }));
214
+ return (_jsx(QueryClientProvider, { client: internalQueryClient, children: _jsx(ChatBaseInner, { title: title, showHeader: showHeader, showLoadingIndicator: showLoadingIndicator, showErrors: showErrors, showInput: showInput, showModelSelector: showModelSelector, showToolsMenu: showToolsMenu, showSkillsMenu: showSkillsMenu, codemodeEnabled: codemodeEnabled, initialModel: initialModel, initialMcpServers: initialMcpServers, initialSkills: initialSkills, className: className, loadingState: loadingState, headerActions: headerActions, useStore: useStoreMode, protocol: protocol, onSendMessage: onSendMessage, enableStreaming: enableStreaming, brandIcon: brandIcon, avatarConfig: avatarConfig, headerButtons: headerButtons, showPoweredBy: showPoweredBy, poweredByProps: poweredByProps, emptyState: emptyState, renderToolResult: renderToolResult, footerContent: footerContent, 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 }) }));
173
215
  }
174
216
  // QueryClient already available, render inner component directly
175
- return (_jsx(ChatBaseInner, { title: title, showHeader: showHeader, showLoadingIndicator: showLoadingIndicator, showErrors: showErrors, showInput: showInput, showModelSelector: showModelSelector, showToolsMenu: showToolsMenu, className: className, loadingState: loadingState, headerActions: headerActions, useStore: useStoreMode, protocol: protocol, onSendMessage: onSendMessage, enableStreaming: enableStreaming, brandIcon: brandIcon, avatarConfig: avatarConfig, headerButtons: headerButtons, showPoweredBy: showPoweredBy, poweredByProps: poweredByProps, emptyState: emptyState, renderToolResult: renderToolResult, footerContent: footerContent, 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 }));
217
+ return (_jsx(ChatBaseInner, { title: title, showHeader: showHeader, showLoadingIndicator: showLoadingIndicator, showErrors: showErrors, showInput: showInput, showModelSelector: showModelSelector, showToolsMenu: showToolsMenu, showSkillsMenu: showSkillsMenu, codemodeEnabled: codemodeEnabled, initialModel: initialModel, initialMcpServers: initialMcpServers, initialSkills: initialSkills, className: className, loadingState: loadingState, headerActions: headerActions, useStore: useStoreMode, protocol: protocol, onSendMessage: onSendMessage, enableStreaming: enableStreaming, brandIcon: brandIcon, avatarConfig: avatarConfig, headerButtons: headerButtons, showPoweredBy: showPoweredBy, poweredByProps: poweredByProps, emptyState: emptyState, renderToolResult: renderToolResult, footerContent: footerContent, 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 }));
176
218
  }
177
219
  /**
178
220
  * Inner ChatBase component - contains all the actual logic
179
221
  */
180
- function ChatBaseInner({ title, showHeader = false, showLoadingIndicator = true, showErrors = true, showInput = true, showModelSelector = false, showToolsMenu = false, className, loadingState, headerActions,
222
+ function ChatBaseInner({ title, showHeader = false, showLoadingIndicator = true, showErrors = true, showInput = true, showModelSelector = false, showToolsMenu = false, showSkillsMenu = false, codemodeEnabled = false, initialModel, initialMcpServers, initialSkills, className, loadingState, headerActions,
181
223
  // Mode selection
182
224
  useStore: useStoreMode = true, protocol, onSendMessage, enableStreaming = false,
183
225
  // Extended props
184
- brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, emptyState, renderToolResult, footerContent, 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, }) {
226
+ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, emptyState, renderToolResult, footerContent, 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,
227
+ // Identity/Authorization props
228
+ onAuthorizationRequired, connectedIdentities, }) {
185
229
  // Ensure Primer's default portal has high z-index for ActionMenu overlays
186
230
  useHighZIndexPortal();
187
231
  // Store (optional for message persistence)
@@ -202,9 +246,13 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
202
246
  // Note: legacy _enabledTools for backend-defined tools from config query
203
247
  // Frontend tools are passed via frontendTools prop
204
248
  const [_enabledTools, setEnabledTools] = useState([]);
249
+ // Skills state - tracks which skills are enabled
250
+ const [enabledSkills, setEnabledSkills] = useState(new Set());
205
251
  // Config query (for protocols that support it)
206
252
  // Safely handles missing QueryClientProvider
207
253
  const configQuery = useConfigQuery(Boolean(protocol?.enableConfigQuery), protocol?.configEndpoint, protocol?.authToken);
254
+ // Skills query (for protocols that support it)
255
+ const skillsQuery = useSkillsQuery(Boolean(protocol?.enableConfigQuery) && showSkillsMenu, protocol?.configEndpoint, protocol?.authToken);
208
256
  // Refs
209
257
  const adapterRef = useRef(null);
210
258
  const unsubscribeRef = useRef(null);
@@ -214,6 +262,10 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
214
262
  const messagesEndRef = useRef(null);
215
263
  const inputRef = useRef(null);
216
264
  const abortControllerRef = useRef(null);
265
+ // Use a ref for connectedIdentities to avoid infinite loops in useCallback
266
+ // (the array reference changes on every render even if contents are the same)
267
+ const connectedIdentitiesRef = useRef(connectedIdentities);
268
+ connectedIdentitiesRef.current = connectedIdentities;
217
269
  // Auto-focus input on mount
218
270
  useEffect(() => {
219
271
  if (autoFocus && inputRef.current) {
@@ -255,7 +307,8 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
255
307
  textarea.style.height = 'auto';
256
308
  // Set height to scrollHeight, capped at maxHeight (120px)
257
309
  const maxHeight = 120;
258
- const newHeight = Math.min(textarea.scrollHeight, maxHeight);
310
+ const minHeight = 40;
311
+ const newHeight = Math.min(Math.max(textarea.scrollHeight, minHeight), maxHeight);
259
312
  textarea.style.height = `${newHeight}px`;
260
313
  // Add overflow if content exceeds maxHeight
261
314
  textarea.style.overflowY =
@@ -266,30 +319,66 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
266
319
  useEffect(() => {
267
320
  adjustTextareaHeight();
268
321
  }, [input, adjustTextareaHeight]);
322
+ // Ensure textarea has a minimum height on mount
323
+ useEffect(() => {
324
+ const timer = setTimeout(adjustTextareaHeight, 0);
325
+ return () => clearTimeout(timer);
326
+ }, [adjustTextareaHeight]);
269
327
  // Initialize model and tools when config is available
270
328
  useEffect(() => {
271
329
  if (configQuery.data && !selectedModel) {
272
- // Select first available model, or fallback to first model if none available
273
- const firstAvailableModel = configQuery.data.models.find(m => m.isAvailable !== false);
274
- const firstModel = firstAvailableModel || configQuery.data.models[0];
275
- if (firstModel) {
276
- setSelectedModel(firstModel.id);
277
- const allToolIds = configQuery.data.builtinTools?.map(tool => tool.id) || [];
278
- setEnabledTools(allToolIds);
330
+ // Use initialModel if provided, otherwise select first available model
331
+ if (initialModel) {
332
+ // Check if the initial model exists in the config
333
+ const modelExists = configQuery.data.models.some(m => m.id === initialModel);
334
+ if (modelExists) {
335
+ setSelectedModel(initialModel);
336
+ }
337
+ else {
338
+ // Fallback to first available model if initialModel not found
339
+ const firstAvailableModel = configQuery.data.models.find(m => m.isAvailable !== false);
340
+ const firstModel = firstAvailableModel || configQuery.data.models[0];
341
+ if (firstModel) {
342
+ setSelectedModel(firstModel.id);
343
+ }
344
+ }
345
+ }
346
+ else {
347
+ // No initialModel provided, select first available model
348
+ const firstAvailableModel = configQuery.data.models.find(m => m.isAvailable !== false);
349
+ const firstModel = firstAvailableModel || configQuery.data.models[0];
350
+ if (firstModel) {
351
+ setSelectedModel(firstModel.id);
352
+ }
279
353
  }
280
- // Initialize MCP server tools - all enabled by default
354
+ const allToolIds = configQuery.data.builtinTools?.map(tool => tool.id) || [];
355
+ setEnabledTools(allToolIds);
356
+ // Initialize MCP server tools
281
357
  if (configQuery.data.mcpServers) {
282
358
  const newEnabledMcpTools = new Map();
283
359
  for (const server of configQuery.data.mcpServers) {
284
360
  if (server.isAvailable && server.enabled) {
285
- const enabledToolNames = new Set(server.tools.filter(t => t.enabled).map(t => t.name));
286
- newEnabledMcpTools.set(server.id, enabledToolNames);
361
+ // If initialMcpServers is provided, only enable those servers
362
+ // If not provided, enable all available servers
363
+ const shouldEnableServer = initialMcpServers
364
+ ? initialMcpServers.includes(server.id)
365
+ : true;
366
+ if (shouldEnableServer) {
367
+ const enabledToolNames = new Set(server.tools.filter(t => t.enabled).map(t => t.name));
368
+ newEnabledMcpTools.set(server.id, enabledToolNames);
369
+ }
287
370
  }
288
371
  }
289
372
  setEnabledMcpTools(newEnabledMcpTools);
290
373
  }
291
374
  }
292
- }, [configQuery.data, selectedModel]);
375
+ }, [configQuery.data, selectedModel, initialModel, initialMcpServers]);
376
+ // Initialize enabled skills from initialSkills prop
377
+ useEffect(() => {
378
+ if (initialSkills && initialSkills.length > 0) {
379
+ setEnabledSkills(new Set(initialSkills));
380
+ }
381
+ }, [initialSkills]);
293
382
  // Helper to toggle MCP tool enabled state
294
383
  const toggleMcpTool = useCallback((serverId, toolName) => {
295
384
  setEnabledMcpTools(prev => {
@@ -318,6 +407,23 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
318
407
  return newMap;
319
408
  });
320
409
  }, []);
410
+ // Helper to toggle skill enabled state
411
+ const toggleSkill = useCallback((skillId) => {
412
+ setEnabledSkills(prev => {
413
+ const newSet = new Set(prev);
414
+ if (newSet.has(skillId)) {
415
+ newSet.delete(skillId);
416
+ }
417
+ else {
418
+ newSet.add(skillId);
419
+ }
420
+ return newSet;
421
+ });
422
+ }, []);
423
+ // Helper to toggle all skills
424
+ const toggleAllSkills = useCallback((allSkillIds, enable) => {
425
+ setEnabledSkills(enable ? new Set(allSkillIds) : new Set());
426
+ }, []);
321
427
  // Get all enabled MCP tool names (for sending with requests)
322
428
  const getEnabledMcpToolNames = useCallback(() => {
323
429
  const toolNames = [];
@@ -326,6 +432,10 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
326
432
  });
327
433
  return toolNames;
328
434
  }, [enabledMcpTools]);
435
+ // Get all enabled skill IDs (for sending with requests)
436
+ const getEnabledSkillIds = useCallback(() => {
437
+ return Array.from(enabledSkills);
438
+ }, [enabledSkills]);
329
439
  // Load messages from store on mount when useStoreMode is enabled
330
440
  useEffect(() => {
331
441
  if (useStoreMode) {
@@ -338,10 +448,16 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
338
448
  // Derived state
339
449
  const messages = displayItems.filter((item) => !isToolCallMessage(item));
340
450
  const ready = true;
341
- // Notify parent when messages change
451
+ // Track previous message count to avoid unnecessary callbacks
452
+ const prevMessageCountRef = useRef(0);
453
+ // Notify parent when messages change (only when count actually changes)
342
454
  useEffect(() => {
343
- onMessagesChange?.(messages);
344
- }, [messages, onMessagesChange]);
455
+ const currentCount = messages.length;
456
+ if (currentCount !== prevMessageCountRef.current) {
457
+ prevMessageCountRef.current = currentCount;
458
+ onMessagesChange?.(messages);
459
+ }
460
+ }, [displayItems, onMessagesChange]); // Use displayItems instead of messages to avoid infinite loop
345
461
  // Padding based on compact mode
346
462
  const padding = compact ? 2 : 3;
347
463
  // Default avatar config
@@ -548,16 +664,58 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
548
664
  const isHumanInTheLoop = existingToolCall.args &&
549
665
  'steps' in existingToolCall.args &&
550
666
  Array.isArray(existingToolCall.args.steps);
667
+ // Extract rich error information from result if available
668
+ const resultData = event.toolResult.result;
669
+ let executionError;
670
+ let codeError;
671
+ let exitCode;
672
+ let hasError = !!event.toolResult.error;
673
+ if (resultData && typeof resultData === 'object') {
674
+ // Check for execution_error (infrastructure/sandbox errors)
675
+ if (resultData.execution_error &&
676
+ typeof resultData.execution_error === 'string') {
677
+ executionError = resultData.execution_error;
678
+ hasError = true;
679
+ }
680
+ // Check for code_error (Python exceptions)
681
+ if (resultData.code_error &&
682
+ typeof resultData.code_error === 'object') {
683
+ const ce = resultData.code_error;
684
+ codeError = {
685
+ name: ce.name || 'Error',
686
+ value: ce.value || 'Unknown error',
687
+ traceback: ce.traceback,
688
+ };
689
+ hasError = true;
690
+ }
691
+ // Check for exit_code (non-zero exit from sys.exit())
692
+ if ('exit_code' in resultData) {
693
+ const ec = resultData.exit_code;
694
+ exitCode = typeof ec === 'number' ? ec : null;
695
+ // Non-zero exit code counts as an error condition
696
+ if (exitCode != null && exitCode !== 0) {
697
+ hasError = true;
698
+ }
699
+ }
700
+ // Check for execution_ok flag
701
+ if ('execution_ok' in resultData &&
702
+ resultData.execution_ok === false) {
703
+ hasError = true;
704
+ }
705
+ }
551
706
  const updatedToolCall = {
552
707
  ...existingToolCall,
553
708
  result: event.toolResult.result,
554
709
  // Keep executing for HITL, otherwise mark complete/error
555
- status: event.toolResult.error
710
+ status: hasError
556
711
  ? 'error'
557
712
  : isHumanInTheLoop
558
713
  ? 'executing'
559
714
  : 'complete',
560
715
  error: event.toolResult.error,
716
+ executionError,
717
+ codeError,
718
+ exitCode,
561
719
  };
562
720
  toolCallsRef.current.set(toolCallId, updatedToolCall);
563
721
  setDisplayItems(prev => prev.map(item => isToolCallMessage(item) && item.toolCallId === toolCallId
@@ -724,8 +882,9 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
724
882
  description: tool.description,
725
883
  parameters: tool.parameters || { type: 'object', properties: {} },
726
884
  }));
727
- // Get enabled MCP tool names
885
+ // Get enabled MCP tool names and skill IDs
728
886
  const enabledMcpToolNames = getEnabledMcpToolNames();
887
+ const enabledSkillIds = getEnabledSkillIds();
729
888
  await adapterRef.current.sendMessage(userMessage, {
730
889
  threadId: threadIdRef.current,
731
890
  messages: allMessages,
@@ -733,6 +892,10 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
733
892
  tools: toolsForRequest,
734
893
  // Include enabled MCP tools as builtin_tools for backend
735
894
  builtinTools: enabledMcpToolNames,
895
+ // Include enabled skills for backend
896
+ skills: enabledSkillIds,
897
+ // Include connected identities with access tokens (use ref to avoid infinite loops)
898
+ identities: connectedIdentitiesRef.current,
736
899
  });
737
900
  }
738
901
  }
@@ -760,6 +923,7 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
760
923
  onSendMessage,
761
924
  enableStreaming,
762
925
  getEnabledMcpToolNames,
926
+ getEnabledSkillIds,
763
927
  ]);
764
928
  // Handle stop
765
929
  const handleStop = useCallback(() => {
@@ -1018,7 +1182,7 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
1018
1182
  status: item.status,
1019
1183
  error: item.error,
1020
1184
  respond,
1021
- })) : (_jsx(ToolCallDisplay, { toolCallId: item.toolCallId, toolName: item.toolName, args: item.args, result: item.result, status: item.status, error: item.error }));
1185
+ })) : (_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 }));
1022
1186
  // Skip if custom render returns null/undefined
1023
1187
  if (toolUI === null || toolUI === undefined)
1024
1188
  return null;
@@ -1281,7 +1445,8 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
1281
1445
  maxHeight: '120px',
1282
1446
  overflow: 'hidden',
1283
1447
  transition: 'height 0.1s ease-out',
1284
- }, rows: 1 }), isLoading ? (_jsx(IconButton, { icon: SquareCircleIcon, "aria-label": "Stop", onClick: handleStop, sx: { alignSelf: 'flex-end' } })) : (_jsx(IconButton, { icon: PaperAirplaneIcon, "aria-label": "Send", onClick: handleSend, disabled: !input.trim(), sx: { alignSelf: 'flex-end' } }))] }) }), (showModelSelector || showToolsMenu) && configQuery.data && (_jsxs(Box, { sx: {
1448
+ }, rows: 1 }), isLoading ? (_jsx(IconButton, { icon: SquareCircleIcon, "aria-label": "Stop", onClick: handleStop, sx: { alignSelf: 'flex-end' } })) : (_jsx(IconButton, { icon: PaperAirplaneIcon, "aria-label": "Send", onClick: handleSend, disabled: !input.trim(), sx: { alignSelf: 'flex-end' } }))] }) }), (showModelSelector || showToolsMenu || showSkillsMenu) &&
1449
+ (configQuery.data || skillsQuery.data) && (_jsxs(Box, { sx: {
1285
1450
  display: 'flex',
1286
1451
  gap: 2,
1287
1452
  px: padding,
@@ -1290,61 +1455,100 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
1290
1455
  borderColor: 'border.default',
1291
1456
  alignItems: 'center',
1292
1457
  bg: 'canvas.subtle',
1293
- }, children: [showToolsMenu && (_jsxs(ActionMenu, { children: [_jsx(ActionMenu.Anchor, { children: _jsx(IconButton, { icon: ToolsIcon, "aria-label": "Tools", variant: "invisible", size: "small" }) }), _jsx(ActionMenu.Overlay, { side: "outside-top", align: "start", width: "large", children: _jsx(Box, { sx: {
1458
+ }, 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 &&
1459
+ ` (${getEnabledMcpToolNames().length})`] }) }) }), _jsx(ActionMenu.Overlay, { side: "outside-top", align: "start", width: "large", children: _jsx(Box, { sx: {
1294
1460
  maxHeight: '60vh',
1295
1461
  overflowY: 'auto',
1296
- }, children: _jsx(ActionList, { children: configQuery.data?.mcpServers &&
1297
- configQuery.data.mcpServers.length > 0 ? (configQuery.data.mcpServers.map(server => {
1298
- const serverTools = enabledMcpTools.get(server.id);
1299
- const allToolNames = server.tools.map(t => t.name);
1300
- const enabledCount = serverTools?.size ?? 0;
1301
- const allEnabled = enabledCount === allToolNames.length &&
1302
- allToolNames.length > 0;
1303
- return (_jsxs(ActionList.Group, { title: `${server.name}${server.isAvailable ? '' : ' (unavailable)'}`, children: [server.isAvailable &&
1304
- server.tools.length > 0 && (_jsxs(Box, { sx: {
1305
- display: 'flex',
1306
- alignItems: 'center',
1307
- justifyContent: 'space-between',
1308
- px: 3,
1309
- py: 2,
1310
- borderBottom: '1px solid',
1311
- borderColor: 'border.muted',
1312
- }, children: [_jsxs(Text, { id: `toggle-all-${server.id}`, sx: {
1313
- fontSize: 0,
1314
- fontWeight: 'semibold',
1315
- color: 'fg.muted',
1316
- }, 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 && server.tools.length > 0 ? (server.tools.map(tool => {
1317
- const isEnabled = serverTools?.has(tool.name) ?? false;
1318
- return (_jsxs(Box, { sx: {
1462
+ }, 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 &&
1463
+ configQuery.data.mcpServers.length > 0 ? (configQuery.data.mcpServers.map(server => {
1464
+ const serverTools = enabledMcpTools.get(server.id);
1465
+ const allToolNames = server.tools.map(t => t.name);
1466
+ const enabledCount = serverTools?.size ?? 0;
1467
+ const allEnabled = enabledCount === allToolNames.length &&
1468
+ allToolNames.length > 0;
1469
+ return (_jsxs(ActionList.Group, { title: `${server.name}${server.isAvailable ? '' : ' (unavailable)'}`, children: [server.isAvailable &&
1470
+ server.tools.length > 0 && (_jsxs(Box, { sx: {
1319
1471
  display: 'flex',
1320
1472
  alignItems: 'center',
1321
1473
  justifyContent: 'space-between',
1322
1474
  px: 3,
1323
1475
  py: 2,
1324
- '&:hover': {
1325
- backgroundColor: 'canvas.subtle',
1326
- },
1327
- }, 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: {
1328
- display: 'block',
1329
- fontSize: 0,
1330
- color: 'fg.muted',
1331
- overflow: 'hidden',
1332
- textOverflow: 'ellipsis',
1333
- whiteSpace: 'nowrap',
1334
- }, 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}`));
1335
- })) : server.isAvailable ? (_jsx(ActionList.Item, { disabled: true, children: _jsx(Text, { sx: {
1336
- color: 'fg.muted',
1337
- fontStyle: 'italic',
1338
- }, children: "No tools discovered" }) })) : (_jsx(ActionList.Item, { disabled: true, children: _jsx(Text, { sx: {
1476
+ borderBottom: '1px solid',
1477
+ borderColor: 'border.muted',
1478
+ }, children: [_jsxs(Text, { id: `toggle-all-${server.id}`, sx: {
1479
+ fontSize: 0,
1480
+ fontWeight: 'semibold',
1481
+ color: 'fg.muted',
1482
+ }, 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 &&
1483
+ server.tools.length > 0 ? (server.tools.map(tool => {
1484
+ const isEnabled = serverTools?.has(tool.name) ?? false;
1485
+ return (_jsxs(Box, { sx: {
1486
+ display: 'flex',
1487
+ alignItems: 'center',
1488
+ justifyContent: 'space-between',
1489
+ px: 3,
1490
+ py: 2,
1491
+ '&:hover': {
1492
+ backgroundColor: 'canvas.subtle',
1493
+ },
1494
+ }, 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: {
1495
+ display: 'block',
1496
+ fontSize: 0,
1497
+ color: 'fg.muted',
1498
+ overflow: 'hidden',
1499
+ textOverflow: 'ellipsis',
1500
+ whiteSpace: 'nowrap',
1501
+ }, 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}`));
1502
+ })) : server.isAvailable ? (_jsx(ActionList.Item, { disabled: true, children: _jsx(Text, { sx: {
1503
+ color: 'fg.muted',
1504
+ fontStyle: 'italic',
1505
+ }, children: "No tools discovered" }) })) : (_jsx(ActionList.Item, { disabled: true, children: _jsx(Text, { sx: {
1506
+ color: 'fg.muted',
1507
+ fontStyle: 'italic',
1508
+ }, children: "Server unavailable" }) }))] }, server.id));
1509
+ })) : (_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: {
1510
+ width: 8,
1511
+ height: 8,
1512
+ borderRadius: '50%',
1513
+ backgroundColor: 'success.emphasis',
1514
+ } }) }), tool.name] }, tool.id)))) : (_jsx(ActionList.Item, { disabled: true, children: _jsx(Text, { sx: {
1515
+ color: 'fg.muted',
1516
+ fontStyle: 'italic',
1517
+ }, 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: {
1518
+ maxHeight: '60vh',
1519
+ overflowY: 'auto',
1520
+ }, children: _jsx(ActionList, { children: skillsQuery.isLoading ? (_jsx(ActionList.Item, { disabled: true, children: _jsx(Text, { sx: { color: 'fg.muted' }, children: "Loading skills..." }) })) : skillsQuery.data?.skills &&
1521
+ skillsQuery.data.skills.length > 0 ? (_jsxs(_Fragment, { children: [_jsxs(Box, { sx: {
1522
+ display: 'flex',
1523
+ alignItems: 'center',
1524
+ justifyContent: 'space-between',
1525
+ px: 3,
1526
+ py: 2,
1527
+ borderBottom: '1px solid',
1528
+ borderColor: 'border.muted',
1529
+ }, children: [_jsxs(Text, { id: "toggle-all-skills", sx: {
1530
+ fontSize: 0,
1531
+ fontWeight: 'semibold',
1339
1532
  color: 'fg.muted',
1340
- fontStyle: 'italic',
1341
- }, children: "Server unavailable" }) }))] }, server.id));
1342
- })) : (_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: {
1343
- width: 8,
1344
- height: 8,
1345
- borderRadius: '50%',
1346
- backgroundColor: 'success.emphasis',
1347
- } }) }), tool.name] }, tool.id)))) : (_jsx(ActionList.Item, { disabled: true, children: _jsx(Text, { sx: { color: 'fg.muted', fontStyle: 'italic' }, children: "No tools available" }) })) })) }) }) })] })), showModelSelector && models.length > 0 && selectedModel && (_jsxs(Box, { sx: {
1533
+ }, children: ["Enable all (", enabledSkills.size, "/", skillsQuery.data.skills.length, ")"] }), _jsx(ToggleSwitch, { size: "small", checked: enabledSkills.size ===
1534
+ skillsQuery.data.skills.length, onClick: () => toggleAllSkills(skillsQuery.data.skills.map(s => s.id), enabledSkills.size !==
1535
+ skillsQuery.data.skills.length), "aria-labelledby": "toggle-all-skills" })] }), skillsQuery.data.skills.map(skill => (_jsxs(Box, { sx: {
1536
+ display: 'flex',
1537
+ alignItems: 'center',
1538
+ justifyContent: 'space-between',
1539
+ px: 3,
1540
+ py: 2,
1541
+ '&:hover': {
1542
+ backgroundColor: 'canvas.subtle',
1543
+ },
1544
+ }, 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: {
1545
+ display: 'block',
1546
+ fontSize: 0,
1547
+ color: 'fg.muted',
1548
+ overflow: 'hidden',
1549
+ textOverflow: 'ellipsis',
1550
+ whiteSpace: 'nowrap',
1551
+ }, 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: {
1348
1552
  display: 'flex',
1349
1553
  flexDirection: 'column',
1350
1554
  alignItems: 'flex-end',
@@ -1372,7 +1576,7 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
1372
1576
  bg: 'danger.subtle',
1373
1577
  borderBottom: '1px solid',
1374
1578
  borderColor: 'danger.muted',
1375
- }, children: [_jsx(AlertIcon, { size: 16 }), _jsx(Text, { sx: { color: 'danger.fg', fontSize: 1 }, children: error.message })] })), _jsx(Box, { sx: { flex: 1, overflow: 'auto', bg: 'canvas.default' }, children: children ? (children) : (_jsx(Box, { sx: {
1579
+ }, 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: {
1376
1580
  display: 'flex',
1377
1581
  flexDirection: 'column',
1378
1582
  minHeight: '100%',
@@ -1,4 +1,9 @@
1
1
  import type { ToolCallStatus } from '../base/ChatBase';
2
+ import type { CodeError, ExecutionResult } from '../../types/execution';
3
+ /**
4
+ * Error type classification for display purposes
5
+ */
6
+ export type ErrorType = 'execution' | 'code' | 'exit' | 'unknown';
2
7
  export interface ToolCallDisplayProps {
3
8
  /** Tool call ID */
4
9
  toolCallId: string;
@@ -10,8 +15,16 @@ export interface ToolCallDisplayProps {
10
15
  result?: unknown;
11
16
  /** Current status */
12
17
  status: ToolCallStatus;
13
- /** Error message if failed */
18
+ /** Error message if failed (backwards compatible) */
14
19
  error?: string;
20
+ /** Rich execution result with detailed error information */
21
+ executionResult?: ExecutionResult;
22
+ /** Code error details (Python exception) */
23
+ codeError?: CodeError;
24
+ /** Exit code when code called sys.exit() */
25
+ exitCode?: number | null;
26
+ /** Execution/infrastructure error message */
27
+ executionError?: string;
15
28
  }
16
29
  /**
17
30
  * ToolCallDisplay component - Default display for tool calls in chat
@@ -21,6 +34,7 @@ export interface ToolCallDisplayProps {
21
34
  * - Shows tool name, status icon, and brief summary when collapsed
22
35
  * - Expands to show full parameters and results
23
36
  * - Color-coded status indicators
37
+ * - Rich error display distinguishing execution errors from code errors
24
38
  */
25
- export declare function ToolCallDisplay({ toolCallId, toolName, args, result, status, error, }: ToolCallDisplayProps): import("react/jsx-runtime").JSX.Element;
39
+ export declare function ToolCallDisplay({ toolCallId, toolName, args, result, status, error, executionResult, codeError, exitCode, executionError, }: ToolCallDisplayProps): import("react/jsx-runtime").JSX.Element;
26
40
  export default ToolCallDisplay;