@datalayer/agent-runtimes 0.0.10 → 0.0.11

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 (36) hide show
  1. package/lib/components/AgentConfiguration.d.ts +30 -0
  2. package/lib/components/AgentConfiguration.js +71 -16
  3. package/lib/components/chat/components/AgentDetails.js +159 -1
  4. package/lib/components/chat/components/ContextDistribution.js +2 -2
  5. package/lib/components/chat/components/ContextInspector.js +4 -2
  6. package/lib/components/chat/components/ContextPanel.js +1 -6
  7. package/lib/components/index.d.ts +2 -2
  8. package/lib/components/index.js +1 -1
  9. package/lib/config/agents/code-ai/agents.d.ts +25 -0
  10. package/lib/config/agents/code-ai/agents.js +70 -0
  11. package/lib/config/agents/code-ai/index.d.ts +1 -0
  12. package/lib/config/agents/code-ai/index.js +5 -0
  13. package/lib/config/{agents.d.ts → agents/codemode-paper/agents.d.ts} +1 -5
  14. package/lib/config/{agents.js → agents/codemode-paper/agents.js} +29 -165
  15. package/lib/config/agents/codemode-paper/index.d.ts +1 -0
  16. package/lib/config/agents/codemode-paper/index.js +5 -0
  17. package/lib/config/agents/datalayer-ai/agents.d.ts +29 -0
  18. package/lib/config/agents/datalayer-ai/agents.js +267 -0
  19. package/lib/config/agents/datalayer-ai/index.d.ts +1 -0
  20. package/lib/config/agents/datalayer-ai/index.js +5 -0
  21. package/lib/config/agents/index.d.ts +19 -0
  22. package/lib/config/agents/index.js +38 -0
  23. package/lib/config/envvars.d.ts +28 -0
  24. package/lib/config/envvars.js +115 -0
  25. package/lib/config/index.d.ts +1 -0
  26. package/lib/config/index.js +1 -0
  27. package/lib/config/mcpServers.js +26 -2
  28. package/lib/config/skills.d.ts +2 -0
  29. package/lib/config/skills.js +6 -0
  30. package/lib/examples/AgentSpaceFormExample.js +51 -9
  31. package/lib/types.d.ts +10 -2
  32. package/package.json +2 -2
  33. package/scripts/codegen/generate_agents.py +565 -154
  34. package/scripts/codegen/generate_envvars.py +302 -0
  35. package/scripts/codegen/generate_mcp_servers.py +28 -16
  36. package/scripts/codegen/generate_skills.py +19 -6
@@ -4,6 +4,36 @@ import type { Transport, Extension } from './chat';
4
4
  import type { McpServerSelection } from './McpServerManager';
5
5
  import type { OAuthProvider, Identity } from '../identity';
6
6
  import type { MCPServerTool as MCPServerToolType } from '../types';
7
+ /**
8
+ * Agent spec entry from the library endpoint.
9
+ */
10
+ export interface LibraryAgentSpec {
11
+ id: string;
12
+ name: string;
13
+ description: string;
14
+ tags: string[];
15
+ enabled: boolean;
16
+ emoji?: string | null;
17
+ icon?: string | null;
18
+ color?: string | null;
19
+ skills: string[];
20
+ systemPrompt?: string | null;
21
+ systemPromptCodemodeAddons?: string | null;
22
+ suggestions: string[];
23
+ welcomeMessage?: string | null;
24
+ mcpServers: {
25
+ name: string;
26
+ id: string;
27
+ }[];
28
+ }
29
+ /**
30
+ * Helper: is the selected agent id a library spec?
31
+ */
32
+ export declare const isSpecSelection: (id: string) => boolean;
33
+ /**
34
+ * Helper: extract the spec id from a spec selection value.
35
+ */
36
+ export declare const getSpecId: (id: string) => string;
7
37
  /**
8
38
  * MCP Server Tool type (re-exported from types.ts)
9
39
  */
@@ -10,6 +10,14 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
10
10
  import { Box } from '@datalayer/primer-addons';
11
11
  import { IdentityConnect, useIdentity } from '../identity';
12
12
  import { IdentityCard } from './chat';
13
+ /**
14
+ * Helper: is the selected agent id a library spec?
15
+ */
16
+ export const isSpecSelection = (id) => id.startsWith('spec:');
17
+ /**
18
+ * Helper: extract the spec id from a spec selection value.
19
+ */
20
+ export const getSpecId = (id) => id.replace(/^spec:/, '');
13
21
  /**
14
22
  * Token-based identity providers that can be connected via API key
15
23
  */
@@ -228,6 +236,32 @@ export const AgentConfiguration = ({ agentLibrary, transport, extensions, wsUrl,
228
236
  staleTime: 1000 * 60 * 1, // 1 minute (refresh more often for running status)
229
237
  retry: 1,
230
238
  });
239
+ // Fetch agent specs from library
240
+ const libraryQuery = useQuery({
241
+ queryKey: ['agent-library', baseUrl],
242
+ queryFn: async () => {
243
+ const response = await fetch(`${baseUrl}/api/v1/agents/library`);
244
+ if (!response.ok) {
245
+ throw new Error('Failed to fetch agent library');
246
+ }
247
+ return response.json();
248
+ },
249
+ enabled: !!baseUrl,
250
+ staleTime: 1000 * 60 * 5, // 5 minutes
251
+ retry: 1,
252
+ });
253
+ const librarySpecs = libraryQuery.data || [];
254
+ // The currently selected library spec (if any)
255
+ const selectedSpec = useMemo(() => {
256
+ if (!isSpecSelection(selectedAgentId))
257
+ return null;
258
+ const specId = getSpecId(selectedAgentId);
259
+ return librarySpecs.find(s => s.id === specId) || null;
260
+ }, [selectedAgentId, librarySpecs]);
261
+ // When a spec is selected, form behaves like new-agent but with pre-filled values
262
+ const isNewAgentMode = selectedAgentId === 'new-agent' || isSpecSelection(selectedAgentId);
263
+ // True when a library spec is selected (fields locked down except Name, URL, Library, Model, Transport, Extensions)
264
+ const isSpecMode = isSpecSelection(selectedAgentId);
231
265
  // Fetch skills from the backend (only when codemode is enabled)
232
266
  const skillsQuery = useQuery({
233
267
  queryKey: ['agent-skills', baseUrl],
@@ -348,11 +382,11 @@ export const AgentConfiguration = ({ agentLibrary, transport, extensions, wsUrl,
348
382
  onSelectedSkillsChange?.(selectedSkills.filter(id => id !== skillId));
349
383
  }
350
384
  };
351
- // MCP servers are disabled for existing agents (new-agent only)
352
- const mcpServersDisabled = selectedAgentId !== 'new-agent';
385
+ // MCP servers are disabled for existing agents and when a spec is selected
386
+ const mcpServersDisabled = !isNewAgentMode || isSpecMode;
353
387
  // Determine which extensions are enabled based on transport
354
388
  const isExtensionEnabled = (ext) => {
355
- if (selectedAgentId !== 'new-agent')
389
+ if (!isNewAgentMode)
356
390
  return false;
357
391
  if (transport === 'ag-ui')
358
392
  return true; // Both mcp-ui and a2ui enabled
@@ -380,55 +414,71 @@ export const AgentConfiguration = ({ agentLibrary, transport, extensions, wsUrl,
380
414
  fontWeight: 'bold',
381
415
  display: 'block',
382
416
  marginBottom: 3,
383
- }, 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'
384
- ? 'Configure a new custom agent'
385
- : 'Selected agent - form fields below are disabled' })] }), _jsxs(FormControl, { sx: { marginBottom: 3 }, children: [_jsx(FormControl.Label, { children: "Agent Name" }), _jsx(TextInput, { value: agentName, onChange: e => onAgentNameChange(e.target.value), disabled: selectedAgentId !== 'new-agent', placeholder: "demo-agent", sx: { width: '100%' } }), _jsx(FormControl.Caption, { children: "The name of the agent to connect to" })] }), _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'
417
+ }, 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..." }), librarySpecs
418
+ .filter(s => s.enabled)
419
+ .map(spec => (_jsxs(Select.Option, { value: `spec:${spec.id}`, children: [spec.emoji ? `${spec.emoji} ` : '', spec.name] }, `spec:${spec.id}`))), agents.map(agent => (_jsxs(Select.Option, { value: agent.id, children: [agent.status === 'running' && ' ', agent.name] }, agent.id)))] }), _jsx(FormControl.Caption, { children: isNewAgentMode
420
+ ? selectedSpec
421
+ ? `Creating from spec: ${selectedSpec.name} — capabilities are locked`
422
+ : 'Configure a new custom agent'
423
+ : 'Selected agent - form fields below are disabled' })] }), _jsxs(FormControl, { sx: { marginBottom: 3 }, children: [_jsx(FormControl.Label, { children: "Agent Name" }), _jsx(TextInput, { value: agentName, onChange: e => onAgentNameChange(e.target.value), disabled: !isNewAgentMode, placeholder: "demo-agent", sx: { width: '100%' } }), _jsx(FormControl.Caption, { children: "The name of the agent to connect to" })] }), _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'
386
424
  ? onWsUrlChange(e.target.value)
387
- : onBaseUrlChange(e.target.value), disabled: selectedAgentId !== 'new-agent', placeholder: transport === 'acp'
425
+ : onBaseUrlChange(e.target.value), disabled: !isNewAgentMode, placeholder: transport === 'acp'
388
426
  ? 'ws://localhost:8000/api/v1/acp/ws'
389
427
  : 'http://localhost:8000', sx: { width: '100%' } }), _jsx(FormControl.Caption, { children: transport === 'acp'
390
428
  ? 'The WebSocket endpoint of your agent-runtimes server'
391
- : 'The base URL of your agent-runtimes server' })] }), _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: {
429
+ : 'The base URL of your agent-runtimes server' })] }), _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: !isNewAgentMode, 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: !isNewAgentMode || 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: !isNewAgentMode, 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: {
392
430
  marginBottom: 3,
393
431
  padding: 3,
394
432
  border: '1px solid',
395
433
  borderColor: 'border.default',
396
434
  borderRadius: 2,
397
435
  backgroundColor: 'canvas.default',
436
+ opacity: isSpecMode ? 0.6 : 1,
398
437
  }, children: [_jsxs(Box, { sx: {
399
438
  display: 'flex',
400
439
  alignItems: 'center',
401
440
  gap: 2,
402
441
  marginBottom: 2,
403
- }, children: [_jsx(KeyIcon, { size: 16 }), _jsx(Text, { sx: { fontSize: 1, fontWeight: 'bold' }, children: "Connected Accounts" })] }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', display: 'block', mb: 3 }, children: "Connect your accounts to give the agent access to external services like GitHub repositories, Google services, or Kaggle datasets." }), _jsx(IdentityConnectWithStatus, { identityProviders: identityProviders, disabled: selectedAgentId !== 'new-agent', onConnect: onIdentityConnect, onDisconnect: onIdentityDisconnect })] }), _jsxs(Box, { sx: {
442
+ }, children: [_jsx(KeyIcon, { size: 16 }), _jsx(Text, { sx: { fontSize: 1, fontWeight: 'bold' }, children: "Connected Accounts" })] }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', display: 'block', mb: 3 }, children: "Connect your accounts to give the agent access to external services like GitHub repositories, Google services, or Kaggle datasets." }), _jsx(IdentityConnectWithStatus, { identityProviders: identityProviders, disabled: !isNewAgentMode || isSpecMode, onConnect: onIdentityConnect, onDisconnect: onIdentityDisconnect })] }), _jsxs(Box, { sx: {
404
443
  marginBottom: 3,
405
444
  padding: 3,
406
445
  border: '1px solid',
407
446
  borderColor: 'border.default',
408
447
  borderRadius: 2,
409
448
  backgroundColor: 'canvas.default',
410
- }, children: [_jsx(Text, { sx: { fontSize: 1, fontWeight: 'bold', display: 'block', mb: 2 }, children: "Agent Capabilities" }), _jsx(Box, { sx: { display: 'flex', gap: 4 }, children: _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" })] })] }) }), skillsEnabled && 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(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Checkbox, { checked: useJupyterSandbox, disabled: selectedAgentId !== 'new-agent', onChange: e => onUseJupyterSandboxChange?.(e.target.checked) }), _jsxs(Box, { children: [_jsx(Text, { sx: { fontSize: 1 }, children: "Use Jupyter Sandbox" }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', display: 'block' }, children: "Execute code in a Jupyter kernel instead of local-eval" })] })] })] }))] }), _jsxs(Box, { sx: {
449
+ opacity: isSpecMode ? 0.6 : 1,
450
+ }, children: [_jsxs(Text, { sx: { fontSize: 1, fontWeight: 'bold', display: 'block', mb: 2 }, children: ["Agent Capabilities", isSpecMode && (_jsx(Text, { as: "span", sx: {
451
+ fontSize: 0,
452
+ color: 'fg.muted',
453
+ fontWeight: 'normal',
454
+ ml: 2,
455
+ }, children: "\u2014 defined by spec" }))] }), _jsx(Box, { sx: { display: 'flex', gap: 4, opacity: isSpecMode ? 0.6 : 1 }, children: _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Checkbox, { checked: enableCodemode, disabled: !isNewAgentMode || isSpecMode, 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" })] })] }) }), skillsEnabled && 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: !isNewAgentMode || isSpecMode, 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: !isNewAgentMode || isSpecMode, 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(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Checkbox, { checked: useJupyterSandbox, disabled: !isNewAgentMode || isSpecMode, onChange: e => onUseJupyterSandboxChange?.(e.target.checked) }), _jsxs(Box, { children: [_jsx(Text, { sx: { fontSize: 1 }, children: "Use Jupyter Sandbox" }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', display: 'block' }, children: "Execute code in a Jupyter kernel instead of local-eval" })] })] })] }))] }), _jsxs(Box, { sx: {
411
456
  marginBottom: 3,
412
457
  padding: 3,
413
458
  border: '1px solid',
414
459
  borderColor: 'border.default',
415
460
  borderRadius: 2,
416
461
  backgroundColor: 'canvas.default',
417
- opacity: enableCodemode ? 1 : 0.5,
462
+ opacity: isSpecMode ? 0.6 : enableCodemode ? 1 : 0.5,
418
463
  }, children: [_jsxs(Box, { sx: {
419
464
  display: 'flex',
420
465
  alignItems: 'center',
421
466
  gap: 2,
422
467
  marginBottom: 2,
423
- }, children: [_jsx(Text, { sx: { fontSize: 1, fontWeight: 'bold' }, children: "Skills" }), skillsQuery.isLoading && _jsx(Spinner, { size: "small" }), enableCodemode && !skillsQuery.isLoading && (_jsx(Button, { variant: "invisible", size: "small", onClick: () => skillsQuery.refetch(), sx: { padding: 1 }, "aria-label": "Refresh skills", children: _jsx(SyncIcon, { size: 14 }) }))] }), !enableCodemode ? (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Enable Codemode to use skills." })) : skillsQuery.isError ? (_jsx(Flash, { variant: "warning", sx: { marginBottom: 2 }, children: _jsx(Text, { sx: { fontSize: 0 }, children: "Unable to fetch skills. Check that the server is running." }) })) : displaySkills.length === 0 && !skillsQuery.isLoading ? (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "No skills available." })) : (_jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: displaySkills.map(skill => (_jsxs(Box, { sx: {
468
+ }, children: [_jsxs(Text, { sx: { fontSize: 1, fontWeight: 'bold' }, children: ["Skills", isSpecMode && (_jsx(Text, { as: "span", sx: {
469
+ fontSize: 0,
470
+ color: 'fg.muted',
471
+ fontWeight: 'normal',
472
+ ml: 2,
473
+ }, children: "\u2014 defined by spec" }))] }), skillsQuery.isLoading && _jsx(Spinner, { size: "small" }), enableCodemode && !skillsQuery.isLoading && (_jsx(Button, { variant: "invisible", size: "small", onClick: () => skillsQuery.refetch(), sx: { padding: 1 }, "aria-label": "Refresh skills", children: _jsx(SyncIcon, { size: 14 }) }))] }), !enableCodemode ? (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Enable Codemode to use skills." })) : skillsQuery.isError ? (_jsx(Flash, { variant: "warning", sx: { marginBottom: 2 }, children: _jsx(Text, { sx: { fontSize: 0 }, children: "Unable to fetch skills. Check that the server is running." }) })) : displaySkills.length === 0 && !skillsQuery.isLoading ? (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "No skills available." })) : (_jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: displaySkills.map(skill => (_jsxs(Box, { sx: {
424
474
  display: 'flex',
425
475
  alignItems: 'center',
426
476
  gap: 2,
427
477
  padding: 2,
428
478
  borderRadius: 1,
429
479
  backgroundColor: 'canvas.subtle',
430
- opacity: selectedAgentId !== 'new-agent' ? 0.6 : 1,
431
- }, children: [_jsx(Checkbox, { checked: selectedSkills.includes(skill.id), disabled: selectedAgentId !== 'new-agent', onChange: e => handleSkillChange(skill.id, e.target.checked) }), _jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 1 }, children: [_jsx(Text, { sx: { fontWeight: 'semibold' }, children: skill.name }), skill.description && (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: skill.description }))] })] }, skill.id))) }))] }), _jsxs(Box, { sx: {
480
+ opacity: !isNewAgentMode || isSpecMode ? 0.6 : 1,
481
+ }, children: [_jsx(Checkbox, { checked: selectedSkills.includes(skill.id), disabled: !isNewAgentMode || isSpecMode, onChange: e => handleSkillChange(skill.id, e.target.checked) }), _jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 1 }, children: [_jsx(Text, { sx: { fontWeight: 'semibold' }, children: skill.name }), skill.description && (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: skill.description }))] })] }, skill.id))) }))] }), _jsxs(Box, { sx: {
432
482
  marginBottom: 3,
433
483
  padding: 3,
434
484
  border: '1px solid',
@@ -440,7 +490,12 @@ export const AgentConfiguration = ({ agentLibrary, transport, extensions, wsUrl,
440
490
  alignItems: 'center',
441
491
  gap: 2,
442
492
  marginBottom: 2,
443
- }, children: [_jsx(ToolsIcon, { size: 16 }), _jsx(Text, { sx: { fontSize: 1, fontWeight: 'bold' }, children: "MCP Config Servers" }), mcpServersQuery.isLoading && _jsx(Spinner, { size: "small" }), !mcpServersQuery.isLoading && (_jsx(Button, { variant: "invisible", size: "small", onClick: () => mcpServersQuery.refetch(), sx: { padding: 1 }, "aria-label": "Refresh MCP config servers", children: _jsx(SyncIcon, { size: 14 }) }))] }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', marginBottom: 2 }, children: "Servers from your mcp.json configuration file. Started automatically." }), mcpServersQuery.isError && (_jsx(Flash, { variant: "warning", sx: { marginBottom: 2 }, children: _jsx(Text, { sx: { fontSize: 0 }, children: "Unable to fetch MCP config servers. Check that the server is running." }) })), configServers.length === 0 &&
493
+ }, children: [_jsx(ToolsIcon, { size: 16 }), _jsxs(Text, { sx: { fontSize: 1, fontWeight: 'bold' }, children: ["MCP Config Servers", isSpecMode && (_jsx(Text, { as: "span", sx: {
494
+ fontSize: 0,
495
+ color: 'fg.muted',
496
+ fontWeight: 'normal',
497
+ ml: 2,
498
+ }, children: "\u2014 defined by spec" }))] }), mcpServersQuery.isLoading && _jsx(Spinner, { size: "small" }), !mcpServersQuery.isLoading && (_jsx(Button, { variant: "invisible", size: "small", onClick: () => mcpServersQuery.refetch(), sx: { padding: 1 }, "aria-label": "Refresh MCP config servers", children: _jsx(SyncIcon, { size: 14 }) }))] }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', marginBottom: 2 }, children: "Servers from your mcp.json configuration file. Started automatically." }), mcpServersQuery.isError && (_jsx(Flash, { variant: "warning", sx: { marginBottom: 2 }, children: _jsx(Text, { sx: { fontSize: 0 }, children: "Unable to fetch MCP config servers. Check that the server is running." }) })), configServers.length === 0 &&
444
499
  !mcpServersQuery.isLoading &&
445
500
  !mcpServersQuery.isError && (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "No MCP config servers found. Add servers to ~/.datalayer/mcp.json" })), enableCodemode && (_jsx(Flash, { variant: "default", sx: { marginBottom: 2 }, children: _jsx(Text, { sx: { fontSize: 0 }, children: "When Codemode is enabled, selected MCP servers are used to build the Codemode tool registry (tools are exposed via Codemode meta tools like search and execute_code)." }) })), enableCodemode && (_jsxs(Box, { sx: {
446
501
  marginBottom: 2,
@@ -525,6 +580,6 @@ export const AgentConfiguration = ({ agentLibrary, transport, extensions, wsUrl,
525
580
  alignItems: 'center',
526
581
  justifyContent: 'center',
527
582
  gap: 2,
528
- }, children: [_jsx(Spinner, { size: "small" }), _jsx("span", { children: "Creating Agent..." })] })) : selectedAgentId === 'new-agent' ? ('Create the Agent') : agents.find(a => a.id === selectedAgentId)?.status === 'running' ? ('Connect to the Agent') : ('Start and Connect to the Agent') })] }));
583
+ }, children: [_jsx(Spinner, { size: "small" }), _jsx("span", { children: "Creating Agent..." })] })) : isNewAgentMode ? (selectedSpec ? (`Create from "${selectedSpec.name}"`) : ('Create the Agent')) : agents.find(a => a.id === selectedAgentId)?.status === 'running' ? ('Connect to the Agent') : ('Start and Connect to the Agent') })] }));
529
584
  };
530
585
  export { AGENT_LIBRARIES, TRANSPORTS, EXTENSIONS };
@@ -5,7 +5,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
5
5
  * AgentDetails component - Shows detailed information about the agent
6
6
  * including name, protocol, URL, message count, and context details.
7
7
  */
8
- import { ArrowLeftIcon, CheckCircleIcon, XCircleIcon, CodeIcon, ZapIcon, DownloadIcon, } from '@primer/octicons-react';
8
+ import { ArrowLeftIcon, CheckCircleIcon, XCircleIcon, CodeIcon, ZapIcon, DownloadIcon, NoteIcon, ServerIcon, } from '@primer/octicons-react';
9
9
  import { Button, Heading, IconButton, Text, Label, Spinner, ToggleSwitch, } from '@primer/react';
10
10
  import { Box } from '@datalayer/primer-addons';
11
11
  import { AiAgentIcon } from '@datalayer/icons-react';
@@ -220,6 +220,19 @@ export function AgentDetails({ name = 'AI Agent', protocol, url, messageCount, a
220
220
  queryClient.invalidateQueries({ queryKey: ['codemode-status'] });
221
221
  },
222
222
  });
223
+ // Fetch agent spec (original creation request with separated system prompts)
224
+ const { data: agentSpec, isLoading: specLoading } = useQuery({
225
+ queryKey: ['agent-spec', agentId, apiBase],
226
+ queryFn: async () => {
227
+ const base = getApiBase(apiBase);
228
+ const response = await fetch(`${base}/api/v1/configure/agents/${encodeURIComponent(agentId)}/spec`);
229
+ if (!response.ok) {
230
+ throw new Error('Failed to fetch agent spec');
231
+ }
232
+ return response.json();
233
+ },
234
+ enabled: !!agentId,
235
+ });
223
236
  return (_jsxs(Box, { sx: {
224
237
  display: 'flex',
225
238
  flexDirection: 'column',
@@ -266,6 +279,151 @@ export function AgentDetails({ name = 'AI Agent', protocol, url, messageCount, a
266
279
  fontWeight: 'semibold',
267
280
  mb: 2,
268
281
  color: 'fg.muted',
282
+ }, children: "Agent Spec" }), _jsx(Box, { sx: {
283
+ p: 3,
284
+ bg: 'canvas.subtle',
285
+ borderRadius: 2,
286
+ border: '1px solid',
287
+ borderColor: 'border.default',
288
+ }, children: specLoading ? (_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Spinner, { size: "small" }), _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "Loading agent spec..." })] })) : agentSpec ? (_jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 3 }, children: [_jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 1 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', width: 100 }, children: "Model:" }), _jsx(Text, { sx: {
289
+ fontSize: 0,
290
+ fontFamily: 'mono',
291
+ color: 'fg.default',
292
+ }, children: agentSpec.model })] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', width: 100 }, children: "Library:" }), _jsx(Label, { variant: "secondary", size: "small", children: agentSpec.agent_library })] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', width: 100 }, children: "Codemode:" }), _jsx(Label, { variant: agentSpec.enable_codemode ? 'accent' : 'secondary', size: "small", children: agentSpec.enable_codemode ? 'Enabled' : 'Disabled' })] }), agentSpec.enable_skills && agentSpec.skills.length > 0 && (_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', width: 100 }, children: "Skills:" }), _jsx(Box, { sx: { display: 'flex', gap: 1, flexWrap: 'wrap' }, children: agentSpec.skills.map(skill => (_jsx(Label, { variant: "secondary", size: "small", children: skill }, skill))) })] }))] }), _jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 1 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(NoteIcon, { size: 16 }), _jsx(Text, { sx: {
293
+ fontSize: 0,
294
+ fontWeight: 'semibold',
295
+ color: 'fg.muted',
296
+ }, children: "System Prompt" })] }), _jsx(Box, { sx: {
297
+ p: 2,
298
+ bg: 'canvas.default',
299
+ borderRadius: 2,
300
+ border: '1px solid',
301
+ borderColor: 'border.default',
302
+ maxHeight: 200,
303
+ overflow: 'auto',
304
+ }, children: _jsx(Text, { sx: {
305
+ fontSize: 0,
306
+ fontFamily: 'mono',
307
+ whiteSpace: 'pre-wrap',
308
+ wordBreak: 'break-word',
309
+ color: 'fg.default',
310
+ }, children: agentSpec.system_prompt }) })] }), agentSpec.system_prompt_codemode_addons && (_jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 1 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(CodeIcon, { size: 16 }), _jsx(Text, { sx: {
311
+ fontSize: 0,
312
+ fontWeight: 'semibold',
313
+ color: 'fg.muted',
314
+ }, children: "Codemode Addon Prompt" }), agentSpec.enable_codemode ? (_jsx(Label, { variant: "accent", size: "small", children: "Active" })) : (_jsx(Label, { variant: "secondary", size: "small", children: "Inactive" }))] }), _jsx(Box, { sx: {
315
+ p: 2,
316
+ bg: 'canvas.default',
317
+ borderRadius: 2,
318
+ border: '1px solid',
319
+ borderColor: 'border.default',
320
+ maxHeight: 200,
321
+ overflow: 'auto',
322
+ }, children: _jsx(Text, { sx: {
323
+ fontSize: 0,
324
+ fontFamily: 'mono',
325
+ whiteSpace: 'pre-wrap',
326
+ wordBreak: 'break-word',
327
+ color: 'fg.default',
328
+ }, children: agentSpec.system_prompt_codemode_addons }) })] }))] })) : (_jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "No agent spec available" })) })] }), agentSpec?.sandbox && (_jsxs(Box, { children: [_jsx(Heading, { as: "h4", sx: {
329
+ fontSize: 1,
330
+ fontWeight: 'semibold',
331
+ mb: 2,
332
+ color: 'fg.muted',
333
+ }, children: "Code Sandbox" }), _jsx(Box, { sx: {
334
+ p: 3,
335
+ bg: 'canvas.subtle',
336
+ borderRadius: 2,
337
+ border: '1px solid',
338
+ borderColor: 'border.default',
339
+ }, children: _jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(ServerIcon, { size: 16 }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', width: 100 }, children: "Variant:" }), _jsx(Label, { variant: agentSpec.sandbox.variant === 'local-jupyter'
340
+ ? 'accent'
341
+ : 'secondary', size: "small", children: agentSpec.sandbox.variant })] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', width: 100, pl: 4 }, children: "Running:" }), agentSpec.sandbox.sandbox_running ? (_jsxs(Box, { sx: {
342
+ display: 'flex',
343
+ alignItems: 'center',
344
+ gap: 1,
345
+ }, children: [_jsx(CheckCircleIcon, { size: 12, fill: "success.fg" }), _jsx(Text, { sx: { fontSize: 0, color: 'success.fg' }, children: "Yes" })] })) : (_jsxs(Box, { sx: {
346
+ display: 'flex',
347
+ alignItems: 'center',
348
+ gap: 1,
349
+ }, children: [_jsx(XCircleIcon, { size: 12, fill: "fg.muted" }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "No" })] }))] }), agentSpec.sandbox.variant === 'local-jupyter' && (_jsxs(_Fragment, { children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Text, { sx: {
350
+ fontSize: 0,
351
+ color: 'fg.muted',
352
+ width: 100,
353
+ pl: 4,
354
+ }, children: "Jupyter URL:" }), _jsx(Text, { sx: {
355
+ fontSize: 0,
356
+ fontFamily: 'mono',
357
+ color: 'fg.default',
358
+ overflow: 'hidden',
359
+ textOverflow: 'ellipsis',
360
+ whiteSpace: 'nowrap',
361
+ }, children: agentSpec.jupyter_sandbox ||
362
+ agentSpec.sandbox.jupyter_url ||
363
+ 'Not configured' })] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Text, { sx: {
364
+ fontSize: 0,
365
+ color: 'fg.muted',
366
+ width: 100,
367
+ pl: 4,
368
+ }, children: "Connection:" }), agentSpec.sandbox.jupyter_connected ? (_jsxs(Box, { sx: {
369
+ display: 'flex',
370
+ alignItems: 'center',
371
+ gap: 1,
372
+ }, children: [_jsx(CheckCircleIcon, { size: 12, fill: "success.fg" }), _jsx(Text, { sx: { fontSize: 0, color: 'success.fg' }, children: "Connected" })] })) : (_jsxs(Box, { sx: {
373
+ display: 'flex',
374
+ flexDirection: 'column',
375
+ gap: 1,
376
+ }, children: [_jsxs(Box, { sx: {
377
+ display: 'flex',
378
+ alignItems: 'center',
379
+ gap: 1,
380
+ }, children: [_jsx(XCircleIcon, { size: 12, fill: "danger.fg" }), _jsx(Text, { sx: { fontSize: 0, color: 'danger.fg' }, children: "Not Connected" })] }), agentSpec.sandbox.jupyter_error && (_jsx(Text, { sx: {
381
+ fontSize: 0,
382
+ color: 'danger.fg',
383
+ fontFamily: 'mono',
384
+ whiteSpace: 'pre-wrap',
385
+ wordBreak: 'break-word',
386
+ }, children: agentSpec.sandbox.jupyter_error }))] }))] })] })), agentSpec.sandbox.generated_path && (_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Text, { sx: {
387
+ fontSize: 0,
388
+ color: 'fg.muted',
389
+ width: 100,
390
+ pl: 4,
391
+ }, children: "Generated:" }), _jsx(Text, { sx: {
392
+ fontSize: 0,
393
+ fontFamily: 'mono',
394
+ color: 'fg.default',
395
+ overflow: 'hidden',
396
+ textOverflow: 'ellipsis',
397
+ whiteSpace: 'nowrap',
398
+ }, title: agentSpec.sandbox.generated_path, children: agentSpec.sandbox.generated_path })] })), agentSpec.sandbox.skills_path && (_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Text, { sx: {
399
+ fontSize: 0,
400
+ color: 'fg.muted',
401
+ width: 100,
402
+ pl: 4,
403
+ }, children: "Skills:" }), _jsx(Text, { sx: {
404
+ fontSize: 0,
405
+ fontFamily: 'mono',
406
+ color: 'fg.default',
407
+ overflow: 'hidden',
408
+ textOverflow: 'ellipsis',
409
+ whiteSpace: 'nowrap',
410
+ }, title: agentSpec.sandbox.skills_path, children: agentSpec.sandbox.skills_path })] })), agentSpec.sandbox.python_path && (_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Text, { sx: {
411
+ fontSize: 0,
412
+ color: 'fg.muted',
413
+ width: 100,
414
+ pl: 4,
415
+ }, children: "Python:" }), _jsx(Text, { sx: {
416
+ fontSize: 0,
417
+ fontFamily: 'mono',
418
+ color: 'fg.default',
419
+ overflow: 'hidden',
420
+ textOverflow: 'ellipsis',
421
+ whiteSpace: 'nowrap',
422
+ }, title: agentSpec.sandbox.python_path, children: agentSpec.sandbox.python_path })] }))] }) })] })), _jsxs(Box, { children: [_jsx(Heading, { as: "h4", sx: {
423
+ fontSize: 1,
424
+ fontWeight: 'semibold',
425
+ mb: 2,
426
+ color: 'fg.muted',
269
427
  }, children: "Config MCP Servers" }), _jsx(Box, { sx: {
270
428
  p: 3,
271
429
  bg: 'canvas.subtle',
@@ -67,7 +67,7 @@ export function ContextDistribution({ agentId, height = '250px', }) {
67
67
  borderColor: 'border.default',
68
68
  }, children: _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "Failed to load context distribution" }) }));
69
69
  }
70
- const { distribution, totalTokens, contextWindow } = snapshotData;
70
+ const { distribution, totalTokens } = snapshotData;
71
71
  const hasData = distribution.children && distribution.children.length > 0;
72
72
  // ECharts option for treemap
73
73
  const chartOption = {
@@ -121,7 +121,7 @@ export function ContextDistribution({ agentId, height = '250px', }) {
121
121
  justifyContent: 'space-between',
122
122
  alignItems: 'center',
123
123
  mb: 2,
124
- }, children: [_jsxs(Text, { sx: { fontSize: 1, fontWeight: 'bold' }, children: ["Current Context (", formatTokens(totalTokens), " /", ' ', formatTokens(contextWindow), " tokens)"] }), _jsx(Button, { size: "small", variant: "invisible", onClick: () => setShowDetails(!showDetails), leadingVisual: ListUnorderedIcon, children: showDetails ? 'Hide Details' : 'Show Details' })] }), hasData ? (_jsx(ReactECharts, { option: chartOption, style: { height }, opts: { renderer: 'svg' } })) : (_jsx(Box, { sx: {
124
+ }, children: [_jsxs(Text, { sx: { fontSize: 1, fontWeight: 'bold' }, children: ["Current Context (", formatTokens(totalTokens), " total)"] }), _jsx(Button, { size: "small", variant: "invisible", onClick: () => setShowDetails(!showDetails), leadingVisual: ListUnorderedIcon, children: showDetails ? 'Hide Details' : 'Show Details' })] }), hasData ? (_jsx(ReactECharts, { option: chartOption, style: { height }, opts: { renderer: 'svg' } })) : (_jsx(Box, { sx: {
125
125
  p: 4,
126
126
  bg: 'canvas.subtle',
127
127
  borderRadius: 2,
@@ -194,9 +194,11 @@ export function ContextInspector({ agentId, apiBase }) {
194
194
  }, children: _jsx(Text, { sx: { color: 'attention.fg' }, children: contextData.error }) }));
195
195
  }
196
196
  const { tokenSummary, modelConfiguration } = contextData;
197
- return (_jsxs(Box, { children: [_jsxs(Box, { sx: { mb: 3 }, children: [_jsxs(Box, { sx: { display: 'flex', justifyContent: 'space-between', mb: 1 }, children: [_jsxs(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: ["Context Usage: ", formatTokens(tokenSummary.total), " /", ' ', formatTokens(tokenSummary.contextWindow)] }), _jsxs(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: [tokenSummary.usagePercent.toFixed(1), "%"] })] }), _jsx(ProgressBar, { progress: tokenSummary.usagePercent, sx: { height: 8 }, bg: tokenSummary.usagePercent > 80
197
+ return (_jsxs(Box, { children: [_jsxs(Box, { sx: { mb: 3 }, children: [_jsxs(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: ["Total usage: ", formatTokens(tokenSummary.total), " /", ' ', formatTokens(tokenSummary.contextWindow), " (", Math.round(tokenSummary.usagePercent), "%)"] }), _jsx(ProgressBar, { progress: Math.min(tokenSummary.usagePercent, 100), sx: { mt: 2, height: 8 }, bg: tokenSummary.usagePercent > 90
198
198
  ? 'danger.emphasis'
199
- : 'accent.emphasis' }), _jsxs(Box, { sx: {
199
+ : tokenSummary.usagePercent > 70
200
+ ? 'attention.emphasis'
201
+ : 'success.emphasis' }), _jsxs(Box, { sx: {
200
202
  display: 'flex',
201
203
  gap: 3,
202
204
  mt: 2,
@@ -270,7 +270,6 @@ export function ContextPanel({ agentId, apiBase, messageCount = 0, defaultView =
270
270
  }, children: _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "Failed to load context details. Make sure the agent is running." }) })] }));
271
271
  }
272
272
  const { totalTokens, contextWindow, sessionUsage, turnUsage, distribution } = snapshotData;
273
- const contextUsagePercent = (totalTokens / contextWindow) * 100;
274
273
  const hasDistributionData = distribution?.children && distribution.children.length > 0;
275
274
  const hasHistoryData = snapshotData.perRequestUsage && snapshotData.perRequestUsage.length > 0;
276
275
  return (_jsxs(Box, { children: [_jsxs(Box, { sx: {
@@ -288,11 +287,7 @@ export function ContextPanel({ agentId, apiBase, messageCount = 0, defaultView =
288
287
  borderRadius: 2,
289
288
  border: '1px solid',
290
289
  borderColor: 'border.default',
291
- }, children: [_jsxs(Box, { sx: { mb: 3 }, children: [_jsxs(Box, { sx: {
292
- display: 'flex',
293
- justifyContent: 'space-between',
294
- mb: 1,
295
- }, children: [_jsxs(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: [formatTokens(totalTokens), " / ", formatTokens(contextWindow), " tokens"] }), _jsxs(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: [contextUsagePercent.toFixed(1), "%"] })] }), _jsx(ProgressBar, { progress: Math.min(contextUsagePercent, 100), sx: { height: 8 }, bg: contextUsagePercent > 80 ? 'danger.emphasis' : 'accent.emphasis' })] }), (sessionUsage || turnUsage) && (_jsxs(Box, { sx: {
290
+ }, children: [_jsx(Box, { sx: { mb: 3 }, children: _jsxs(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: ["Total usage: ", formatTokens(totalTokens)] }) }), (sessionUsage || turnUsage) && (_jsxs(Box, { sx: {
296
291
  display: 'flex',
297
292
  gap: 3,
298
293
  mb: 3,
@@ -17,8 +17,8 @@ export { TimeTravel } from './TimeTravel';
17
17
  export type { TimeTravelProps } from './TimeTravel';
18
18
  export { LexicalEditor } from './LexicalEditor';
19
19
  export type { LexicalEditorProps } from './LexicalEditor';
20
- export { AgentConfiguration, AGENT_LIBRARIES, TRANSPORTS, EXTENSIONS, } from './AgentConfiguration';
21
- export type { AgentLibrary, Transport, Extension, AgentConfigurationProps, SkillOption, MCPServerConfig, MCPServerTool, } from './AgentConfiguration';
20
+ export { AgentConfiguration, AGENT_LIBRARIES, TRANSPORTS, EXTENSIONS, isSpecSelection, getSpecId, } from './AgentConfiguration';
21
+ export type { AgentLibrary, Transport, Extension, AgentConfigurationProps, SkillOption, MCPServerConfig, MCPServerTool, LibraryAgentSpec, } from './AgentConfiguration';
22
22
  export { McpServerManager } from './McpServerManager';
23
23
  export type { McpServerSelection, McpServerManagerProps, } from './McpServerManager';
24
24
  export type { Agent as ExampleAgent, AgentStatus as ExampleAgentStatus, AgentsState, Transport as ExampleTransport, } from '../examples/stores/examplesStore';
@@ -13,5 +13,5 @@ export { HeaderControls } from './HeaderControls';
13
13
  export { FooterMetrics } from './FooterMetrics';
14
14
  export { TimeTravel } from './TimeTravel';
15
15
  export { LexicalEditor } from './LexicalEditor';
16
- export { AgentConfiguration, AGENT_LIBRARIES, TRANSPORTS, EXTENSIONS, } from './AgentConfiguration';
16
+ export { AgentConfiguration, AGENT_LIBRARIES, TRANSPORTS, EXTENSIONS, isSpecSelection, getSpecId, } from './AgentConfiguration';
17
17
  export { McpServerManager } from './McpServerManager';
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Agent Library.
3
+ *
4
+ * Predefined agent specifications that can be instantiated as AgentSpaces.
5
+ * THIS FILE IS AUTO-GENERATED. DO NOT EDIT MANUALLY.
6
+ * Generated from YAML specifications in specs/agents/
7
+ */
8
+ import type { AgentSpec } from '../../../types';
9
+ export declare const SIMPLE_AGENT_SPEC: AgentSpec;
10
+ export declare const AGENT_SPECS: Record<string, AgentSpec>;
11
+ /**
12
+ * Get an agent specification by ID.
13
+ */
14
+ export declare function getAgentSpecs(agentId: string): AgentSpec | undefined;
15
+ /**
16
+ * List all available agent specifications.
17
+ */
18
+ export declare function listAgentSpecs(): AgentSpec[];
19
+ /**
20
+ * Collect all required environment variables for an agent spec.
21
+ *
22
+ * Iterates over the spec's MCP servers and skills and returns the
23
+ * deduplicated union of their `requiredEnvVars` arrays.
24
+ */
25
+ export declare function getAgentSpecRequiredEnvVars(spec: AgentSpec): string[];
@@ -0,0 +1,70 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ // ============================================================================
6
+ // Agent Specs
7
+ // ============================================================================
8
+ // Code Ai Agents
9
+ // ============================================================================
10
+ export const SIMPLE_AGENT_SPEC = {
11
+ id: 'code-ai/simple',
12
+ name: 'A Simple Agent',
13
+ description: `A simple conversational agent. No tools, no MCP servers, no skills — just a helpful AI assistant you can chat with.`,
14
+ tags: ['simple', 'chat', 'assistant'],
15
+ enabled: true,
16
+ mcpServers: [],
17
+ skills: [],
18
+ environmentName: 'ai-agents-env',
19
+ icon: 'share-2',
20
+ emoji: '🤖',
21
+ color: '#6366F1',
22
+ suggestions: [
23
+ 'Tell me a joke',
24
+ 'Explain quantum computing in simple terms',
25
+ 'Help me brainstorm ideas for a weekend project',
26
+ 'Summarize the key points of a topic I describe',
27
+ ],
28
+ systemPrompt: `You are a helpful, friendly AI assistant. You do not have access to any external tools, MCP servers, or skills. Answer questions using your training knowledge, be concise, and let the user know if a question is outside your knowledge.
29
+ `,
30
+ systemPromptCodemodeAddons: undefined,
31
+ };
32
+ // ============================================================================
33
+ // Agent Specs Registry
34
+ // ============================================================================
35
+ export const AGENT_SPECS = {
36
+ // Code Ai
37
+ 'code-ai/simple': SIMPLE_AGENT_SPEC,
38
+ };
39
+ /**
40
+ * Get an agent specification by ID.
41
+ */
42
+ export function getAgentSpecs(agentId) {
43
+ return AGENT_SPECS[agentId];
44
+ }
45
+ /**
46
+ * List all available agent specifications.
47
+ */
48
+ export function listAgentSpecs() {
49
+ return Object.values(AGENT_SPECS);
50
+ }
51
+ /**
52
+ * Collect all required environment variables for an agent spec.
53
+ *
54
+ * Iterates over the spec's MCP servers and skills and returns the
55
+ * deduplicated union of their `requiredEnvVars` arrays.
56
+ */
57
+ export function getAgentSpecRequiredEnvVars(spec) {
58
+ const vars = new Set();
59
+ for (const server of spec.mcpServers) {
60
+ for (const v of server.requiredEnvVars ?? []) {
61
+ vars.add(v);
62
+ }
63
+ }
64
+ for (const skill of spec.skills) {
65
+ for (const v of skill.requiredEnvVars ?? []) {
66
+ vars.add(v);
67
+ }
68
+ }
69
+ return Array.from(vars);
70
+ }
@@ -0,0 +1 @@
1
+ export * from './agents';
@@ -0,0 +1,5 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ export * from './agents';