@datalayer/agent-runtimes 0.0.7 → 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.
- package/README.md +9 -0
- package/lib/components/chat/components/AgentDetails.d.ts +14 -1
- package/lib/components/chat/components/AgentDetails.js +3 -2
- package/lib/components/chat/components/AgentIdentity.d.ts +92 -0
- package/lib/components/chat/components/AgentIdentity.js +318 -0
- package/lib/components/chat/components/Chat.d.ts +20 -1
- package/lib/components/chat/components/Chat.js +16 -3
- package/lib/components/chat/components/ChatFloating.d.ts +6 -1
- package/lib/components/chat/components/ChatFloating.js +12 -6
- package/lib/components/chat/components/base/ChatBase.d.ts +47 -1
- package/lib/components/chat/components/base/ChatBase.js +242 -63
- package/lib/components/chat/components/display/ToolCallDisplay.d.ts +16 -2
- package/lib/components/chat/components/display/ToolCallDisplay.js +148 -6
- package/lib/components/chat/components/display/index.d.ts +1 -1
- package/lib/components/chat/components/display/index.js +1 -1
- package/lib/components/chat/components/elements/ChatInputPrompt.d.ts +12 -1
- package/lib/components/chat/components/elements/ChatInputPrompt.js +8 -3
- package/lib/components/chat/components/index.d.ts +1 -0
- package/lib/components/chat/components/index.js +1 -0
- package/lib/components/chat/components/parts/ToolPart.d.ts +1 -1
- package/lib/components/chat/components/parts/ToolPart.js +142 -6
- package/lib/components/chat/index.d.ts +1 -1
- package/lib/components/chat/index.js +1 -1
- package/lib/components/chat/protocols/A2AAdapter.d.ts +9 -0
- package/lib/components/chat/protocols/A2AAdapter.js +13 -2
- package/lib/components/chat/protocols/ACPAdapter.d.ts +9 -0
- package/lib/components/chat/protocols/ACPAdapter.js +13 -2
- package/lib/components/chat/protocols/AGUIAdapter.d.ts +9 -0
- package/lib/components/chat/protocols/AGUIAdapter.js +19 -1
- package/lib/components/chat/protocols/VercelAIAdapter.d.ts +7 -0
- package/lib/components/chat/protocols/VercelAIAdapter.js +19 -0
- package/lib/components/chat/types/execution.d.ts +78 -0
- package/lib/components/chat/types/execution.js +64 -0
- package/lib/components/chat/types/index.d.ts +1 -0
- package/lib/components/chat/types/index.js +1 -0
- package/lib/components/chat/types/protocol.d.ts +9 -0
- package/lib/components/ui/pagination.d.ts +2 -2
- package/lib/components/ui/pagination.js +4 -4
- package/lib/components/ui/resizable.d.ts +4 -4
- package/lib/components/ui/resizable.js +4 -4
- package/lib/examples/A2UiRestaurantExample.js +2 -2
- package/lib/examples/AgUiAgenticExample.js +2 -2
- package/lib/examples/AgUiBackendToolRenderingExample.js +2 -2
- package/lib/examples/AgUiHaikuGenUIExample.js +2 -2
- package/lib/examples/AgUiHumanInTheLoopExample.js +2 -2
- package/lib/examples/AgUiSharedStateExample.js +2 -2
- package/lib/examples/AgUiToolsBasedGenUIExample.js +2 -2
- package/lib/examples/AgentRuntimeCustomExample.js +2 -2
- package/lib/examples/AgentRuntimeLexical2Example.js +2 -1
- package/lib/examples/AgentRuntimeLexicalExample.js +5 -2
- package/lib/examples/AgentRuntimeLexicalSidebarExample.js +4 -2
- package/lib/examples/AgentRuntimeNotebookExample.js +1 -1
- package/lib/examples/AgentRuntimeStandaloneExample.js +2 -2
- package/lib/examples/AgentSpaceFormExample.d.ts +70 -2
- package/lib/examples/AgentSpaceFormExample.js +177 -43
- package/lib/examples/CopilotKitLexicalExample.js +2 -1
- package/lib/examples/components/AgentConfiguration.d.ts +17 -2
- package/lib/examples/components/AgentConfiguration.js +220 -16
- package/lib/examples/components/LexicalEditor.js +2 -1
- package/lib/examples/components/MockFileBrowser.js +6 -2
- package/lib/examples/components/index.d.ts +0 -1
- package/lib/examples/components/index.js +0 -1
- package/lib/examples/example-selector.js +0 -1
- package/lib/examples/index.d.ts +0 -1
- package/lib/examples/index.js +0 -1
- package/lib/examples/lexical/editorConfig.d.ts +3 -2
- package/lib/examples/lexical/editorConfig.js +7 -1
- package/lib/examples/lexical/initial-content.json +2210 -0
- package/lib/examples/main.js +15 -1
- package/lib/identity/IdentityConnect.d.ts +90 -0
- package/lib/identity/IdentityConnect.js +316 -0
- package/lib/identity/OAuthCallback.d.ts +58 -0
- package/lib/identity/OAuthCallback.js +223 -0
- package/lib/identity/dcr.d.ts +257 -0
- package/lib/identity/dcr.js +282 -0
- package/lib/identity/identityStore.d.ts +72 -0
- package/lib/identity/identityStore.js +529 -0
- package/lib/identity/index.d.ts +46 -0
- package/lib/identity/index.js +17 -0
- package/lib/identity/pkce.d.ts +30 -0
- package/lib/identity/pkce.js +65 -0
- package/lib/identity/types.d.ts +293 -0
- package/lib/identity/types.js +73 -0
- package/lib/identity/useIdentity.d.ts +108 -0
- package/lib/identity/useIdentity.js +323 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/lib/utils.js +1 -1
- package/lib/renderers/a2ui/lib/utils.js +1 -1
- package/lib/test-setup.d.ts +1 -1
- package/lib/test-setup.js +1 -0
- package/lib/tools/adapters/agent-runtimes/AgentRuntimesToolAdapter.js +32 -1
- package/lib/tools/adapters/agent-runtimes/lexicalHooks.d.ts +6 -0
- package/lib/tools/adapters/agent-runtimes/lexicalHooks.js +16 -17
- package/package.json +20 -7
- package/patches/@datalayer+jupyter-lexical+1.0.8.patch +11628 -0
- package/patches/@datalayer+jupyter-react+2.0.2.patch +5338 -0
- package/lib/examples/AgentSpaceHomeExample.d.ts +0 -8
- package/lib/examples/AgentSpaceHomeExample.js +0 -171
- package/lib/examples/components/AgentsDataTable.d.ts +0 -13
- package/lib/examples/components/AgentsDataTable.js +0 -74
- package/lib/examples/components/Rating.d.ts +0 -14
- 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
|
|
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
|
-
//
|
|
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
|
-
|
|
369
|
-
|
|
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:
|
|
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
|
|
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(
|
|
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
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
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
|
-
'
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
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
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
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;
|