@datalayer/agent-runtimes 0.0.5 → 0.0.7

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 (31) hide show
  1. package/README.md +141 -22
  2. package/lib/components/chat/components/AgentDetails.d.ts +1 -1
  3. package/lib/components/chat/components/AgentDetails.js +7 -92
  4. package/lib/components/chat/components/Chat.d.ts +5 -1
  5. package/lib/components/chat/components/Chat.js +28 -19
  6. package/lib/components/chat/components/ContextDistribution.d.ts +47 -0
  7. package/lib/components/chat/components/ContextDistribution.js +146 -0
  8. package/lib/components/chat/components/ContextUsage.d.ts +33 -0
  9. package/lib/components/chat/components/ContextUsage.js +127 -0
  10. package/lib/components/chat/components/base/ChatBase.d.ts +5 -1
  11. package/lib/components/chat/components/base/ChatBase.js +40 -15
  12. package/lib/components/chat/components/index.d.ts +2 -0
  13. package/lib/components/chat/components/index.js +2 -0
  14. package/lib/examples/AgentSpaceFormExample.js +41 -6
  15. package/lib/examples/components/AgentConfiguration.d.ts +22 -0
  16. package/lib/examples/components/AgentConfiguration.js +37 -10
  17. package/lib/examples/components/Header.d.ts +0 -2
  18. package/lib/examples/components/Header.js +2 -16
  19. package/lib/index.d.ts +1 -0
  20. package/lib/index.js +1 -0
  21. package/lib/runtime/index.d.ts +35 -0
  22. package/lib/runtime/index.js +40 -0
  23. package/lib/runtime/runtimeStore.d.ts +77 -0
  24. package/lib/runtime/runtimeStore.js +184 -0
  25. package/lib/runtime/types.d.ts +84 -0
  26. package/lib/runtime/types.js +15 -0
  27. package/lib/runtime/useAgentConnection.d.ts +46 -0
  28. package/lib/runtime/useAgentConnection.js +112 -0
  29. package/lib/runtime/useAgentRuntime.d.ts +94 -0
  30. package/lib/runtime/useAgentRuntime.js +125 -0
  31. package/package.json +1 -1
@@ -0,0 +1,127 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // Copyright (c) 2025-2026 Datalayer, Inc.
3
+ // Distributed under the terms of the Modified BSD License.
4
+ /**
5
+ * ContextUsage component - Shows context usage details with token breakdown.
6
+ */
7
+ import { CommentDiscussionIcon, DatabaseIcon, FileIcon, ToolsIcon, ClockIcon, } from '@primer/octicons-react';
8
+ import { Box, Heading, Text, ProgressBar, Spinner } from '@primer/react';
9
+ import { useQuery } from '@tanstack/react-query';
10
+ function getLocalApiBase() {
11
+ if (typeof window === 'undefined') {
12
+ return '';
13
+ }
14
+ const host = window.location.hostname;
15
+ return host === 'localhost' || host === '127.0.0.1'
16
+ ? 'http://127.0.0.1:8765'
17
+ : '';
18
+ }
19
+ /**
20
+ * Format token count for display
21
+ */
22
+ function formatTokens(tokens) {
23
+ if (tokens >= 1000000) {
24
+ return `${(tokens / 1000000).toFixed(1)}M`;
25
+ }
26
+ if (tokens >= 1000) {
27
+ return `${(tokens / 1000).toFixed(1)}K`;
28
+ }
29
+ return tokens.toString();
30
+ }
31
+ /**
32
+ * Get icon for context category
33
+ */
34
+ function getCategoryIcon(name) {
35
+ switch (name.toLowerCase()) {
36
+ case 'files':
37
+ return FileIcon;
38
+ case 'messages':
39
+ return CommentDiscussionIcon;
40
+ case 'tools':
41
+ return ToolsIcon;
42
+ case 'memory':
43
+ return DatabaseIcon;
44
+ case 'system':
45
+ return FileIcon;
46
+ case 'cache':
47
+ return DatabaseIcon;
48
+ default:
49
+ return ClockIcon;
50
+ }
51
+ }
52
+ /**
53
+ * ContextUsage component displays token usage breakdown by category.
54
+ */
55
+ export function ContextUsage({ agentId }) {
56
+ const { data: contextData, isLoading, error, } = useQuery({
57
+ queryKey: ['context-details', agentId],
58
+ queryFn: async () => {
59
+ const apiBase = getLocalApiBase();
60
+ const response = await fetch(`${apiBase}/api/v1/configure/agents/${encodeURIComponent(agentId)}/context-details`);
61
+ if (!response.ok) {
62
+ throw new Error('Failed to fetch context details');
63
+ }
64
+ return response.json();
65
+ },
66
+ refetchInterval: 10000, // Refresh every 10 seconds
67
+ refetchOnMount: 'always', // Always refetch when component mounts
68
+ staleTime: 0, // Data is always considered stale
69
+ });
70
+ if (isLoading) {
71
+ return (_jsxs(Box, { children: [_jsx(Heading, { as: "h4", sx: {
72
+ fontSize: 1,
73
+ fontWeight: 'semibold',
74
+ mb: 2,
75
+ color: 'fg.muted',
76
+ }, children: "Cumulative Context Usage" }), _jsxs(Box, { sx: {
77
+ p: 3,
78
+ bg: 'canvas.subtle',
79
+ borderRadius: 2,
80
+ border: '1px solid',
81
+ borderColor: 'border.default',
82
+ display: 'flex',
83
+ alignItems: 'center',
84
+ gap: 2,
85
+ }, children: [_jsx(Spinner, { size: "small" }), _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "Loading context details..." })] })] }));
86
+ }
87
+ if (error || !contextData) {
88
+ return (_jsxs(Box, { children: [_jsx(Heading, { as: "h4", sx: {
89
+ fontSize: 1,
90
+ fontWeight: 'semibold',
91
+ mb: 2,
92
+ color: 'fg.muted',
93
+ }, children: "Cumulative Context Usage" }), _jsx(Box, { sx: {
94
+ p: 3,
95
+ bg: 'canvas.subtle',
96
+ borderRadius: 2,
97
+ border: '1px solid',
98
+ borderColor: 'border.default',
99
+ }, children: _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "Failed to load context details" }) })] }));
100
+ }
101
+ const contextUsagePercent = (contextData.usedTokens / contextData.totalTokens) * 100;
102
+ return (_jsxs(Box, { children: [_jsx(Heading, { as: "h4", sx: {
103
+ fontSize: 1,
104
+ fontWeight: 'semibold',
105
+ mb: 2,
106
+ color: 'fg.muted',
107
+ }, children: "Cumulative Context Usage" }), _jsxs(Box, { sx: {
108
+ p: 3,
109
+ bg: 'canvas.subtle',
110
+ borderRadius: 2,
111
+ border: '1px solid',
112
+ borderColor: 'border.default',
113
+ }, children: [_jsxs(Box, { sx: { mb: 3 }, children: [_jsxs(Box, { sx: {
114
+ display: 'flex',
115
+ justifyContent: 'space-between',
116
+ mb: 1,
117
+ }, children: [_jsxs(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: [formatTokens(contextData.usedTokens), " /", ' ', formatTokens(contextData.totalTokens), " tokens"] }), _jsxs(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: [contextUsagePercent.toFixed(0), "%"] })] }), _jsx(ProgressBar, { progress: contextUsagePercent, sx: { height: 8 }, bg: contextUsagePercent > 80 ? 'danger.emphasis' : 'accent.emphasis' })] }), _jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: contextData.children.map(category => {
118
+ const CategoryIcon = getCategoryIcon(category.name);
119
+ const categoryPercent = (category.value / contextData.totalTokens) * 100;
120
+ return (_jsxs(Box, { sx: {
121
+ display: 'flex',
122
+ alignItems: 'center',
123
+ gap: 2,
124
+ }, children: [_jsx(Box, { sx: { color: 'fg.muted', width: 20 }, children: _jsx(CategoryIcon, { size: 16 }) }), _jsx(Text, { sx: { fontSize: 1, flex: 1 }, children: category.name }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', minWidth: 60 }, children: formatTokens(category.value) }), _jsx(Box, { sx: { width: 80 }, children: _jsx(ProgressBar, { progress: categoryPercent, sx: { height: 4 } }) })] }, category.name));
125
+ }) })] })] }));
126
+ }
127
+ export default ContextUsage;
@@ -201,6 +201,10 @@ export interface ChatBaseProps {
201
201
  showModelSelector?: boolean;
202
202
  /** Show tools menu (for protocols that support it) */
203
203
  showToolsMenu?: boolean;
204
+ /** Initial model ID to select (e.g., 'openai:gpt-4o-mini') */
205
+ initialModel?: string;
206
+ /** Initial MCP server IDs to enable (others will be disabled) */
207
+ initialMcpServers?: string[];
204
208
  /** Custom class name */
205
209
  className?: string;
206
210
  /** Custom loading state */
@@ -305,6 +309,6 @@ export interface ChatBaseProps {
305
309
  /**
306
310
  * ChatBase component - Universal chat panel supporting store, protocol, and custom modes
307
311
  */
308
- export declare function ChatBase({ title, showHeader, showLoadingIndicator, showErrors, showInput, showModelSelector, showToolsMenu, 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;
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;
309
313
  export type { PoweredByTagProps };
310
314
  export default ChatBase;
@@ -160,7 +160,7 @@ function useConfigQuery(enabled, configEndpoint, authToken) {
160
160
  /**
161
161
  * ChatBase component - Universal chat panel supporting store, protocol, and custom modes
162
162
  */
163
- export function ChatBase({ title, showHeader = false, showLoadingIndicator = true, showErrors = true, showInput = true, showModelSelector = false, showToolsMenu = false, className, loadingState, headerActions,
163
+ export function ChatBase({ title, showHeader = false, showLoadingIndicator = true, showErrors = true, showInput = true, showModelSelector = false, showToolsMenu = false, initialModel, initialMcpServers, className, loadingState, headerActions,
164
164
  // Mode selection
165
165
  useStore: useStoreMode = true, protocol, onSendMessage, enableStreaming = false,
166
166
  // Extended props
@@ -169,15 +169,15 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
169
169
  const existingQueryClient = useContext(QueryClientContext);
170
170
  // If no QueryClient is available, wrap with our internal provider
171
171
  if (!existingQueryClient) {
172
- return (_jsx(QueryClientProvider, { client: internalQueryClient, children: _jsx(ChatBaseInner, { title: title, showHeader: showHeader, showLoadingIndicator: showLoadingIndicator, showErrors: showErrors, showInput: showInput, showModelSelector: showModelSelector, showToolsMenu: showToolsMenu, className: className, loadingState: loadingState, headerActions: headerActions, useStore: useStoreMode, protocol: protocol, onSendMessage: onSendMessage, enableStreaming: enableStreaming, brandIcon: brandIcon, avatarConfig: avatarConfig, headerButtons: headerButtons, showPoweredBy: showPoweredBy, poweredByProps: poweredByProps, emptyState: emptyState, renderToolResult: renderToolResult, footerContent: footerContent, headerContent: headerContent, children: children, borderRadius: borderRadius, backgroundColor: backgroundColor, border: border, boxShadow: boxShadow, compact: compact, placeholder: placeholder, description: description, onStateUpdate: onStateUpdate, onNewChat: onNewChat, onClear: onClear, onMessagesChange: onMessagesChange, autoFocus: autoFocus, suggestions: suggestions, submitOnSuggestionClick: submitOnSuggestionClick, hideMessagesAfterToolUI: hideMessagesAfterToolUI, focusTrigger: focusTrigger, frontendTools: frontendTools }) }));
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 }) }));
173
173
  }
174
174
  // QueryClient already available, render inner component directly
175
- return (_jsx(ChatBaseInner, { title: title, showHeader: showHeader, showLoadingIndicator: showLoadingIndicator, showErrors: showErrors, showInput: showInput, showModelSelector: showModelSelector, showToolsMenu: showToolsMenu, className: className, loadingState: loadingState, headerActions: headerActions, useStore: useStoreMode, protocol: protocol, onSendMessage: onSendMessage, enableStreaming: enableStreaming, brandIcon: brandIcon, avatarConfig: avatarConfig, headerButtons: headerButtons, showPoweredBy: showPoweredBy, poweredByProps: poweredByProps, emptyState: emptyState, renderToolResult: renderToolResult, footerContent: footerContent, headerContent: headerContent, children: children, borderRadius: borderRadius, backgroundColor: backgroundColor, border: border, boxShadow: boxShadow, compact: compact, placeholder: placeholder, description: description, onStateUpdate: onStateUpdate, onNewChat: onNewChat, onClear: onClear, onMessagesChange: onMessagesChange, autoFocus: autoFocus, suggestions: suggestions, submitOnSuggestionClick: submitOnSuggestionClick, hideMessagesAfterToolUI: hideMessagesAfterToolUI, focusTrigger: focusTrigger, frontendTools: frontendTools }));
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 }));
176
176
  }
177
177
  /**
178
178
  * Inner ChatBase component - contains all the actual logic
179
179
  */
180
- function ChatBaseInner({ title, showHeader = false, showLoadingIndicator = true, showErrors = true, showInput = true, showModelSelector = false, showToolsMenu = false, className, loadingState, headerActions,
180
+ function ChatBaseInner({ title, showHeader = false, showLoadingIndicator = true, showErrors = true, showInput = true, showModelSelector = false, showToolsMenu = false, initialModel, initialMcpServers, className, loadingState, headerActions,
181
181
  // Mode selection
182
182
  useStore: useStoreMode = true, protocol, onSendMessage, enableStreaming = false,
183
183
  // Extended props
@@ -269,27 +269,52 @@ brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, e
269
269
  // Initialize model and tools when config is available
270
270
  useEffect(() => {
271
271
  if (configQuery.data && !selectedModel) {
272
- // Select first available model, or fallback to first model if none available
273
- const firstAvailableModel = configQuery.data.models.find(m => m.isAvailable !== false);
274
- const firstModel = firstAvailableModel || configQuery.data.models[0];
275
- if (firstModel) {
276
- setSelectedModel(firstModel.id);
277
- const allToolIds = configQuery.data.builtinTools?.map(tool => tool.id) || [];
278
- setEnabledTools(allToolIds);
272
+ // Use initialModel if provided, otherwise select first available model
273
+ if (initialModel) {
274
+ // Check if the initial model exists in the config
275
+ const modelExists = configQuery.data.models.some(m => m.id === initialModel);
276
+ if (modelExists) {
277
+ setSelectedModel(initialModel);
278
+ }
279
+ else {
280
+ // Fallback to first available model if initialModel not found
281
+ const firstAvailableModel = configQuery.data.models.find(m => m.isAvailable !== false);
282
+ const firstModel = firstAvailableModel || configQuery.data.models[0];
283
+ if (firstModel) {
284
+ setSelectedModel(firstModel.id);
285
+ }
286
+ }
279
287
  }
280
- // Initialize MCP server tools - all enabled by default
288
+ else {
289
+ // No initialModel provided, select first available model
290
+ const firstAvailableModel = configQuery.data.models.find(m => m.isAvailable !== false);
291
+ const firstModel = firstAvailableModel || configQuery.data.models[0];
292
+ if (firstModel) {
293
+ setSelectedModel(firstModel.id);
294
+ }
295
+ }
296
+ const allToolIds = configQuery.data.builtinTools?.map(tool => tool.id) || [];
297
+ setEnabledTools(allToolIds);
298
+ // Initialize MCP server tools
281
299
  if (configQuery.data.mcpServers) {
282
300
  const newEnabledMcpTools = new Map();
283
301
  for (const server of configQuery.data.mcpServers) {
284
302
  if (server.isAvailable && server.enabled) {
285
- const enabledToolNames = new Set(server.tools.filter(t => t.enabled).map(t => t.name));
286
- newEnabledMcpTools.set(server.id, enabledToolNames);
303
+ // If initialMcpServers is provided, only enable those servers
304
+ // If not provided, enable all available servers
305
+ const shouldEnableServer = initialMcpServers
306
+ ? initialMcpServers.includes(server.id)
307
+ : true;
308
+ if (shouldEnableServer) {
309
+ const enabledToolNames = new Set(server.tools.filter(t => t.enabled).map(t => t.name));
310
+ newEnabledMcpTools.set(server.id, enabledToolNames);
311
+ }
287
312
  }
288
313
  }
289
314
  setEnabledMcpTools(newEnabledMcpTools);
290
315
  }
291
316
  }
292
- }, [configQuery.data, selectedModel]);
317
+ }, [configQuery.data, selectedModel, initialModel, initialMcpServers]);
293
318
  // Helper to toggle MCP tool enabled state
294
319
  const toggleMcpTool = useCallback((serverId, toolName) => {
295
320
  setEnabledMcpTools(prev => {
@@ -9,6 +9,8 @@ export { ChatSidebar, type ChatSidebarProps } from './ChatSidebar';
9
9
  export { ChatStandalone, type ChatStandaloneProps, type MessageHandler, } from './ChatStandalone';
10
10
  export { ChatBase, type ChatBaseProps, type ProtocolConfig, } from './base/ChatBase';
11
11
  export { AgentDetails, type AgentDetailsProps } from './AgentDetails';
12
+ export { ContextUsage, type ContextUsageProps, type ContextDetailsResponse, } from './ContextUsage';
13
+ export { ContextDistribution, type ContextDistributionProps, type ContextSnapshotResponse, } from './ContextDistribution';
12
14
  export { ToolApprovalDialog, useToolApprovalDialog, type ToolApprovalDialogProps, } from './elements/ToolApprovalDialog';
13
15
  export { PoweredByTag, type PoweredByTagProps } from './elements/PoweredByTag';
14
16
  export { FloatingBrandButton, type FloatingBrandButtonProps, } from './elements/FloatingBrandButton';
@@ -13,6 +13,8 @@ export { ChatSidebar } from './ChatSidebar';
13
13
  export { ChatStandalone, } from './ChatStandalone';
14
14
  export { ChatBase, } from './base/ChatBase';
15
15
  export { AgentDetails } from './AgentDetails';
16
+ export { ContextUsage, } from './ContextUsage';
17
+ export { ContextDistribution, } from './ContextDistribution';
16
18
  export { ToolApprovalDialog, useToolApprovalDialog, } from './elements/ToolApprovalDialog';
17
19
  export { PoweredByTag } from './elements/PoweredByTag';
18
20
  export { FloatingBrandButton, } from './elements/FloatingBrandButton';
@@ -59,12 +59,31 @@ const AgentSpaceFormExample = () => {
59
59
  const [agentLibrary, setAgentLibrary] = useState('pydantic-ai');
60
60
  const [transport, setTransport] = useState('ag-ui');
61
61
  const [extensions, setExtensions] = useState([]);
62
+ const [model, setModel] = useState('openai:gpt-4o-mini');
62
63
  const [isConfigured, setIsConfigured] = useState(false);
64
+ // Agent capabilities state (moved from Header toggles)
65
+ const [enableSkills, setEnableSkills] = useState(false);
66
+ const [enableCodemode, setEnableCodemode] = useState(false);
67
+ const [allowDirectToolCalls, setAllowDirectToolCalls] = useState(false);
68
+ const [enableToolReranker, setEnableToolReranker] = useState(false);
69
+ const [selectedMcpServers, setSelectedMcpServers] = useState([]);
70
+ // Handle codemode change - clear MCP servers when enabled
71
+ const handleEnableCodemodeChange = (enabled) => {
72
+ setEnableCodemode(enabled);
73
+ if (enabled) {
74
+ // Clear selected MCP servers when codemode is enabled
75
+ setSelectedMcpServers([]);
76
+ }
77
+ if (!enabled) {
78
+ setAllowDirectToolCalls(false);
79
+ setEnableToolReranker(false);
80
+ }
81
+ };
63
82
  // UI state
64
83
  const [activeSession, setActiveSession] = useState('session-1');
65
84
  const [richEditor, setRichEditor] = useState(false);
66
85
  const [durable, setDurable] = useState(true);
67
- const [codemode, setCodemode] = useState(false);
86
+ const [codemode, _] = useState(false);
68
87
  const [showContextTree, setShowContextTree] = useState(false);
69
88
  const [showNotebook] = useState(true);
70
89
  const [leftPaneVisible, setLeftPaneVisible] = useState(true);
@@ -116,8 +135,13 @@ const AgentSpaceFormExample = () => {
116
135
  description: `Agent created via UI (${agentLibrary})`,
117
136
  agent_library: agentLibrary,
118
137
  transport: transport,
119
- model: 'openai:gpt-4o-mini',
138
+ model: model,
120
139
  system_prompt: 'You are a helpful AI assistant.',
140
+ enable_skills: enableSkills,
141
+ enable_codemode: enableCodemode,
142
+ allow_direct_tool_calls: allowDirectToolCalls,
143
+ enable_tool_reranker: enableToolReranker,
144
+ selected_mcp_servers: enableCodemode ? [] : selectedMcpServers,
121
145
  }),
122
146
  });
123
147
  if (!response.ok) {
@@ -139,7 +163,18 @@ const AgentSpaceFormExample = () => {
139
163
  finally {
140
164
  setIsCreatingAgent(false);
141
165
  }
142
- }, [baseUrl, agentName, agentLibrary, transport]);
166
+ }, [
167
+ baseUrl,
168
+ agentName,
169
+ agentLibrary,
170
+ transport,
171
+ model,
172
+ enableSkills,
173
+ enableCodemode,
174
+ allowDirectToolCalls,
175
+ enableToolReranker,
176
+ selectedMcpServers,
177
+ ]);
143
178
  /**
144
179
  * Delete an agent via the API
145
180
  */
@@ -206,7 +241,7 @@ const AgentSpaceFormExample = () => {
206
241
  };
207
242
  return (_jsx(QueryClientProvider, { client: queryClient, children: _jsx(DatalayerThemeProvider, { theme: datalayerTheme, children: _jsxs(PageLayout, { containerWidth: "full", children: [_jsx(Header, { activeSession: activeSession, agentName: selectedAgentId === 'new-agent' ? undefined : currentAgent?.name, agentDescription: selectedAgentId === 'new-agent'
208
243
  ? undefined
209
- : currentAgent?.description, agentStatus: currentAgent?.status, richEditor: richEditor, durable: durable, codemode: codemode, showContextTree: showContextTree, isNewAgent: selectedAgentId === 'new-agent', isConfigured: isConfigured, onSessionChange: setActiveSession, onRichEditorChange: setRichEditor, onDurableChange: setDurable, onCodemodeChange: setCodemode, onToggleContextTree: () => setShowContextTree(!showContextTree), onToggleStatus: currentAgent
244
+ : currentAgent?.description, agentStatus: currentAgent?.status, richEditor: richEditor, durable: durable, showContextTree: showContextTree, isNewAgent: selectedAgentId === 'new-agent', isConfigured: isConfigured, onSessionChange: setActiveSession, onRichEditorChange: setRichEditor, onDurableChange: setDurable, onToggleContextTree: () => setShowContextTree(!showContextTree), onToggleStatus: currentAgent
210
245
  ? () => toggleAgentStatus(currentAgent.id)
211
246
  : undefined }), leftPaneVisible ? (_jsxs(_Fragment, { children: [_jsx(Box, { sx: {
212
247
  position: 'fixed',
@@ -249,9 +284,9 @@ const AgentSpaceFormExample = () => {
249
284
  display: 'flex',
250
285
  flexDirection: 'column',
251
286
  p: 2,
252
- }, children: !isConfigured ? (_jsx(AgentConfiguration, { agentLibrary: agentLibrary, transport: currentAgent?.transport || transport, extensions: extensions, wsUrl: wsUrl, baseUrl: baseUrl, agentName: agentName, agents: agents, selectedAgentId: selectedAgentId, isCreatingAgent: isCreatingAgent, createError: createError, onAgentLibraryChange: setAgentLibrary, onTransportChange: setTransport, onExtensionsChange: setExtensions, onWsUrlChange: setWsUrl, onBaseUrlChange: setBaseUrl, onAgentNameChange: setAgentName, onAgentSelect: handleAgentSelect, onConnect: handleConnect })) : (
287
+ }, children: !isConfigured ? (_jsx(AgentConfiguration, { agentLibrary: agentLibrary, transport: currentAgent?.transport || transport, extensions: extensions, wsUrl: wsUrl, baseUrl: baseUrl, agentName: agentName, model: model, agents: agents, selectedAgentId: selectedAgentId, isCreatingAgent: isCreatingAgent, createError: createError, enableSkills: enableSkills, enableCodemode: enableCodemode, allowDirectToolCalls: allowDirectToolCalls, enableToolReranker: enableToolReranker, selectedMcpServers: selectedMcpServers, onAgentLibraryChange: setAgentLibrary, onTransportChange: setTransport, onExtensionsChange: setExtensions, onWsUrlChange: setWsUrl, onBaseUrlChange: setBaseUrl, onAgentNameChange: setAgentName, onModelChange: setModel, onAgentSelect: handleAgentSelect, onConnect: handleConnect, onEnableSkillsChange: setEnableSkills, onEnableCodemodeChange: handleEnableCodemodeChange, onAllowDirectToolCallsChange: setAllowDirectToolCalls, onEnableToolRerankerChange: setEnableToolReranker, onSelectedMcpServersChange: setSelectedMcpServers })) : (
253
288
  /* Chat Interface */
254
- _jsx(Box, { sx: { flex: 1 }, children: _jsx(Chat, { transport: currentAgent?.transport || transport, extensions: extensions, wsUrl: wsUrl, baseUrl: baseUrl, agentId: currentAgent?.id || agentName, title: currentAgent?.name || agentName || 'AI Assistant', autoConnect: true, autoFocus: true, placeholder: "Type your message to the agent...", height: "calc(100vh - 250px)", suggestions: [
289
+ _jsx(Box, { sx: { flex: 1 }, children: _jsx(Chat, { transport: currentAgent?.transport || transport, extensions: extensions, wsUrl: wsUrl, baseUrl: baseUrl, agentId: currentAgent?.id || agentName, title: currentAgent?.name || agentName || 'AI Assistant', autoConnect: true, autoFocus: true, placeholder: "Type your message to the agent...", height: "calc(100vh - 250px)", showModelSelector: true, showToolsMenu: true, initialModel: model, initialMcpServers: selectedMcpServers, suggestions: [
255
290
  {
256
291
  title: '👋 Say hello',
257
292
  message: 'Hello! What can you help me with today?',
@@ -43,6 +43,16 @@ declare const EXTENSIONS: {
43
43
  label: string;
44
44
  description: string;
45
45
  }[];
46
+ /**
47
+ * AI Model configuration from backend
48
+ */
49
+ export interface AIModelConfig {
50
+ id: string;
51
+ name: string;
52
+ builtinTools?: string[];
53
+ requiredEnvVars?: string[];
54
+ isAvailable?: boolean;
55
+ }
46
56
  interface AgentConfigurationProps {
47
57
  agentLibrary: AgentLibrary;
48
58
  transport: Transport;
@@ -50,18 +60,30 @@ interface AgentConfigurationProps {
50
60
  wsUrl: string;
51
61
  baseUrl: string;
52
62
  agentName: string;
63
+ model: string;
53
64
  agents: readonly Agent[];
54
65
  selectedAgentId: string;
55
66
  isCreatingAgent?: boolean;
56
67
  createError?: string | null;
68
+ enableSkills?: boolean;
69
+ enableCodemode?: boolean;
70
+ allowDirectToolCalls?: boolean;
71
+ enableToolReranker?: boolean;
72
+ selectedMcpServers?: string[];
57
73
  onAgentLibraryChange: (library: AgentLibrary) => void;
58
74
  onTransportChange: (transport: Transport) => void;
59
75
  onExtensionsChange: (extensions: Extension[]) => void;
60
76
  onWsUrlChange: (url: string) => void;
61
77
  onBaseUrlChange: (url: string) => void;
62
78
  onAgentNameChange: (name: string) => void;
79
+ onModelChange: (model: string) => void;
63
80
  onAgentSelect: (agentId: string) => void;
64
81
  onConnect: () => void;
82
+ onEnableSkillsChange?: (enabled: boolean) => void;
83
+ onEnableCodemodeChange?: (enabled: boolean) => void;
84
+ onAllowDirectToolCallsChange?: (enabled: boolean) => void;
85
+ onEnableToolRerankerChange?: (enabled: boolean) => void;
86
+ onSelectedMcpServersChange?: (servers: string[]) => void;
65
87
  }
66
88
  /**
67
89
  * Agent Configuration Component
@@ -1,11 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- /*
3
- * Copyright (c) 2025-2026 Datalayer, Inc.
4
- * Distributed under the terms of the Modified BSD License.
5
- */
6
- import React from 'react';
7
- import { Text, TextInput, Button, FormControl, Select, Checkbox, Spinner, Flash, Label, ActionList, } from '@primer/react';
8
- import { CheckIcon, XIcon, ToolsIcon } from '@primer/octicons-react';
2
+ import { Text, TextInput, Button, FormControl, Select, Checkbox, Spinner, Flash, Label, } from '@primer/react';
3
+ import { ToolsIcon } from '@primer/octicons-react';
9
4
  import { useQuery } from '@tanstack/react-query';
10
5
  import { Box } from '@datalayer/primer-addons';
11
6
  const AGENT_LIBRARIES = [
@@ -71,7 +66,7 @@ const EXTENSIONS = [
71
66
  *
72
67
  * Form for configuring agent connection settings.
73
68
  */
74
- export const AgentConfiguration = ({ agentLibrary, transport, extensions, wsUrl, baseUrl, agentName, agents, selectedAgentId, isCreatingAgent = false, createError = null, onAgentLibraryChange, onTransportChange, onExtensionsChange, onWsUrlChange, onBaseUrlChange, onAgentNameChange, onAgentSelect, onConnect, }) => {
69
+ export const AgentConfiguration = ({ agentLibrary, transport, extensions, wsUrl, baseUrl, agentName, model, agents, selectedAgentId, isCreatingAgent = false, createError = null, enableSkills = false, enableCodemode = false, allowDirectToolCalls = false, enableToolReranker = false, selectedMcpServers = [], onAgentLibraryChange, onTransportChange, onExtensionsChange, onWsUrlChange, onBaseUrlChange, onAgentNameChange, onModelChange, onAgentSelect, onConnect, onEnableSkillsChange, onEnableCodemodeChange, onAllowDirectToolCallsChange, onEnableToolRerankerChange, onSelectedMcpServersChange, }) => {
75
70
  // Fetch MCP servers configuration from the backend
76
71
  const configQuery = useQuery({
77
72
  queryKey: ['agent-config', baseUrl],
@@ -87,6 +82,18 @@ export const AgentConfiguration = ({ agentLibrary, transport, extensions, wsUrl,
87
82
  retry: 1,
88
83
  });
89
84
  const mcpServers = configQuery.data?.mcpServers || [];
85
+ const models = configQuery.data?.models || [];
86
+ // Handle MCP server checkbox change
87
+ const handleMcpServerChange = (serverId, checked) => {
88
+ if (checked) {
89
+ onSelectedMcpServersChange?.([...selectedMcpServers, serverId]);
90
+ }
91
+ else {
92
+ onSelectedMcpServersChange?.(selectedMcpServers.filter(id => id !== serverId));
93
+ }
94
+ };
95
+ // MCP servers are disabled when codemode is enabled
96
+ const mcpServersDisabled = enableCodemode || selectedAgentId !== 'new-agent';
90
97
  // Determine which extensions are enabled based on transport
91
98
  const isExtensionEnabled = (ext) => {
92
99
  if (selectedAgentId !== 'new-agent')
@@ -119,7 +126,14 @@ export const AgentConfiguration = ({ agentLibrary, transport, extensions, wsUrl,
119
126
  marginBottom: 3,
120
127
  }, children: "Create a new Agent" }), _jsxs(FormControl, { sx: { marginBottom: 3 }, children: [_jsx(FormControl.Label, { children: "Available Agents" }), _jsxs(Select, { value: selectedAgentId, onChange: e => onAgentSelect(e.target.value), sx: { width: '100%' }, children: [_jsx(Select.Option, { value: "new-agent", children: "+ New Agent..." }), agents.map(agent => (_jsxs(Select.Option, { value: agent.id, children: [agent.status === 'running' && '● ', agent.name] }, agent.id)))] }), _jsx(FormControl.Caption, { children: selectedAgentId === 'new-agent'
121
128
  ? 'Configure a new custom agent'
122
- : 'Selected agent - form fields below are disabled' })] }), _jsxs(Box, { sx: { display: 'flex', gap: 3, marginBottom: 3 }, children: [_jsxs(FormControl, { sx: { flex: 1 }, children: [_jsx(FormControl.Label, { children: "Agent Library" }), _jsx(Select, { value: agentLibrary, onChange: e => onAgentLibraryChange(e.target.value), disabled: selectedAgentId !== 'new-agent', sx: { width: '100%' }, children: AGENT_LIBRARIES.map(lib => (_jsxs(Select.Option, { value: lib.value, disabled: lib.disabled, children: [lib.label, lib.disabled && ' (Coming Soon)'] }, lib.value))) })] }), _jsxs(FormControl, { sx: { flex: 1 }, children: [_jsx(FormControl.Label, { children: "Transport" }), _jsx(Select, { value: transport, onChange: e => onTransportChange(e.target.value), disabled: selectedAgentId !== 'new-agent', sx: { width: '100%' }, children: TRANSPORTS.map(t => (_jsx(Select.Option, { value: t.value, children: t.label }, t.value))) })] }), _jsxs(FormControl, { sx: { flex: 1 }, children: [_jsx(FormControl.Label, { children: "Extensions" }), _jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: EXTENSIONS.map(ext => (_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Checkbox, { value: ext.value, checked: extensions.includes(ext.value), disabled: !isExtensionEnabled(ext.value), onChange: e => handleExtensionChange(ext.value, e.target.checked) }), _jsx(Text, { children: ext.label })] }, ext.value))) })] })] }), _jsxs(FormControl, { sx: { marginBottom: 3 }, children: [_jsx(FormControl.Label, { children: transport === 'acp' ? 'WebSocket URL' : 'Base URL' }), _jsx(TextInput, { value: transport === 'acp' ? wsUrl : baseUrl, onChange: e => transport === 'acp'
129
+ : 'Selected agent - form fields below are disabled' })] }), _jsxs(Box, { sx: { display: 'flex', gap: 3, marginBottom: 3 }, children: [_jsxs(FormControl, { sx: { flex: 1 }, children: [_jsx(FormControl.Label, { children: "Agent Library" }), _jsx(Select, { value: agentLibrary, onChange: e => onAgentLibraryChange(e.target.value), disabled: selectedAgentId !== 'new-agent', sx: { width: '100%' }, children: AGENT_LIBRARIES.map(lib => (_jsxs(Select.Option, { value: lib.value, disabled: lib.disabled, children: [lib.label, lib.disabled && ' (Coming Soon)'] }, lib.value))) })] }), _jsxs(FormControl, { sx: { flex: 1 }, children: [_jsx(FormControl.Label, { children: "Model" }), _jsx(Select, { value: model, onChange: e => onModelChange(e.target.value), disabled: selectedAgentId !== 'new-agent' || models.length === 0, sx: { width: '100%' }, children: models.length === 0 ? (_jsx(Select.Option, { value: "", children: "Loading models..." })) : (models.map(m => (_jsxs(Select.Option, { value: m.id, disabled: !m.isAvailable, children: [m.name, !m.isAvailable && ' (API key required)'] }, m.id)))) })] }), _jsxs(FormControl, { sx: { flex: 1 }, children: [_jsx(FormControl.Label, { children: "Transport" }), _jsx(Select, { value: transport, onChange: e => onTransportChange(e.target.value), disabled: selectedAgentId !== 'new-agent', sx: { width: '100%' }, children: TRANSPORTS.map(t => (_jsx(Select.Option, { value: t.value, children: t.label }, t.value))) })] }), _jsxs(FormControl, { sx: { flex: 1 }, children: [_jsx(FormControl.Label, { children: "Extensions" }), _jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: EXTENSIONS.map(ext => (_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Checkbox, { value: ext.value, checked: extensions.includes(ext.value), disabled: !isExtensionEnabled(ext.value), onChange: e => handleExtensionChange(ext.value, e.target.checked) }), _jsx(Text, { children: ext.label })] }, ext.value))) })] })] }), _jsxs(Box, { sx: {
130
+ marginBottom: 3,
131
+ padding: 3,
132
+ border: '1px solid',
133
+ borderColor: 'border.default',
134
+ borderRadius: 2,
135
+ backgroundColor: 'canvas.default',
136
+ }, children: [_jsx(Text, { sx: { fontSize: 1, fontWeight: 'bold', display: 'block', mb: 2 }, children: "Agent Capabilities" }), _jsxs(Box, { sx: { display: 'flex', gap: 4 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Checkbox, { checked: enableSkills, disabled: selectedAgentId !== 'new-agent', onChange: e => onEnableSkillsChange?.(e.target.checked) }), _jsxs(Box, { children: [_jsx(Text, { sx: { fontSize: 1 }, children: "Skills" }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', display: 'block' }, children: "Enable reusable skill compositions" })] })] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Checkbox, { checked: enableCodemode, disabled: selectedAgentId !== 'new-agent', onChange: e => onEnableCodemodeChange?.(e.target.checked) }), _jsxs(Box, { children: [_jsx(Text, { sx: { fontSize: 1 }, children: "Codemode" }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', display: 'block' }, children: "Execute code to compose tools" })] })] })] }), enableSkills && enableCodemode && (_jsx(Flash, { variant: "default", sx: { mt: 3 }, children: _jsx(Text, { sx: { fontSize: 0 }, children: "Skills provide curated capabilities; Codemode composes tools with Python for multi-step execution." }) })), enableCodemode && (_jsxs(Box, { sx: { mt: 3, display: 'flex', flexDirection: 'column', gap: 2 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Checkbox, { checked: allowDirectToolCalls, disabled: selectedAgentId !== 'new-agent', onChange: e => onAllowDirectToolCallsChange?.(e.target.checked) }), _jsxs(Box, { children: [_jsx(Text, { sx: { fontSize: 1 }, children: "Allow direct tool calls" }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', display: 'block' }, children: "Expose call_tool for simple, single-tool operations" })] })] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Checkbox, { checked: enableToolReranker, disabled: selectedAgentId !== 'new-agent', onChange: e => onEnableToolRerankerChange?.(e.target.checked) }), _jsxs(Box, { children: [_jsx(Text, { sx: { fontSize: 1 }, children: "Enable tool reranker" }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', display: 'block' }, children: "Reorder search results using the configured reranker" })] })] })] }))] }), _jsxs(FormControl, { sx: { marginBottom: 3 }, children: [_jsx(FormControl.Label, { children: transport === 'acp' ? 'WebSocket URL' : 'Base URL' }), _jsx(TextInput, { value: transport === 'acp' ? wsUrl : baseUrl, onChange: e => transport === 'acp'
123
137
  ? onWsUrlChange(e.target.value)
124
138
  : onBaseUrlChange(e.target.value), disabled: selectedAgentId !== 'new-agent', placeholder: transport === 'acp'
125
139
  ? 'ws://localhost:8000/api/v1/acp/ws'
@@ -139,7 +153,20 @@ export const AgentConfiguration = ({ agentLibrary, transport, extensions, wsUrl,
139
153
  marginBottom: 2,
140
154
  }, children: [_jsx(ToolsIcon, { size: 16 }), _jsx(Text, { sx: { fontSize: 1, fontWeight: 'bold' }, children: "MCP Servers" }), configQuery.isLoading && _jsx(Spinner, { size: "small" })] }), configQuery.isError && (_jsx(Flash, { variant: "warning", sx: { marginBottom: 2 }, children: _jsx(Text, { sx: { fontSize: 0 }, children: "Unable to fetch MCP servers. Check that the server is running." }) })), mcpServers.length === 0 &&
141
155
  !configQuery.isLoading &&
142
- !configQuery.isError && (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "No MCP servers configured." })), mcpServers.length > 0 && (_jsx(ActionList, { children: mcpServers.map((server, index) => (_jsxs(React.Fragment, { children: [index > 0 && _jsx(ActionList.Divider, {}), _jsxs(ActionList.Item, { disabled: true, children: [_jsx(ActionList.LeadingVisual, { children: server.isAvailable ? (_jsx(CheckIcon, { size: 16 })) : (_jsx(XIcon, { size: 16 })) }), _jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 1 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Text, { sx: { fontWeight: 'semibold' }, children: server.name }), _jsx(Label, { variant: server.isAvailable ? 'success' : 'secondary', size: "small", children: server.isAvailable ? 'Available' : 'Not Available' })] }), server.tools.length > 0 && (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["Tools: ", server.tools.map(t => t.name).join(', ')] }))] })] })] }, server.id))) }))] }), createError && (_jsx(Flash, { variant: "danger", sx: { marginBottom: 3 }, children: createError })), _jsx(Button, { variant: "primary", onClick: onConnect, disabled: isCreatingAgent ||
156
+ !configQuery.isError && (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "No MCP servers configured." })), enableCodemode && (_jsx(Flash, { variant: "warning", sx: { marginBottom: 2 }, children: _jsx(Text, { sx: { fontSize: 0 }, children: "MCP servers are disabled when Codemode is enabled. Codemode provides its own tool discovery and execution." }) })), mcpServers.length > 0 && (_jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: mcpServers.map(server => (_jsxs(Box, { sx: {
157
+ display: 'flex',
158
+ alignItems: 'flex-start',
159
+ gap: 2,
160
+ padding: 2,
161
+ borderRadius: 1,
162
+ backgroundColor: 'canvas.subtle',
163
+ opacity: mcpServersDisabled || !server.isAvailable ? 0.6 : 1,
164
+ }, children: [_jsx(Checkbox, { checked: !enableCodemode && selectedMcpServers.includes(server.id), disabled: mcpServersDisabled || !server.isAvailable, onChange: e => handleMcpServerChange(server.id, e.target.checked) }), _jsxs(Box, { sx: {
165
+ display: 'flex',
166
+ flexDirection: 'column',
167
+ gap: 1,
168
+ flex: 1,
169
+ }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Text, { sx: { fontWeight: 'semibold' }, children: server.name }), _jsx(Label, { variant: server.isAvailable ? 'success' : 'secondary', size: "small", children: server.isAvailable ? 'Available' : 'Not Available' })] }), server.tools.length > 0 && (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["Tools: ", server.tools.map(t => t.name).join(', ')] }))] })] }, server.id))) }))] }), createError && (_jsx(Flash, { variant: "danger", sx: { marginBottom: 3 }, children: createError })), _jsx(Button, { variant: "primary", onClick: onConnect, disabled: isCreatingAgent ||
143
170
  !agentName ||
144
171
  (transport === 'acp' ? !wsUrl : !baseUrl), sx: { width: '100%' }, children: isCreatingAgent ? (_jsxs(Box, { sx: {
145
172
  display: 'flex',
@@ -6,14 +6,12 @@ interface HeaderProps {
6
6
  agentStatus?: 'running' | 'paused';
7
7
  richEditor: boolean;
8
8
  durable: boolean;
9
- codemode: boolean;
10
9
  showContextTree: boolean;
11
10
  isNewAgent?: boolean;
12
11
  isConfigured?: boolean;
13
12
  onSessionChange: (sessionId: string) => void;
14
13
  onRichEditorChange: (value: boolean) => void;
15
14
  onDurableChange: (value: boolean) => void;
16
- onCodemodeChange: (value: boolean) => void;
17
15
  onToggleContextTree: () => void;
18
16
  onToggleStatus?: () => void;
19
17
  }
@@ -112,7 +112,7 @@ const OPTIMIZED_CONTEXT_DATA = {
112
112
  * Main header for the agent runtime interface with session tabs,
113
113
  * toggle switches, controls, and optional context treemap.
114
114
  */
115
- export const Header = ({ activeSession, agentName, agentDescription, agentStatus, richEditor, durable, codemode, showContextTree, isNewAgent = false, isConfigured = false, onSessionChange, onRichEditorChange, onDurableChange, onCodemodeChange, onToggleContextTree, onToggleStatus, }) => {
115
+ export const Header = ({ activeSession, agentName, agentDescription, agentStatus, richEditor, durable, showContextTree, isNewAgent = false, isConfigured = false, onSessionChange, onRichEditorChange, onDurableChange, onToggleContextTree, onToggleStatus, }) => {
116
116
  const [isOptimizing, setIsOptimizing] = useState(false);
117
117
  const [contextData, setContextData] = useState(MOCK_CONTEXT_DATA);
118
118
  const [totalTokens, setTotalTokens] = useState('1.52M');
@@ -121,7 +121,6 @@ export const Header = ({ activeSession, agentName, agentDescription, agentStatus
121
121
  const [openOverlay, setOpenOverlay] = useState(null);
122
122
  const richEditorRef = useRef(null);
123
123
  const durableRef = useRef(null);
124
- const codemodeRef = useRef(null);
125
124
  const handleOptimize = () => {
126
125
  setIsOptimizing(true);
127
126
  setTimeout(() => {
@@ -142,20 +141,7 @@ export const Header = ({ activeSession, agentName, agentDescription, agentStatus
142
141
  alignItems: 'center',
143
142
  gap: 3,
144
143
  marginLeft: 'auto',
145
- }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(ToggleSwitch, { size: "small", checked: codemode, onClick: () => onCodemodeChange(!codemode), "aria-labelledby": "codemode-label", disabled: isNewAgent || !isConfigured }), _jsx(Text, { id: "codemode-label", sx: { fontSize: 0 }, children: "Codemode" }), _jsx(IconButton, { ref: codemodeRef, icon: InfoIcon, size: "small", variant: "invisible", "aria-label": "Codemode info", onClick: () => setOpenOverlay(openOverlay === 'codemode' ? null : 'codemode') }), _jsx(AnchoredOverlay, { open: openOverlay === 'codemode', onOpen: () => setOpenOverlay('codemode'), onClose: () => setOpenOverlay(null), renderAnchor: () => _jsx("span", {}), anchorRef: codemodeRef, children: _jsxs(Box, { sx: {
146
- p: 3,
147
- maxWidth: '300px',
148
- bg: 'canvas.overlay',
149
- border: '1px solid',
150
- borderColor: 'border.default',
151
- borderRadius: 2,
152
- boxShadow: 'shadow.large',
153
- }, children: [_jsx(Text, { sx: {
154
- fontSize: 0,
155
- display: 'block',
156
- mb: 1,
157
- fontWeight: 'bold',
158
- }, children: "Codemode" }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Optimize the agent for code-focused tasks including code generation, debugging, and technical problem-solving." })] }) })] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(ToggleSwitch, { size: "small", checked: richEditor, onClick: () => onRichEditorChange(!richEditor), "aria-labelledby": "rich-editor-label", disabled: isNewAgent || !isConfigured }), _jsx(Text, { id: "rich-editor-label", sx: { fontSize: 0 }, children: "Rich Editor" }), _jsx(IconButton, { ref: richEditorRef, icon: InfoIcon, size: "small", variant: "invisible", "aria-label": "Rich Editor info", onClick: () => setOpenOverlay(openOverlay === 'richEditor' ? null : 'richEditor') }), _jsx(AnchoredOverlay, { open: openOverlay === 'richEditor', onOpen: () => setOpenOverlay('richEditor'), onClose: () => setOpenOverlay(null), renderAnchor: () => _jsx("span", {}), anchorRef: richEditorRef, children: _jsxs(Box, { sx: {
144
+ }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(ToggleSwitch, { size: "small", checked: richEditor, onClick: () => onRichEditorChange(!richEditor), "aria-labelledby": "rich-editor-label", disabled: isNewAgent || !isConfigured }), _jsx(Text, { id: "rich-editor-label", sx: { fontSize: 0 }, children: "Rich Editor" }), _jsx(IconButton, { ref: richEditorRef, icon: InfoIcon, size: "small", variant: "invisible", "aria-label": "Rich Editor info", onClick: () => setOpenOverlay(openOverlay === 'richEditor' ? null : 'richEditor') }), _jsx(AnchoredOverlay, { open: openOverlay === 'richEditor', onOpen: () => setOpenOverlay('richEditor'), onClose: () => setOpenOverlay(null), renderAnchor: () => _jsx("span", {}), anchorRef: richEditorRef, children: _jsxs(Box, { sx: {
159
145
  p: 3,
160
146
  maxWidth: '300px',
161
147
  bg: 'canvas.overlay',
package/lib/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from './components';
2
2
  export * from './state';
3
+ export * from './runtime';
package/lib/index.js CHANGED
@@ -4,3 +4,4 @@
4
4
  */
5
5
  export * from './components';
6
6
  export * from './state';
7
+ export * from './runtime';
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Runtime management module for agent-runtimes.
3
+ *
4
+ * Provides a Zustand store and hooks for launching and managing cloud runtimes
5
+ * with integrated AI agent support.
6
+ *
7
+ * @module runtime
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { useRuntimeStore, useRuntime, useAgent } from '@datalayer/agent-runtimes/lib/runtime';
12
+ *
13
+ * // Launch a new runtime
14
+ * const { launchRuntime, createAgent } = useRuntimeStore();
15
+ * const runtime = await launchRuntime({ environmentName: 'python-simple', creditsLimit: 100 });
16
+ * const agent = await createAgent({ model: 'anthropic:claude-sonnet-4-5' });
17
+ *
18
+ * // Use in ChatFloating
19
+ * <ChatFloating endpoint={agent.endpoint} />
20
+ *
21
+ * // Or connect to an existing runtime
22
+ * const { connectToRuntime } = useRuntimeStore();
23
+ * connectToRuntime({
24
+ * podName: 'my-pod',
25
+ * environmentName: 'python-simple',
26
+ * serviceManager: myServiceManager,
27
+ * });
28
+ * ```
29
+ */
30
+ export { useRuntimeStore, useRuntime, useAgent, useRuntimeStatus, useRuntimeError, useIsLaunching, getRuntimeState, subscribeToRuntime, } from './runtimeStore';
31
+ export { useAgentConnection } from './useAgentConnection';
32
+ export { useAgentRuntime } from './useAgentRuntime';
33
+ export type { IRuntimeLocation, IRuntimeType, IRuntimeCapabilities, IRuntimePod, IRuntimeOptions, IRuntimeDesc, } from './types';
34
+ export type { RuntimeConnection, AgentRuntimeStatus as RuntimeStatus, AgentConfig, AgentConnection, AgentRuntimeState, } from './types';
35
+ export { DEFAULT_AGENT_CONFIG } from './types';