@datalayer/agent-runtimes 0.0.7 → 0.0.9

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 (104) hide show
  1. package/README.md +9 -0
  2. package/lib/components/chat/components/AgentDetails.d.ts +14 -1
  3. package/lib/components/chat/components/AgentDetails.js +3 -2
  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 +20 -1
  7. package/lib/components/chat/components/Chat.js +16 -3
  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/base/ChatBase.d.ts +47 -1
  11. package/lib/components/chat/components/base/ChatBase.js +242 -63
  12. package/lib/components/chat/components/display/ToolCallDisplay.d.ts +16 -2
  13. package/lib/components/chat/components/display/ToolCallDisplay.js +148 -6
  14. package/lib/components/chat/components/display/index.d.ts +1 -1
  15. package/lib/components/chat/components/display/index.js +1 -1
  16. package/lib/components/chat/components/elements/ChatInputPrompt.d.ts +12 -1
  17. package/lib/components/chat/components/elements/ChatInputPrompt.js +8 -3
  18. package/lib/components/chat/components/index.d.ts +1 -0
  19. package/lib/components/chat/components/index.js +1 -0
  20. package/lib/components/chat/components/parts/ToolPart.d.ts +1 -1
  21. package/lib/components/chat/components/parts/ToolPart.js +142 -6
  22. package/lib/components/chat/index.d.ts +1 -1
  23. package/lib/components/chat/index.js +1 -1
  24. package/lib/components/chat/protocols/A2AAdapter.d.ts +9 -0
  25. package/lib/components/chat/protocols/A2AAdapter.js +13 -2
  26. package/lib/components/chat/protocols/ACPAdapter.d.ts +9 -0
  27. package/lib/components/chat/protocols/ACPAdapter.js +13 -2
  28. package/lib/components/chat/protocols/AGUIAdapter.d.ts +9 -0
  29. package/lib/components/chat/protocols/AGUIAdapter.js +19 -1
  30. package/lib/components/chat/protocols/VercelAIAdapter.d.ts +7 -0
  31. package/lib/components/chat/protocols/VercelAIAdapter.js +19 -0
  32. package/lib/components/chat/types/execution.d.ts +78 -0
  33. package/lib/components/chat/types/execution.js +64 -0
  34. package/lib/components/chat/types/index.d.ts +1 -0
  35. package/lib/components/chat/types/index.js +1 -0
  36. package/lib/components/chat/types/protocol.d.ts +9 -0
  37. package/lib/components/ui/pagination.d.ts +2 -2
  38. package/lib/components/ui/pagination.js +4 -4
  39. package/lib/components/ui/resizable.d.ts +4 -4
  40. package/lib/components/ui/resizable.js +4 -4
  41. package/lib/examples/A2UiRestaurantExample.js +2 -2
  42. package/lib/examples/AgUiAgenticExample.js +2 -2
  43. package/lib/examples/AgUiBackendToolRenderingExample.js +2 -2
  44. package/lib/examples/AgUiHaikuGenUIExample.js +2 -2
  45. package/lib/examples/AgUiHumanInTheLoopExample.js +2 -2
  46. package/lib/examples/AgUiSharedStateExample.js +2 -2
  47. package/lib/examples/AgUiToolsBasedGenUIExample.js +2 -2
  48. package/lib/examples/AgentRuntimeCustomExample.js +2 -2
  49. package/lib/examples/AgentRuntimeLexical2Example.js +2 -1
  50. package/lib/examples/AgentRuntimeLexicalExample.js +5 -2
  51. package/lib/examples/AgentRuntimeLexicalSidebarExample.js +4 -2
  52. package/lib/examples/AgentRuntimeNotebookExample.js +1 -1
  53. package/lib/examples/AgentRuntimeStandaloneExample.js +2 -2
  54. package/lib/examples/AgentSpaceFormExample.d.ts +70 -2
  55. package/lib/examples/AgentSpaceFormExample.js +177 -43
  56. package/lib/examples/CopilotKitLexicalExample.js +2 -1
  57. package/lib/examples/components/AgentConfiguration.d.ts +17 -2
  58. package/lib/examples/components/AgentConfiguration.js +220 -16
  59. package/lib/examples/components/LexicalEditor.js +2 -1
  60. package/lib/examples/components/MockFileBrowser.js +6 -2
  61. package/lib/examples/components/index.d.ts +0 -1
  62. package/lib/examples/components/index.js +0 -1
  63. package/lib/examples/example-selector.js +0 -1
  64. package/lib/examples/index.d.ts +0 -1
  65. package/lib/examples/index.js +0 -1
  66. package/lib/examples/lexical/editorConfig.d.ts +3 -2
  67. package/lib/examples/lexical/editorConfig.js +7 -1
  68. package/lib/examples/lexical/initial-content.json +2210 -0
  69. package/lib/examples/main.js +15 -1
  70. package/lib/identity/IdentityConnect.d.ts +90 -0
  71. package/lib/identity/IdentityConnect.js +316 -0
  72. package/lib/identity/OAuthCallback.d.ts +58 -0
  73. package/lib/identity/OAuthCallback.js +223 -0
  74. package/lib/identity/dcr.d.ts +257 -0
  75. package/lib/identity/dcr.js +282 -0
  76. package/lib/identity/identityStore.d.ts +72 -0
  77. package/lib/identity/identityStore.js +529 -0
  78. package/lib/identity/index.d.ts +46 -0
  79. package/lib/identity/index.js +17 -0
  80. package/lib/identity/pkce.d.ts +30 -0
  81. package/lib/identity/pkce.js +65 -0
  82. package/lib/identity/types.d.ts +293 -0
  83. package/lib/identity/types.js +73 -0
  84. package/lib/identity/useIdentity.d.ts +108 -0
  85. package/lib/identity/useIdentity.js +323 -0
  86. package/lib/index.d.ts +1 -0
  87. package/lib/index.js +1 -0
  88. package/lib/lib/utils.js +1 -1
  89. package/lib/renderers/a2ui/lib/utils.js +1 -1
  90. package/lib/test-setup.d.ts +1 -1
  91. package/lib/test-setup.js +1 -0
  92. package/lib/tools/adapters/agent-runtimes/AgentRuntimesToolAdapter.js +32 -1
  93. package/lib/tools/adapters/agent-runtimes/lexicalHooks.d.ts +6 -0
  94. package/lib/tools/adapters/agent-runtimes/lexicalHooks.js +16 -17
  95. package/package.json +20 -7
  96. package/patches/@datalayer+jupyter-lexical+1.0.8.patch +11628 -0
  97. package/patches/@datalayer+jupyter-react+2.0.2.patch +5338 -0
  98. package/scripts/apply-patches.sh +32 -0
  99. package/lib/examples/AgentSpaceHomeExample.d.ts +0 -8
  100. package/lib/examples/AgentSpaceHomeExample.js +0 -171
  101. package/lib/examples/components/AgentsDataTable.d.ts +0 -13
  102. package/lib/examples/components/AgentsDataTable.js +0 -74
  103. package/lib/examples/components/Rating.d.ts +0 -14
  104. package/lib/examples/components/Rating.js +0 -12
@@ -201,10 +201,16 @@ export interface ChatBaseProps {
201
201
  showModelSelector?: boolean;
202
202
  /** Show tools menu (for protocols that support it) */
203
203
  showToolsMenu?: boolean;
204
+ /** Show skills menu (for protocols that support it) */
205
+ showSkillsMenu?: boolean;
206
+ /** Indicate tools are accessed via Codemode meta-tools */
207
+ codemodeEnabled?: boolean;
204
208
  /** Initial model ID to select (e.g., 'openai:gpt-4o-mini') */
205
209
  initialModel?: string;
206
210
  /** Initial MCP server IDs to enable (others will be disabled) */
207
211
  initialMcpServers?: string[];
212
+ /** Initial skill IDs to enable */
213
+ initialSkills?: string[];
208
214
  /** Custom class name */
209
215
  className?: string;
210
216
  /** Custom loading state */
@@ -305,10 +311,50 @@ export interface ChatBaseProps {
305
311
  * These tools execute in the browser and their results are sent back to the agent.
306
312
  */
307
313
  frontendTools?: FrontendToolDefinition[];
314
+ /**
315
+ * Callback when the agent requests authorization for an external service.
316
+ * This is called when a tool needs OAuth access to a service like GitHub.
317
+ *
318
+ * @param provider - The OAuth provider name (e.g., 'github', 'google')
319
+ * @param scopes - The requested OAuth scopes
320
+ * @param context - Additional context about why authorization is needed
321
+ * @returns Promise resolving to the access token, or null if user cancels
322
+ *
323
+ * @example
324
+ * ```tsx
325
+ * <ChatBase
326
+ * onAuthorizationRequired={async (provider, scopes, context) => {
327
+ * // Show UI to user to authorize
328
+ * const token = await showAuthDialog(provider, scopes);
329
+ * return token;
330
+ * }}
331
+ * />
332
+ * ```
333
+ */
334
+ onAuthorizationRequired?: (provider: string, scopes: string[], context?: {
335
+ toolName?: string;
336
+ reason?: string;
337
+ }) => Promise<string | null>;
338
+ /**
339
+ * Connected identities to pass to agent tools.
340
+ * When provided, access tokens for these identities are automatically
341
+ * included in tool calls that need them.
342
+ *
343
+ * @example
344
+ * ```tsx
345
+ * const { identities, getAccessToken } = useIdentity();
346
+ * <ChatBase connectedIdentities={identities} />
347
+ * ```
348
+ */
349
+ connectedIdentities?: Array<{
350
+ provider: string;
351
+ userId?: string;
352
+ accessToken?: string;
353
+ }>;
308
354
  }
309
355
  /**
310
356
  * ChatBase component - Universal chat panel supporting store, protocol, and custom modes
311
357
  */
312
- export declare function ChatBase({ title, showHeader, showLoadingIndicator, showErrors, showInput, showModelSelector, showToolsMenu, initialModel, initialMcpServers, className, loadingState, headerActions, useStore: useStoreMode, protocol, onSendMessage, enableStreaming, brandIcon, avatarConfig, headerButtons, showPoweredBy, poweredByProps, emptyState, renderToolResult, footerContent, headerContent, children, borderRadius, backgroundColor, border, boxShadow, compact, placeholder, description, onStateUpdate, onNewChat, onClear, onMessagesChange, autoFocus, suggestions, submitOnSuggestionClick, hideMessagesAfterToolUI, focusTrigger, frontendTools, }: ChatBaseProps): import("react/jsx-runtime").JSX.Element;
358
+ export declare function ChatBase({ title, showHeader, showLoadingIndicator, showErrors, showInput, showModelSelector, showToolsMenu, showSkillsMenu, codemodeEnabled, initialModel, initialMcpServers, initialSkills, className, loadingState, headerActions, useStore: useStoreMode, protocol, onSendMessage, enableStreaming, brandIcon, avatarConfig, headerButtons, showPoweredBy, poweredByProps, emptyState, renderToolResult, footerContent, headerContent, children, borderRadius, backgroundColor, border, boxShadow, compact, placeholder, description, onStateUpdate, onNewChat, onClear, onMessagesChange, autoFocus, suggestions, submitOnSuggestionClick, hideMessagesAfterToolUI, focusTrigger, frontendTools, onAuthorizationRequired, connectedIdentities, }: ChatBaseProps): import("react/jsx-runtime").JSX.Element;
313
359
  export type { PoweredByTagProps };
314
360
  export default ChatBase;
@@ -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, initialModel, initialMcpServers, 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, initialModel: initialModel, initialMcpServers: initialMcpServers, 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, initialModel: initialModel, initialMcpServers: initialMcpServers, 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, initialModel, initialMcpServers, 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,6 +319,11 @@ 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) {
@@ -315,6 +373,12 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
315
373
  }
316
374
  }
317
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]);
318
382
  // Helper to toggle MCP tool enabled state
319
383
  const toggleMcpTool = useCallback((serverId, toolName) => {
320
384
  setEnabledMcpTools(prev => {
@@ -343,6 +407,23 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
343
407
  return newMap;
344
408
  });
345
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
+ }, []);
346
427
  // Get all enabled MCP tool names (for sending with requests)
347
428
  const getEnabledMcpToolNames = useCallback(() => {
348
429
  const toolNames = [];
@@ -351,6 +432,10 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
351
432
  });
352
433
  return toolNames;
353
434
  }, [enabledMcpTools]);
435
+ // Get all enabled skill IDs (for sending with requests)
436
+ const getEnabledSkillIds = useCallback(() => {
437
+ return Array.from(enabledSkills);
438
+ }, [enabledSkills]);
354
439
  // Load messages from store on mount when useStoreMode is enabled
355
440
  useEffect(() => {
356
441
  if (useStoreMode) {
@@ -363,10 +448,16 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
363
448
  // Derived state
364
449
  const messages = displayItems.filter((item) => !isToolCallMessage(item));
365
450
  const ready = true;
366
- // 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)
367
454
  useEffect(() => {
368
- onMessagesChange?.(messages);
369
- }, [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
370
461
  // Padding based on compact mode
371
462
  const padding = compact ? 2 : 3;
372
463
  // Default avatar config
@@ -573,16 +664,58 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
573
664
  const isHumanInTheLoop = existingToolCall.args &&
574
665
  'steps' in existingToolCall.args &&
575
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
+ }
576
706
  const updatedToolCall = {
577
707
  ...existingToolCall,
578
708
  result: event.toolResult.result,
579
709
  // Keep executing for HITL, otherwise mark complete/error
580
- status: event.toolResult.error
710
+ status: hasError
581
711
  ? 'error'
582
712
  : isHumanInTheLoop
583
713
  ? 'executing'
584
714
  : 'complete',
585
715
  error: event.toolResult.error,
716
+ executionError,
717
+ codeError,
718
+ exitCode,
586
719
  };
587
720
  toolCallsRef.current.set(toolCallId, updatedToolCall);
588
721
  setDisplayItems(prev => prev.map(item => isToolCallMessage(item) && item.toolCallId === toolCallId
@@ -749,8 +882,9 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
749
882
  description: tool.description,
750
883
  parameters: tool.parameters || { type: 'object', properties: {} },
751
884
  }));
752
- // Get enabled MCP tool names
885
+ // Get enabled MCP tool names and skill IDs
753
886
  const enabledMcpToolNames = getEnabledMcpToolNames();
887
+ const enabledSkillIds = getEnabledSkillIds();
754
888
  await adapterRef.current.sendMessage(userMessage, {
755
889
  threadId: threadIdRef.current,
756
890
  messages: allMessages,
@@ -758,6 +892,10 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
758
892
  tools: toolsForRequest,
759
893
  // Include enabled MCP tools as builtin_tools for backend
760
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,
761
899
  });
762
900
  }
763
901
  }
@@ -785,6 +923,7 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
785
923
  onSendMessage,
786
924
  enableStreaming,
787
925
  getEnabledMcpToolNames,
926
+ getEnabledSkillIds,
788
927
  ]);
789
928
  // Handle stop
790
929
  const handleStop = useCallback(() => {
@@ -1043,7 +1182,7 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
1043
1182
  status: item.status,
1044
1183
  error: item.error,
1045
1184
  respond,
1046
- })) : (_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 }));
1047
1186
  // Skip if custom render returns null/undefined
1048
1187
  if (toolUI === null || toolUI === undefined)
1049
1188
  return null;
@@ -1306,7 +1445,8 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
1306
1445
  maxHeight: '120px',
1307
1446
  overflow: 'hidden',
1308
1447
  transition: 'height 0.1s ease-out',
1309
- }, 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: {
1310
1450
  display: 'flex',
1311
1451
  gap: 2,
1312
1452
  px: padding,
@@ -1315,61 +1455,100 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
1315
1455
  borderColor: 'border.default',
1316
1456
  alignItems: 'center',
1317
1457
  bg: 'canvas.subtle',
1318
- }, 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: {
1319
1460
  maxHeight: '60vh',
1320
1461
  overflowY: 'auto',
1321
- }, children: _jsx(ActionList, { children: configQuery.data?.mcpServers &&
1322
- configQuery.data.mcpServers.length > 0 ? (configQuery.data.mcpServers.map(server => {
1323
- const serverTools = enabledMcpTools.get(server.id);
1324
- const allToolNames = server.tools.map(t => t.name);
1325
- const enabledCount = serverTools?.size ?? 0;
1326
- const allEnabled = enabledCount === allToolNames.length &&
1327
- allToolNames.length > 0;
1328
- return (_jsxs(ActionList.Group, { title: `${server.name}${server.isAvailable ? '' : ' (unavailable)'}`, children: [server.isAvailable &&
1329
- server.tools.length > 0 && (_jsxs(Box, { sx: {
1330
- display: 'flex',
1331
- alignItems: 'center',
1332
- justifyContent: 'space-between',
1333
- px: 3,
1334
- py: 2,
1335
- borderBottom: '1px solid',
1336
- borderColor: 'border.muted',
1337
- }, children: [_jsxs(Text, { id: `toggle-all-${server.id}`, sx: {
1338
- fontSize: 0,
1339
- fontWeight: 'semibold',
1340
- color: 'fg.muted',
1341
- }, 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 => {
1342
- const isEnabled = serverTools?.has(tool.name) ?? false;
1343
- 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: {
1344
1471
  display: 'flex',
1345
1472
  alignItems: 'center',
1346
1473
  justifyContent: 'space-between',
1347
1474
  px: 3,
1348
1475
  py: 2,
1349
- '&:hover': {
1350
- backgroundColor: 'canvas.subtle',
1351
- },
1352
- }, 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: {
1353
- display: 'block',
1354
- fontSize: 0,
1355
- color: 'fg.muted',
1356
- overflow: 'hidden',
1357
- textOverflow: 'ellipsis',
1358
- whiteSpace: 'nowrap',
1359
- }, 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}`));
1360
- })) : server.isAvailable ? (_jsx(ActionList.Item, { disabled: true, children: _jsx(Text, { sx: {
1361
- color: 'fg.muted',
1362
- fontStyle: 'italic',
1363
- }, 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',
1364
1532
  color: 'fg.muted',
1365
- fontStyle: 'italic',
1366
- }, children: "Server unavailable" }) }))] }, server.id));
1367
- })) : (_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: {
1368
- width: 8,
1369
- height: 8,
1370
- borderRadius: '50%',
1371
- backgroundColor: 'success.emphasis',
1372
- } }) }), 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: {
1373
1552
  display: 'flex',
1374
1553
  flexDirection: 'column',
1375
1554
  alignItems: 'flex-end',
@@ -1397,7 +1576,7 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
1397
1576
  bg: 'danger.subtle',
1398
1577
  borderBottom: '1px solid',
1399
1578
  borderColor: 'danger.muted',
1400
- }, 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: {
1401
1580
  display: 'flex',
1402
1581
  flexDirection: 'column',
1403
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;