@datalayer/agent-runtimes 1.0.5 → 1.0.6

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 (111) hide show
  1. package/README.md +157 -10
  2. package/lib/AgentNode.d.ts +3 -0
  3. package/lib/AgentNode.js +676 -0
  4. package/lib/agent-node/themeStore.d.ts +3 -0
  5. package/lib/agent-node/themeStore.js +156 -0
  6. package/lib/agent-node-main.d.ts +1 -0
  7. package/lib/agent-node-main.js +14 -0
  8. package/lib/chat/Chat.js +16 -10
  9. package/lib/chat/ChatFloating.js +1 -1
  10. package/lib/chat/ChatSidebar.js +81 -49
  11. package/lib/chat/base/ChatBase.js +388 -74
  12. package/lib/chat/display/FloatingBrandButton.js +8 -1
  13. package/lib/chat/header/ChatHeader.d.ts +3 -1
  14. package/lib/chat/header/ChatHeader.js +15 -12
  15. package/lib/chat/header/ChatHeaderBase.d.ts +29 -9
  16. package/lib/chat/header/ChatHeaderBase.js +26 -3
  17. package/lib/chat/indicators/SandboxStatusIndicator.js +82 -47
  18. package/lib/chat/messages/ChatMessageList.js +46 -1
  19. package/lib/chat/messages/ChatMessages.js +6 -2
  20. package/lib/chat/prompt/InputFooter.d.ts +3 -1
  21. package/lib/chat/prompt/InputFooter.js +8 -5
  22. package/lib/chat/prompt/InputPrompt.d.ts +3 -1
  23. package/lib/chat/prompt/InputPrompt.js +2 -2
  24. package/lib/chat/prompt/InputPromptFooter.d.ts +3 -1
  25. package/lib/chat/prompt/InputPromptFooter.js +3 -3
  26. package/lib/client/AgentsMixin.js +14 -0
  27. package/lib/config/AgentConfiguration.d.ts +22 -0
  28. package/lib/config/AgentConfiguration.js +319 -64
  29. package/lib/examples/AgUiSharedStateExample.js +2 -1
  30. package/lib/examples/AgentCheckpointsExample.js +3 -3
  31. package/lib/examples/AgentCodemodeExample.d.ts +3 -3
  32. package/lib/examples/AgentCodemodeExample.js +24 -12
  33. package/lib/examples/AgentEvalsExample.js +330 -40
  34. package/lib/examples/AgentGuardrailsExample.js +16 -5
  35. package/lib/examples/AgentHooksExample.js +27 -9
  36. package/lib/examples/AgentInferenceProviderExample.d.ts +3 -0
  37. package/lib/examples/AgentInferenceProviderExample.js +329 -0
  38. package/lib/examples/AgentMCPExample.js +6 -5
  39. package/lib/examples/AgentMemoryExample.d.ts +1 -2
  40. package/lib/examples/AgentMemoryExample.js +71 -22
  41. package/lib/examples/AgentMonitoringExample.js +5 -5
  42. package/lib/examples/AgentNotificationsExample.d.ts +1 -2
  43. package/lib/examples/AgentNotificationsExample.js +71 -22
  44. package/lib/examples/AgentOtelExample.js +31 -40
  45. package/lib/examples/AgentOutputsExample.d.ts +1 -1
  46. package/lib/examples/AgentOutputsExample.js +67 -16
  47. package/lib/examples/AgentParametersExample.js +10 -8
  48. package/lib/examples/AgentSandboxExample.d.ts +1 -1
  49. package/lib/examples/AgentSandboxExample.js +7 -6
  50. package/lib/examples/AgentSkillsExample.js +6 -6
  51. package/lib/examples/AgentSubagentsExample.d.ts +1 -1
  52. package/lib/examples/AgentSubagentsExample.js +6 -6
  53. package/lib/examples/AgentToolApprovalsExample.js +27 -11
  54. package/lib/examples/AgentTriggersExample.js +5 -5
  55. package/lib/examples/{AgentSpecsExample.d.ts → AgentspecsExample.d.ts} +2 -2
  56. package/lib/examples/AgentspecsExample.js +1096 -0
  57. package/lib/examples/ChatCustomExample.js +6 -5
  58. package/lib/examples/ChatExample.js +6 -5
  59. package/lib/examples/Lexical2Example.js +1 -1
  60. package/lib/examples/LexicalAgentExample.js +1 -1
  61. package/lib/examples/NotebookAgentExample.js +3 -3
  62. package/lib/examples/components/ExampleWrapper.d.ts +6 -7
  63. package/lib/examples/components/ExampleWrapper.js +27 -10
  64. package/lib/examples/example-selector.js +2 -1
  65. package/lib/examples/index.d.ts +2 -1
  66. package/lib/examples/index.js +2 -1
  67. package/lib/examples/lexical/initial-content.json +6 -6
  68. package/lib/examples/main.js +56 -16
  69. package/lib/examples/utils/agentId.d.ts +1 -1
  70. package/lib/examples/utils/agentId.js +1 -1
  71. package/lib/examples/utils/useExampleAgentRuntimesUrl.d.ts +5 -0
  72. package/lib/examples/utils/useExampleAgentRuntimesUrl.js +19 -0
  73. package/lib/hooks/useAIAgentsWebSocket.js +35 -0
  74. package/lib/hooks/useAgentRuntimes.d.ts +32 -3
  75. package/lib/hooks/useAgentRuntimes.js +114 -19
  76. package/lib/index.d.ts +1 -1
  77. package/lib/specs/agents/agents.d.ts +20 -13
  78. package/lib/specs/agents/agents.js +1267 -581
  79. package/lib/specs/benchmarks.d.ts +20 -0
  80. package/lib/specs/benchmarks.js +205 -0
  81. package/lib/specs/envvars.d.ts +0 -1
  82. package/lib/specs/envvars.js +0 -11
  83. package/lib/specs/evals.d.ts +10 -9
  84. package/lib/specs/evals.js +128 -88
  85. package/lib/specs/index.d.ts +0 -1
  86. package/lib/specs/index.js +0 -1
  87. package/lib/specs/models.d.ts +0 -2
  88. package/lib/specs/models.js +0 -15
  89. package/lib/specs/skills.d.ts +0 -1
  90. package/lib/specs/skills.js +0 -18
  91. package/lib/stores/agentRuntimeStore.d.ts +5 -1
  92. package/lib/stores/agentRuntimeStore.js +22 -8
  93. package/lib/stores/conversationStore.js +2 -2
  94. package/lib/types/agents-lifecycle.d.ts +18 -0
  95. package/lib/types/agents.d.ts +6 -0
  96. package/lib/types/agentspecs.d.ts +4 -0
  97. package/lib/types/benchmarks.d.ts +43 -0
  98. package/lib/types/benchmarks.js +5 -0
  99. package/lib/types/chat.d.ts +16 -0
  100. package/lib/types/evals.d.ts +26 -17
  101. package/lib/types/index.d.ts +1 -0
  102. package/lib/types/index.js +1 -0
  103. package/package.json +9 -5
  104. package/scripts/codegen/__pycache__/generate_agents.cpython-313.pyc +0 -0
  105. package/scripts/codegen/__pycache__/generate_benchmarks.cpython-313.pyc +0 -0
  106. package/scripts/codegen/__pycache__/generate_evals.cpython-313.pyc +0 -0
  107. package/scripts/codegen/generate_agents.py +89 -43
  108. package/scripts/codegen/generate_benchmarks.py +441 -0
  109. package/scripts/codegen/generate_evals.py +94 -16
  110. package/scripts/codegen/generate_events.py +0 -1
  111. package/lib/examples/AgentSpecsExample.js +0 -694
@@ -5,16 +5,18 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
5
  */
6
6
  import { useEffect, useState } from 'react';
7
7
  import { Text, Spinner } from '@primer/react';
8
+ import { SyncIcon } from '@primer/octicons-react';
8
9
  import { Box, setupPrimerPortals } from '@datalayer/primer-addons';
9
10
  import { ThemedProvider } from './utils/themedProvider';
10
11
  import { uniqueAgentId } from './utils/agentId';
12
+ import { useExampleAgentRuntimesUrl } from './utils/useExampleAgentRuntimesUrl';
11
13
  import { ErrorView } from './components';
12
14
  import { Chat } from '../chat';
13
15
  setupPrimerPortals();
14
- const BASE_URL = 'http://localhost:8765';
15
- const AGENT_SPEC_ID = 'demo-hooks';
16
- const AGENT_NAME = 'hooks-demo';
16
+ const AGENT_SPEC_ID = 'example-hooks';
17
+ const AGENT_NAME = 'hooks-example-agent';
17
18
  const AgentHooksExample = () => {
19
+ const baseUrl = useExampleAgentRuntimesUrl();
18
20
  const [agentId, setAgentId] = useState(null);
19
21
  const [error, setError] = useState(null);
20
22
  const [isCreating, setIsCreating] = useState(true);
@@ -23,7 +25,7 @@ const AgentHooksExample = () => {
23
25
  const name = uniqueAgentId(AGENT_NAME);
24
26
  const createAgent = async () => {
25
27
  try {
26
- const response = await fetch(`${BASE_URL}/api/v1/agents`, {
28
+ const response = await fetch(`${baseUrl}/api/v1/agents`, {
27
29
  method: 'POST',
28
30
  headers: { 'Content-Type': 'application/json' },
29
31
  body: JSON.stringify({
@@ -55,19 +57,19 @@ const AgentHooksExample = () => {
55
57
  return () => {
56
58
  cancelled = true;
57
59
  };
58
- }, []);
60
+ }, [baseUrl]);
59
61
  useEffect(() => {
60
62
  return () => {
61
63
  if (!agentId) {
62
64
  return;
63
65
  }
64
- void fetch(`${BASE_URL}/api/v1/agents/${encodeURIComponent(agentId)}`, {
66
+ void fetch(`${baseUrl}/api/v1/agents/${encodeURIComponent(agentId)}`, {
65
67
  method: 'DELETE',
66
68
  }).catch(() => {
67
69
  // Ignore teardown failures in example mode.
68
70
  });
69
71
  };
70
- }, [agentId]);
72
+ }, [agentId, baseUrl]);
71
73
  if (isCreating) {
72
74
  return (_jsx(ThemedProvider, { children: _jsxs(Box, { sx: {
73
75
  display: 'flex',
@@ -82,14 +84,14 @@ const AgentHooksExample = () => {
82
84
  if (error || !agentId) {
83
85
  return (_jsx(ThemedProvider, { children: _jsx(ErrorView, { error: "Failed to start hooks agent", detail: error || 'No agent ID returned' }) }));
84
86
  }
85
- return (_jsx(Chat, { protocol: "vercel-ai", baseUrl: BASE_URL, agentId: agentId, title: "Hooks Agent", placeholder: "Ask about lifecycle hooks...", description: "Pre-hook installed 'rich', wrote a marker file, and set hook_name / hook_ran_at / hook_env variables in the sandbox", showHeader: true, showModelSelector: true, showToolsMenu: true, showSkillsMenu: true, showTokenUsage: true, showInformation: true, autoFocus: true, height: "100vh", runtimeId: agentId, historyEndpoint: `${BASE_URL}/api/v1/history`, suggestions: [
87
+ return (_jsx(Chat, { protocol: "vercel-ai", baseUrl: baseUrl, agentId: agentId, title: "Hooks Agent", brandIcon: _jsx(SyncIcon, { size: 16 }), placeholder: "Ask about lifecycle hooks...", description: "Demonstrates lifecycle hooks and pydantic-style tool hooks: before_tool_execute, after_tool_execute, on_tool_execute_error, deferred_tool_calls", showHeader: true, showModelSelector: true, showToolsMenu: true, showSkillsMenu: true, showTokenUsage: true, showInformation: true, autoFocus: true, height: "100vh", runtimeId: agentId, historyEndpoint: `${baseUrl}/api/v1/history`, suggestions: [
86
88
  {
87
89
  title: 'Read the pre-hook marker file',
88
90
  message: 'Use execute_code to read /tmp/agent_runtimes_pre_hook_demo.txt and show its contents.',
89
91
  },
90
92
  {
91
93
  title: 'Verify hook variables',
92
- message: 'Use execute_code to run this verification:\n```python\nassert isinstance(hook_name, str) and hook_name == "demo-hooks:pre", f"❌ hook_name wrong: {hook_name!r}"\nassert isinstance(hook_ran_at, str) and len(hook_ran_at) > 0, f"❌ hook_ran_at wrong: {hook_ran_at!r}"\nassert isinstance(hook_env, dict) and len(hook_env) > 0, f"❌ hook_env wrong: {hook_env!r}"\nprint("✅ hook_name =", hook_name)\nprint("✅ hook_ran_at =", hook_ran_at)\nprint("✅ hook_env =", hook_env)\n```\nThrow an exception with a ❌ message if any variable is missing or has the wrong type, print ✅ lines if all pass.',
94
+ message: 'Use execute_code to run this verification:\n```python\nassert isinstance(hook_name, str) and hook_name == "example-hooks:pre", f"❌ hook_name wrong: {hook_name!r}"\nassert isinstance(hook_ran_at, str) and len(hook_ran_at) > 0, f"❌ hook_ran_at wrong: {hook_ran_at!r}"\nassert isinstance(hook_env, dict) and len(hook_env) > 0, f"❌ hook_env wrong: {hook_env!r}"\nprint("✅ hook_name =", hook_name)\nprint("✅ hook_ran_at =", hook_ran_at)\nprint("✅ hook_env =", hook_env)\n```\nThrow an exception with a ❌ message if any variable is missing or has the wrong type, print ✅ lines if all pass.',
93
95
  },
94
96
  {
95
97
  title: "Verify 'rich' was installed",
@@ -99,6 +101,22 @@ const AgentHooksExample = () => {
99
101
  title: 'Explain the hook lifecycle',
100
102
  message: 'What pre-hooks and post-hooks are configured for this agent, and when does each run?',
101
103
  },
104
+ {
105
+ title: 'Trigger function + python tool hooks',
106
+ message: "Call runtime_sensitive_echo with text 'hello' and reason 'audit', then explain which before_tool_execute hooks ran (function and python).",
107
+ },
108
+ {
109
+ title: 'Trigger deny in python hook',
110
+ message: "Call runtime_sensitive_echo with text 'danger' and reason 'delete notebook', then explain why local Python policy denied it.",
111
+ },
112
+ {
113
+ title: 'Read tool approval audit log',
114
+ message: 'Use execute_code to show the latest entries from /tmp/agent_runtimes_tool_approvals_audit.jsonl and summarize decision and execution status.',
115
+ },
116
+ {
117
+ title: 'Explain deferred hook handling',
118
+ message: 'Explain how deferred_tool_calls works with approval-required tools and when inline resolution is used in this run.',
119
+ },
102
120
  ], submitOnSuggestionClick: true }));
103
121
  };
104
122
  export default AgentHooksExample;
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ declare const AgentInferenceProviderExample: React.FC;
3
+ export default AgentInferenceProviderExample;
@@ -0,0 +1,329 @@
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
+ /// <reference types="vite/client" />
7
+ import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
8
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
9
+ import { Button, Box, Heading, SegmentedControl, Spinner, Text, } from '@primer/react';
10
+ import { ThemedProvider } from './utils/themedProvider';
11
+ import { uniqueAgentId } from './utils/agentId';
12
+ import { useSimpleAuthStore } from '@datalayer/core/lib/views/otel';
13
+ import { Chat } from '../chat';
14
+ import { useAIAgentsWebSocket } from '../hooks';
15
+ const AGENT_SPEC_ID = 'example-inference';
16
+ const AGENT_NAME = 'inference-provider-example-agent';
17
+ const DEFAULT_LOCAL_BASE_URL = import.meta.env.VITE_BASE_URL || 'http://localhost:8765';
18
+ const queryClient = new QueryClient();
19
+ const nowIso = () => new Date().toISOString();
20
+ const toErrorMessage = (error) => error instanceof Error ? error.message : String(error);
21
+ const stringifyPayload = (value) => {
22
+ if (typeof value === 'string') {
23
+ return value;
24
+ }
25
+ try {
26
+ return JSON.stringify(value, null, 2);
27
+ }
28
+ catch {
29
+ return String(value);
30
+ }
31
+ };
32
+ const ProviderBadge = ({ provider, }) => (_jsx(Box, { sx: {
33
+ px: 2,
34
+ py: '2px',
35
+ borderRadius: 999,
36
+ border: '1px solid',
37
+ borderColor: 'border.default',
38
+ fontSize: 0,
39
+ color: 'fg.muted',
40
+ bg: 'canvas.inset',
41
+ textTransform: 'uppercase',
42
+ letterSpacing: '0.03em',
43
+ }, children: provider }));
44
+ const AgentInferenceProviderExampleInner = () => {
45
+ const { token } = useSimpleAuthStore();
46
+ const [provider, setProvider] = useState('local');
47
+ const [agentId, setAgentId] = useState(null);
48
+ const [isLaunching, setIsLaunching] = useState(true);
49
+ const [error, setError] = useState(null);
50
+ const [providerEvents, setProviderEvents] = useState([]);
51
+ const [expandedEventIds, setExpandedEventIds] = useState(() => new Set());
52
+ const currentAgentRef = useRef(null);
53
+ const requestEpochRef = useRef(0);
54
+ const baseUrl = DEFAULT_LOCAL_BASE_URL;
55
+ const inferenceUrl = useMemo(() => {
56
+ if (provider === 'local') {
57
+ return 'local inference';
58
+ }
59
+ const env = import.meta.env ?? {};
60
+ return (env.VITE_DATALAYER_AI_INFERENCE_URL ||
61
+ env.VITE_DATALAYER_RUN_URL ||
62
+ 'https://prod1.datalayer.run');
63
+ }, [provider]);
64
+ const authFetch = useCallback((url, init = {}) => {
65
+ return fetch(url, {
66
+ ...init,
67
+ headers: {
68
+ 'Content-Type': 'application/json',
69
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
70
+ ...(init.headers ?? {}),
71
+ },
72
+ });
73
+ }, [token]);
74
+ const appendProviderEvent = useCallback((type, summary, payload) => {
75
+ setProviderEvents(prev => [
76
+ {
77
+ id: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
78
+ type,
79
+ summary,
80
+ payload: stringifyPayload(payload),
81
+ createdAt: nowIso(),
82
+ },
83
+ ...prev,
84
+ ]);
85
+ }, []);
86
+ const deleteAgent = useCallback(async (id) => {
87
+ await authFetch(`${baseUrl}/api/v1/agents/${encodeURIComponent(id)}`, {
88
+ method: 'DELETE',
89
+ }).catch(() => {
90
+ // Best-effort cleanup in example mode.
91
+ });
92
+ }, [authFetch, baseUrl]);
93
+ const launchAgentForProvider = useCallback(async (nextProvider) => {
94
+ requestEpochRef.current += 1;
95
+ const requestEpoch = requestEpochRef.current;
96
+ setIsLaunching(true);
97
+ setError(null);
98
+ const previousAgentId = currentAgentRef.current;
99
+ if (previousAgentId) {
100
+ await deleteAgent(previousAgentId);
101
+ }
102
+ try {
103
+ const configureResponse = await authFetch(`${baseUrl}/api/v1/configure/inference/provider`, {
104
+ method: 'PUT',
105
+ body: JSON.stringify({ provider: nextProvider }),
106
+ });
107
+ appendProviderEvent('configure.provider', `Configured runtime inference provider to ${nextProvider}`, {
108
+ status: configureResponse.status,
109
+ provider: nextProvider,
110
+ });
111
+ const name = uniqueAgentId(`${AGENT_NAME}-${nextProvider}`);
112
+ const createResponse = await authFetch(`${baseUrl}/api/v1/agents`, {
113
+ method: 'POST',
114
+ body: JSON.stringify({
115
+ name,
116
+ transport: 'vercel-ai',
117
+ agent_spec_id: AGENT_SPEC_ID,
118
+ inferenceProvider: nextProvider,
119
+ }),
120
+ });
121
+ if (!createResponse.ok) {
122
+ const detail = await createResponse.text();
123
+ throw new Error(detail || `Failed to create agent (${createResponse.status})`);
124
+ }
125
+ const payload = (await createResponse.json());
126
+ const nextAgentId = payload.id || name;
127
+ if (requestEpoch !== requestEpochRef.current) {
128
+ return;
129
+ }
130
+ currentAgentRef.current = nextAgentId;
131
+ setAgentId(nextAgentId);
132
+ appendProviderEvent('agent.created', 'Launched local agent runtime', {
133
+ agentId: nextAgentId,
134
+ agentSpecId: AGENT_SPEC_ID,
135
+ inferenceProvider: nextProvider,
136
+ baseUrl,
137
+ });
138
+ }
139
+ catch (launchError) {
140
+ if (requestEpoch !== requestEpochRef.current) {
141
+ return;
142
+ }
143
+ setAgentId(null);
144
+ currentAgentRef.current = null;
145
+ setError(toErrorMessage(launchError));
146
+ appendProviderEvent('agent.error', 'Failed to launch agent', {
147
+ provider: nextProvider,
148
+ error: toErrorMessage(launchError),
149
+ });
150
+ }
151
+ finally {
152
+ if (requestEpoch === requestEpochRef.current) {
153
+ setIsLaunching(false);
154
+ }
155
+ }
156
+ }, [appendProviderEvent, authFetch, baseUrl, deleteAgent]);
157
+ useEffect(() => {
158
+ void launchAgentForProvider(provider);
159
+ }, [launchAgentForProvider, provider]);
160
+ useEffect(() => {
161
+ return () => {
162
+ const existing = currentAgentRef.current;
163
+ if (existing) {
164
+ void deleteAgent(existing);
165
+ }
166
+ };
167
+ }, [deleteAgent]);
168
+ const onProviderChange = useCallback((next) => {
169
+ setProvider(next);
170
+ }, []);
171
+ useAIAgentsWebSocket({
172
+ enabled: Boolean(agentId) && !isLaunching,
173
+ baseUrl,
174
+ path: '/api/v1/tool-approvals/ws',
175
+ queryParams: agentId ? { agent_id: agentId } : undefined,
176
+ onMessage: message => {
177
+ const eventType = typeof message.type === 'string'
178
+ ? message.type
179
+ : typeof message.event === 'string'
180
+ ? message.event
181
+ : 'stream.message';
182
+ appendProviderEvent('provider.stream', `Received ${eventType}`, {
183
+ provider,
184
+ agentId,
185
+ message: message.raw ?? message,
186
+ });
187
+ },
188
+ reconnectDelayMs: attempt => Math.min(1000 * 2 ** Math.max(0, attempt - 1), 10000),
189
+ });
190
+ const orderedProviderEvents = useMemo(() => [...providerEvents].sort((a, b) => b.createdAt.localeCompare(a.createdAt)), [providerEvents]);
191
+ const toggleEventExpanded = useCallback((eventId) => {
192
+ setExpandedEventIds(prev => {
193
+ const next = new Set(prev);
194
+ if (next.has(eventId)) {
195
+ next.delete(eventId);
196
+ }
197
+ else {
198
+ next.add(eventId);
199
+ }
200
+ return next;
201
+ });
202
+ }, []);
203
+ return (_jsx(ThemedProvider, { children: _jsxs(Box, { sx: {
204
+ height: '100%',
205
+ minHeight: 0,
206
+ width: '100%',
207
+ display: 'grid',
208
+ gridTemplateColumns: ['1fr', '1fr', 'minmax(680px, 1fr) 420px'],
209
+ gridTemplateRows: 'minmax(0, 1fr)',
210
+ overflow: 'hidden',
211
+ bg: 'canvas.default',
212
+ }, children: [_jsxs(Box, { sx: {
213
+ height: '100%',
214
+ borderRight: ['none', 'none', '1px solid'],
215
+ borderColor: 'border.default',
216
+ p: 3,
217
+ display: 'flex',
218
+ flexDirection: 'column',
219
+ gap: 3,
220
+ minHeight: 0,
221
+ overflow: 'hidden',
222
+ }, children: [_jsxs(Box, { sx: {
223
+ display: 'flex',
224
+ justifyContent: 'space-between',
225
+ alignItems: 'center',
226
+ flexWrap: 'wrap',
227
+ gap: 2,
228
+ }, children: [_jsxs(Box, { children: [_jsx(Heading, { as: "h2", sx: { fontSize: 4, mb: 1 }, children: "Agent Inference Provider Example" }), _jsxs(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: ["Launches a local agent runtime from ", AGENT_SPEC_ID, " and lets you switch between local and datalayer inference."] })] }), _jsx(ProviderBadge, { provider: provider })] }), _jsxs(SegmentedControl, { "aria-label": "Inference provider", fullWidth: true, children: [_jsx(SegmentedControl.Button, { selected: provider === 'local', onClick: () => onProviderChange('local'), children: "local" }), _jsx(SegmentedControl.Button, { selected: provider === 'datalayer', onClick: () => onProviderChange('datalayer'), children: "datalayer" })] }), _jsxs(Box, { sx: {
229
+ display: 'flex',
230
+ gap: 2,
231
+ alignItems: 'center',
232
+ flexWrap: 'wrap',
233
+ }, children: [isLaunching ? _jsx(Spinner, { size: "small" }) : null, _jsxs(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: ["Runtime URL: ", baseUrl] }), _jsxs(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: ["Inference URL: ", inferenceUrl] }), _jsxs(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: ["Agent ID: ", agentId || 'launching...'] })] }), error ? (_jsx(Box, { sx: {
234
+ p: 2,
235
+ borderRadius: 2,
236
+ border: '1px solid',
237
+ borderColor: 'danger.emphasis',
238
+ bg: 'danger.subtle',
239
+ color: 'danger.fg',
240
+ fontSize: 1,
241
+ }, children: error })) : null, _jsx(Box, { sx: {
242
+ flexGrow: 1,
243
+ flexShrink: 1,
244
+ flexBasis: 0,
245
+ minHeight: 0,
246
+ border: '1px solid',
247
+ borderColor: 'border.default',
248
+ borderRadius: 2,
249
+ overflow: 'hidden',
250
+ bg: 'canvas.default',
251
+ }, children: agentId && !isLaunching ? (_jsx(Chat, { protocol: "vercel-ai", baseUrl: baseUrl, agentId: agentId, authToken: token ?? undefined, title: "Agent Inference Provider Example", subtitle: `Spec: ${AGENT_SPEC_ID}`, placeholder: "Ask the inference provider something...", showHeader: true, showNewChatButton: true, showClearButton: false, showModelSelector: true, showToolsMenu: true, showSkillsMenu: true, showTokenUsage: true, autoFocus: true, height: "100%", runtimeId: agentId, historyEndpoint: `${baseUrl}/api/v1/history`, onMessageSent: content => {
252
+ appendProviderEvent('provider.request', `Sent message via ${provider}`, {
253
+ provider,
254
+ agentId,
255
+ content,
256
+ });
257
+ }, onMessageReceived: message => {
258
+ appendProviderEvent('provider.message', 'Received stream message', message);
259
+ }, suggestions: [
260
+ {
261
+ title: 'Compare providers',
262
+ message: 'Give me a short 3-point comparison between local and datalayer inference providers.',
263
+ },
264
+ ], submitOnSuggestionClick: true })) : (_jsxs(Box, { sx: {
265
+ height: '100%',
266
+ display: 'flex',
267
+ alignItems: 'center',
268
+ justifyContent: 'center',
269
+ gap: 2,
270
+ color: 'fg.muted',
271
+ }, children: [_jsx(Spinner, { size: "small" }), _jsx(Text, { sx: { fontSize: 1 }, children: "Launching provider runtime\u2026" })] })) })] }), _jsxs(Box, { sx: {
272
+ p: 3,
273
+ display: ['none', 'none', 'flex'],
274
+ flexDirection: 'column',
275
+ gap: 2,
276
+ minHeight: 0,
277
+ overflow: 'hidden',
278
+ bg: 'canvas.inset',
279
+ }, children: [_jsx(Heading, { as: "h3", sx: { fontSize: 2 }, children: "Provider Event Stream" }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "Low-level request/response events exchanged with the inference provider (newest first)." }), _jsx(Box, { sx: {
280
+ flex: 1,
281
+ minHeight: 0,
282
+ overflowY: 'auto',
283
+ display: 'flex',
284
+ flexDirection: 'column',
285
+ gap: 2,
286
+ }, children: providerEvents.length === 0 ? (_jsx(Box, { sx: {
287
+ border: '1px dashed',
288
+ borderColor: 'border.default',
289
+ borderRadius: 2,
290
+ p: 3,
291
+ }, children: _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "No provider events yet." }) })) : (orderedProviderEvents.map(event => (_jsxs(Box, { sx: {
292
+ border: '1px solid',
293
+ borderColor: 'border.default',
294
+ borderRadius: 2,
295
+ p: 2,
296
+ bg: 'canvas.default',
297
+ }, children: [_jsx(Text, { sx: {
298
+ display: 'block',
299
+ fontSize: 0,
300
+ color: 'fg.muted',
301
+ textTransform: 'uppercase',
302
+ letterSpacing: '0.04em',
303
+ }, children: event.type }), _jsx(Text, { sx: {
304
+ display: 'block',
305
+ fontWeight: 600,
306
+ fontSize: 1,
307
+ mt: 1,
308
+ }, children: event.summary }), _jsx(Text, { sx: {
309
+ display: 'block',
310
+ color: 'fg.muted',
311
+ fontSize: 0,
312
+ mt: 1,
313
+ }, children: new Date(event.createdAt).toLocaleTimeString() }), _jsx(Box, { sx: { mt: 2 }, children: _jsx(Button, { size: "small", variant: "invisible", onClick: () => toggleEventExpanded(event.id), children: expandedEventIds.has(event.id)
314
+ ? 'Hide details'
315
+ : 'Show details' }) }), expandedEventIds.has(event.id) ? (_jsx(Text, { as: "pre", sx: {
316
+ mt: 2,
317
+ mb: 0,
318
+ p: 2,
319
+ borderRadius: 2,
320
+ bg: 'canvas.subtle',
321
+ fontSize: 0,
322
+ whiteSpace: 'pre-wrap',
323
+ fontFamily: 'mono',
324
+ }, children: event.payload })) : null] }, event.id)))) })] })] }) }));
325
+ };
326
+ const AgentInferenceProviderExample = () => {
327
+ return (_jsx(QueryClientProvider, { client: queryClient, children: _jsx(AgentInferenceProviderExampleInner, {}) }));
328
+ };
329
+ export default AgentInferenceProviderExample;
@@ -19,8 +19,9 @@ import { parseAgentStreamMessage } from '../types/stream';
19
19
  import { MCP_STATUS_COLORS, MCP_STATUS_LABELS } from '../types/mcp';
20
20
  import { MCP_SERVER_LIBRARY } from '../specs/mcpServers';
21
21
  const queryClient = new QueryClient();
22
- const AGENT_NAME = 'mcp-demo-agent';
23
- const AGENT_SPEC_ID = 'demo-mcp';
22
+ const AGENT_NAME = 'mcp-example-agent';
23
+ // Must match agentspecs/agentspecs/agents/example-mcp.yaml `id`.
24
+ const AGENT_SPEC_ID = 'example-mcp';
24
25
  const DEFAULT_LOCAL_BASE_URL = import.meta.env.VITE_BASE_URL || 'http://localhost:8765';
25
26
  /* ── Aggregate MCP status helpers ─────────────────────── */
26
27
  function deriveAggregate(servers) {
@@ -149,7 +150,7 @@ const AgentMCPInner = ({ onLogout }) => {
149
150
  method: 'POST',
150
151
  body: JSON.stringify({
151
152
  name: agentName,
152
- description: 'MCP demo agent – web crawling and research via Tavily',
153
+ description: 'MCP example agent – web crawling and research via Tavily',
153
154
  agent_library: 'pydantic-ai',
154
155
  transport: 'vercel-ai',
155
156
  agent_spec_id: AGENT_SPEC_ID,
@@ -372,7 +373,7 @@ const AgentMCPInner = ({ onLogout }) => {
372
373
  justifyContent: 'center',
373
374
  height: '100vh',
374
375
  gap: 3,
375
- }, children: [_jsx(Spinner, { size: "large" }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Launching MCP demo agent..." })] }));
376
+ }, children: [_jsx(Spinner, { size: "large" }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Launching MCP example agent..." })] }));
376
377
  }
377
378
  if (runtimeStatus === 'error' || hookError) {
378
379
  return _jsx(ErrorView, { error: hookError, onLogout: onLogout });
@@ -386,7 +387,7 @@ const AgentMCPInner = ({ onLogout }) => {
386
387
  py: 1,
387
388
  borderBottom: '1px solid',
388
389
  borderColor: 'border.default',
389
- }, children: _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: "Agent already running - reconnected." }) })), _jsxs(Box, { sx: { flex: 1, minHeight: 0, display: 'flex' }, children: [_jsx(Box, { sx: { flex: 1, minWidth: 0 }, children: _jsx(Chat, { protocol: "vercel-ai", baseUrl: agentBaseUrl, agentId: agentId, authToken: chatAuthToken, title: "MCP Demo Agent", placeholder: "Ask the agent to search the web or explore GitHub...", showHeader: true, showNewChatButton: true, showClearButton: false, showToolsMenu: true, showSkillsMenu: true, showTokenUsage: true, autoFocus: true, height: "100%", runtimeId: agentId, historyEndpoint: `${agentBaseUrl}/api/v1/history`, mcpStatusData: mcpStatusData, showToolApprovalBanner: true, headerActions: _jsx(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: _jsxs(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: ["MCP Tools: ", totalTools] }) }), suggestions: [
390
+ }, children: _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: "Agent already running - reconnected." }) })), _jsxs(Box, { sx: { flex: 1, minHeight: 0, display: 'flex' }, children: [_jsx(Box, { sx: { flex: 1, minWidth: 0 }, children: _jsx(Chat, { protocol: "vercel-ai", baseUrl: agentBaseUrl, agentId: agentId, authToken: chatAuthToken, title: "MCP Demo Agent", brandIcon: _jsx(GlobeIcon, { size: 16 }), placeholder: "Ask the agent to search the web or explore GitHub...", showHeader: true, showNewChatButton: true, showClearButton: false, showToolsMenu: true, showSkillsMenu: true, showTokenUsage: true, autoFocus: true, height: "100%", runtimeId: agentId, historyEndpoint: `${agentBaseUrl}/api/v1/history`, mcpStatusData: mcpStatusData, showToolApprovalBanner: true, headerActions: _jsx(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: _jsxs(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: ["MCP Tools: ", totalTools] }) }), suggestions: [
390
391
  {
391
392
  title: '🔍 Search the web',
392
393
  message: 'Search the web for recent news about AI agents.',
@@ -2,8 +2,7 @@
2
2
  * AgentMemoryExample
3
3
  *
4
4
  * Demonstrates the Mem0 memory backend for durable agents.
5
- * Creates a cloud agent runtime (environment: 'ai-agents-env') via the Datalayer
6
- * Runtimes API, then deploys an agent with persistent memory on its sidecar.
5
+ * Creates a local agent-runtimes agent using the `example-memory` spec.
7
6
  *
8
7
  * The left panel shows a standard Chat. The right panel shows the
9
8
  * agent's memory contents (fetched from the runtime sidecar) and lets
@@ -7,8 +7,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
7
  * AgentMemoryExample
8
8
  *
9
9
  * Demonstrates the Mem0 memory backend for durable agents.
10
- * Creates a cloud agent runtime (environment: 'ai-agents-env') via the Datalayer
11
- * Runtimes API, then deploys an agent with persistent memory on its sidecar.
10
+ * Creates a local agent-runtimes agent using the `example-memory` spec.
12
11
  *
13
12
  * The left panel shows a standard Chat. The right panel shows the
14
13
  * agent's memory contents (fetched from the runtime sidecar) and lets
@@ -26,31 +25,24 @@ import { uniqueAgentId } from './utils/agentId';
26
25
  const queryClient = new QueryClient();
27
26
  import { useSimpleAuthStore } from '@datalayer/core/lib/views/otel';
28
27
  import { Chat } from '../chat';
29
- import { useAgentRuntimes } from '../hooks/useAgentRuntimes';
28
+ import { useExampleAgentRuntimesUrl } from './utils/useExampleAgentRuntimesUrl';
30
29
  // ─── Constants ─────────────────────────────────────────────────────────────
31
- const AGENT_NAME = 'memory-demo-agent';
32
- const AGENT_SPEC_ID = 'monitor-sales-kpis'; // uses mem0 memory
30
+ const AGENT_NAME = 'memory-example-agent';
31
+ const AGENT_SPEC_ID = 'example-memory';
33
32
  // ─── Inner component (rendered after auth) ─────────────────────────────────
34
33
  const AgentMemoryInner = ({ onLogout }) => {
35
34
  const { token } = useSimpleAuthStore();
36
35
  const agentName = useRef(uniqueAgentId(AGENT_NAME)).current;
37
- const { runtime, status: runtimeStatus, isReady, error: hookError, } = useAgentRuntimes({
38
- agentSpecId: AGENT_SPEC_ID,
39
- autoStart: true,
40
- agentConfig: {
41
- name: agentName,
42
- model: 'bedrock:us.anthropic.claude-3-5-haiku-20241022-v1:0',
43
- protocol: 'vercel-ai',
44
- description: 'Agent with Mem0 persistent memory',
45
- },
46
- });
36
+ const agentBaseUrl = useExampleAgentRuntimesUrl();
37
+ const [runtimeStatus, setRuntimeStatus] = useState('launching');
38
+ const [isReady, setIsReady] = useState(false);
39
+ const [hookError, setHookError] = useState(null);
40
+ const [agentId, setAgentId] = useState(agentName);
47
41
  const [memories, setMemories] = useState([]);
48
42
  const [searchQuery, setSearchQuery] = useState('');
49
43
  const [searchResults, setSearchResults] = useState([]);
50
44
  const [searching, setSearching] = useState(false);
51
- const agentBaseUrl = runtime?.agentBaseUrl || '';
52
- const agentId = runtime?.agentId || AGENT_NAME;
53
- const podName = runtime?.podName || '(launching…)';
45
+ const podName = isReady ? `local:${agentId}` : '(launching…)';
54
46
  // Authenticated fetch helper (for sidecar endpoints)
55
47
  const authFetch = useCallback((url, opts = {}) => fetch(url, {
56
48
  ...opts,
@@ -60,6 +52,65 @@ const AgentMemoryInner = ({ onLogout }) => {
60
52
  ...(opts.headers ?? {}),
61
53
  },
62
54
  }), [token]);
55
+ useEffect(() => {
56
+ let isCancelled = false;
57
+ const createLocalAgent = async () => {
58
+ setRuntimeStatus('launching');
59
+ setIsReady(false);
60
+ setHookError(null);
61
+ try {
62
+ const response = await authFetch(`${agentBaseUrl}/api/v1/agents`, {
63
+ method: 'POST',
64
+ body: JSON.stringify({
65
+ name: agentName,
66
+ description: 'Agent with Mem0 persistent memory',
67
+ agent_library: 'pydantic-ai',
68
+ transport: 'vercel-ai',
69
+ agent_spec_id: AGENT_SPEC_ID,
70
+ enable_skills: true,
71
+ tools: [],
72
+ }),
73
+ });
74
+ let resolvedAgentId = agentName;
75
+ if (response.ok) {
76
+ const data = await response.json();
77
+ resolvedAgentId = data?.id || agentName;
78
+ }
79
+ else {
80
+ const contentType = response.headers.get('content-type') || '';
81
+ let detail = '';
82
+ if (contentType.includes('application/json')) {
83
+ const data = await response.json().catch(() => null);
84
+ detail =
85
+ (typeof data?.detail === 'string' && data.detail) ||
86
+ (typeof data?.message === 'string' && data.message) ||
87
+ '';
88
+ }
89
+ else {
90
+ detail = await response.text();
91
+ }
92
+ if (!(response.status === 409 || /already exists/i.test(detail))) {
93
+ throw new Error(detail || `Failed to create local agent: ${response.status}`);
94
+ }
95
+ }
96
+ if (!isCancelled) {
97
+ setAgentId(resolvedAgentId);
98
+ setIsReady(true);
99
+ setRuntimeStatus('ready');
100
+ }
101
+ }
102
+ catch (error) {
103
+ if (!isCancelled) {
104
+ setHookError(error instanceof Error ? error.message : 'Agent failed to start');
105
+ setRuntimeStatus('error');
106
+ }
107
+ }
108
+ };
109
+ void createLocalAgent();
110
+ return () => {
111
+ isCancelled = true;
112
+ };
113
+ }, [agentBaseUrl, agentName, authFetch]);
63
114
  // ── Fetch memory list ────────────────────────────────────────────────────
64
115
  const fetchMemories = useCallback(async () => {
65
116
  if (!isReady || !agentBaseUrl)
@@ -113,9 +164,7 @@ const AgentMemoryInner = ({ onLogout }) => {
113
164
  justifyContent: 'center',
114
165
  height: '100vh',
115
166
  gap: 3,
116
- }, children: [_jsx(Spinner, { size: "large" }), _jsx(Text, { sx: { color: 'fg.muted' }, children: runtimeStatus === 'launching'
117
- ? 'Launching runtime for memory agent…'
118
- : 'Creating memory-enabled agent…' })] }));
167
+ }, children: [_jsx(Spinner, { size: "large" }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Launching local memory-enabled agent\u2026" })] }));
119
168
  }
120
169
  if (runtimeStatus === 'error' || hookError) {
121
170
  return _jsx(ErrorView, { error: hookError, onLogout: onLogout });
@@ -139,7 +188,7 @@ const AgentMemoryInner = ({ onLogout }) => {
139
188
  minWidth: 0,
140
189
  borderRight: '1px solid',
141
190
  borderColor: 'border.default',
142
- }, children: _jsx(Chat, { protocol: "vercel-ai", baseUrl: agentBaseUrl, agentId: agentId, title: "Memory Agent", placeholder: "Chat \u2014 the agent remembers you across sessions\u2026", description: "Agent with Mem0 persistent memory", showHeader: true, showTokenUsage: true, autoFocus: true, height: "100%", runtimeId: podName, historyEndpoint: `${agentBaseUrl}/api/v1/history`, suggestions: [
191
+ }, children: _jsx(Chat, { protocol: "vercel-ai", baseUrl: agentBaseUrl, agentId: agentId, title: "Memory Agent", brandIcon: _jsx(DatabaseIcon, { size: 16 }), placeholder: "Chat \u2014 the agent remembers you across sessions\u2026", description: "Agent with Mem0 persistent memory", showHeader: true, showTokenUsage: true, autoFocus: true, height: "100%", runtimeId: podName, historyEndpoint: `${agentBaseUrl}/api/v1/history`, suggestions: [
143
192
  {
144
193
  title: 'Remember',
145
194
  message: 'My favourite colour is midnight blue.',