@datalayer/agent-runtimes 1.0.18 → 1.0.20

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 (146) hide show
  1. package/README.md +14 -6
  2. package/lib/AgentNode.js +567 -67
  3. package/lib/App.js +2 -2
  4. package/lib/agent-node/AgentNodeGallery.d.ts +80 -0
  5. package/lib/agent-node/AgentNodeGallery.js +627 -0
  6. package/lib/agent-notebook-main.js +0 -1
  7. package/lib/api/runtimes/runtimes.d.ts +57 -0
  8. package/lib/api/runtimes/runtimes.js +55 -1
  9. package/lib/chat/Chat.d.ts +14 -1
  10. package/lib/chat/Chat.js +22 -7
  11. package/lib/chat/base/ChatBase.js +86 -23
  12. package/lib/chat/document/EphemeralDocument.d.ts +49 -1
  13. package/lib/chat/document/EphemeralDocument.js +141 -15
  14. package/lib/chat/header/ChatHeaderBase.d.ts +3 -1
  15. package/lib/chat/header/ChatHeaderBase.js +5 -3
  16. package/lib/chat/messages/ChatMessageList.d.ts +19 -0
  17. package/lib/chat/messages/ChatMessageList.js +180 -9
  18. package/lib/chat/notebook/EphemeralNotebook.d.ts +40 -1
  19. package/lib/chat/notebook/EphemeralNotebook.js +49 -13
  20. package/lib/chat/prompt/InputPrompt.js +1 -1
  21. package/lib/chat/tools/SubagentActivity.d.ts +20 -0
  22. package/lib/chat/tools/SubagentActivity.js +154 -0
  23. package/lib/chat/tools/index.d.ts +1 -0
  24. package/lib/chat/tools/index.js +1 -0
  25. package/lib/chat/usage/TokenUsageBar.js +3 -3
  26. package/lib/client/AgentRuntimesClient.d.ts +5 -0
  27. package/lib/client/AgentsMixin.js +3 -1
  28. package/lib/client/mixins/RuntimesMixin.d.ts +12 -0
  29. package/lib/client/mixins/RuntimesMixin.js +24 -1
  30. package/lib/components/AgentSummary.d.ts +1 -0
  31. package/lib/components/AgentSummary.js +66 -10
  32. package/lib/components/runtimes/RuntimePickerBase.js +64 -29
  33. package/lib/components/runtimes/RuntimePickerCell.js +1 -1
  34. package/lib/components/runtimes/RuntimePickerNotebook.js +1 -1
  35. package/lib/components/runtimes/RuntimeSimplePicker.js +3 -2
  36. package/lib/components/runtimes/RuntimeUtils.js +2 -2
  37. package/lib/context/GraphFlowChart.js +1 -1
  38. package/lib/context/TokenUsageChart.js +1 -1
  39. package/lib/context/TurnGraphChart.js +2 -2
  40. package/lib/examples/A2UiAgentExample.js +4 -4
  41. package/lib/examples/A2UiComponentsGalleryExample.js +3 -3
  42. package/lib/examples/A2UiContactCardExample.js +3 -3
  43. package/lib/examples/A2UiRestaurantExample.js +2 -2
  44. package/lib/examples/A2UiViewerExample.js +3 -3
  45. package/lib/examples/AgUiAgenticExample.js +3 -3
  46. package/lib/examples/AgUiBackendToolRenderingExample.js +2 -2
  47. package/lib/examples/AgUiHaikuGenUiExample.js +3 -3
  48. package/lib/examples/AgUiHumanInTheLoopExample.js +5 -5
  49. package/lib/examples/AgUiSharedStateExample.js +3 -3
  50. package/lib/examples/AgUiToolsBasedGenUiExample.js +7 -5
  51. package/lib/examples/AgentCheckpointsExample.js +6 -5
  52. package/lib/examples/{AgentSandboxExample.d.ts → AgentCodeSandboxExample.d.ts} +4 -4
  53. package/lib/examples/{AgentSandboxExample.js → AgentCodeSandboxExample.js} +10 -9
  54. package/lib/examples/AgentCodeSandboxesExample.js +2 -2
  55. package/lib/examples/AgentCodemodeExample.js +3 -3
  56. package/lib/examples/AgentCompactionExample.d.ts +12 -0
  57. package/lib/examples/AgentCompactionExample.js +244 -0
  58. package/lib/examples/AgentEvalsExample.js +10 -20
  59. package/lib/examples/AgentGuardrailsExample.js +2 -2
  60. package/lib/examples/AgentHooksExample.js +1 -1
  61. package/lib/examples/AgentInferenceProviderExample.js +6 -8
  62. package/lib/examples/AgentLoopExample.js +3 -3
  63. package/lib/examples/AgentMCPExample.js +3 -3
  64. package/lib/examples/AgentMemoryExample.d.ts +4 -2
  65. package/lib/examples/AgentMemoryExample.js +93 -10
  66. package/lib/examples/AgentMonitoringExample.js +2 -2
  67. package/lib/examples/AgentNotificationsExample.js +1 -1
  68. package/lib/examples/AgentOtelExample.d.ts +2 -3
  69. package/lib/examples/AgentOtelExample.js +7 -12
  70. package/lib/examples/AgentOutputsExample.js +5 -5
  71. package/lib/examples/AgentParametersExample.js +3 -3
  72. package/lib/examples/AgentSkillsExample.js +4 -4
  73. package/lib/examples/AgentSubagentsExample.d.ts +2 -2
  74. package/lib/examples/AgentSubagentsExample.js +43 -34
  75. package/lib/examples/AgentToolApprovalsExample.js +1 -1
  76. package/lib/examples/AgentTriggersExample.js +9 -11
  77. package/lib/examples/AgentspecsExample.js +13 -5
  78. package/lib/examples/CellExample.d.ts +6 -0
  79. package/lib/examples/CellExample.js +84 -0
  80. package/lib/examples/ChatCustomExample.js +1 -1
  81. package/lib/examples/ChatExample.js +1 -1
  82. package/lib/examples/ChatStandaloneExample.js +2 -2
  83. package/lib/examples/HomeExample.js +29 -28
  84. package/lib/examples/Lexical2Example.js +2 -2
  85. package/lib/examples/LexicalAgentExample.js +1 -1
  86. package/lib/examples/LexicalAgentSidebarExample.js +2 -2
  87. package/lib/examples/NotebookAgentSidebarExample.d.ts +1 -1
  88. package/lib/examples/NotebookAgentSidebarExample.js +5 -4
  89. package/lib/examples/NotebookCollaborationExample.js +3 -3
  90. package/lib/examples/NotebookExample.js +2 -2
  91. package/lib/examples/components/ExampleWrapper.js +5 -0
  92. package/lib/examples/components/Header.js +2 -2
  93. package/lib/examples/components/HeaderControls.js +1 -1
  94. package/lib/examples/components/LexicalEditor.js +1 -1
  95. package/lib/examples/components/MainContent.js +1 -1
  96. package/lib/examples/components/MockFileBrowser.js +8 -8
  97. package/lib/examples/components/TimeTravel.js +1 -1
  98. package/lib/examples/components/haiku/HaikuDisplay.js +2 -2
  99. package/lib/examples/example-selector.js +4 -3
  100. package/lib/examples/hooks/useExampleAgentRuntime.js +11 -1
  101. package/lib/examples/hooks/useExampleAgentRuntimes.d.ts +7 -0
  102. package/lib/examples/hooks/useExampleAgentRuntimes.js +82 -0
  103. package/lib/examples/index.d.ts +2 -1
  104. package/lib/examples/index.js +2 -1
  105. package/lib/examples/main.js +359 -46
  106. package/lib/examples/utils/agentSummaryStore.d.ts +17 -0
  107. package/lib/examples/utils/themedProvider.js +32 -2
  108. package/lib/examples/utils/useExampleAgentRuntimesUrl.js +2 -1
  109. package/lib/examples2/NotebookMutationsKernel.js +6 -3
  110. package/lib/examples2/NotebookMutationsServiceManager.js +6 -3
  111. package/lib/examples2/ReactRouterExample.js +5 -5
  112. package/lib/examples2/main.js +38 -0
  113. package/lib/hooks/useAIAgentsWebSocket.js +35 -0
  114. package/lib/hooks/useAgentRuntimes.d.ts +22 -0
  115. package/lib/hooks/useAgentRuntimes.js +192 -5
  116. package/lib/hooks/useCheckpoints.js +2 -2
  117. package/lib/hooks/useToolApprovals.d.ts +10 -4
  118. package/lib/hooks/useToolApprovals.js +11 -5
  119. package/lib/identity/AgentIdentity.js +1 -2
  120. package/lib/models/Runtime.d.ts +10 -8
  121. package/lib/models/RuntimeDTO.d.ts +5 -3
  122. package/lib/models/RuntimeDTO.js +3 -3
  123. package/lib/runtimes/actions.js +3 -1
  124. package/lib/runtimes/apis.d.ts +7 -0
  125. package/lib/shims/primerAddonsBox.d.ts +1 -0
  126. package/lib/shims/primerAddonsBox.js +7 -0
  127. package/lib/specs/agents/agents.d.ts +51 -0
  128. package/lib/specs/agents/agents.js +3389 -94
  129. package/lib/specs/models.d.ts +2 -0
  130. package/lib/specs/models.js +37 -0
  131. package/lib/stores/agentRuntimeStore.d.ts +32 -1
  132. package/lib/stores/agentRuntimeStore.js +65 -1
  133. package/lib/stores/index.d.ts +1 -1
  134. package/lib/stores/index.js +1 -1
  135. package/lib/types/agents.d.ts +9 -3
  136. package/lib/types/agentspecs.d.ts +2 -0
  137. package/lib/types/chat.d.ts +83 -0
  138. package/lib/types/models.d.ts +5 -0
  139. package/lib/types/stream.d.ts +58 -1
  140. package/package.json +6 -5
  141. package/scripts/codegen/generate_agents.py +7 -0
  142. package/scripts/codegen/generate_models.py +22 -9
  143. package/scripts/gen_workers.py +340 -0
  144. package/lib/examples/CellSimpleExample.d.ts +0 -6
  145. package/lib/examples/CellSimpleExample.js +0 -20
  146. package/scripts/codegen/__pycache__/generate_sandbox_agents.cpython-313.pyc +0 -0
@@ -21,7 +21,8 @@ import { jsx as _jsx } from "react/jsx-runtime";
21
21
  */
22
22
  import { useEffect, useMemo, useRef, useState } from 'react';
23
23
  import { ServerConnection, ServiceManager } from '@jupyterlab/services';
24
- import { Box, DatalayerThemeProvider, getThemeConfig, useSystemColorMode, useThemeStore, } from '@datalayer/primer-addons';
24
+ import { DatalayerThemeProvider, getThemeConfig, useSystemColorMode, useThemeStore, } from '@datalayer/primer-addons';
25
+ import { Box } from '@primer/react';
25
26
  import { Notebook, NotebookToolbar, CellSidebarExtension, CellSidebarButton, JupyterReactTheme, notebookStore, disposeServiceManager, } from '@datalayer/jupyter-react';
26
27
  import { useAgentsRuntimes } from '../../hooks/useAgentRuntimes';
27
28
  import { registerSandboxServiceManager } from '../../services/sandboxServiceManagers';
@@ -54,7 +55,7 @@ const EPHEMERAL_NOTEBOOK_CONTENT = {
54
55
  /**
55
56
  * Renders an in-memory notebook backed by a sandbox kernel.
56
57
  */
57
- export function EphemeralNotebook({ notebookId, runtimePodName, cellSidebarMargin = 120, nbformat, onNbformatChange, toolbarComponent, }) {
58
+ export function EphemeralNotebook({ notebookId, runtimePodName, runtimeOverride, cellSidebarMargin = 120, nbformat, onNbformatChange, toolbarComponent, themeVariant, colorMode, collaborationProvider, }) {
58
59
  // The `nbformat` passed to the `Notebook` component MUST stay a stable
59
60
  // reference for the lifetime of a given `notebookId`: the underlying
60
61
  // `useNotebookModel` hook rebuilds the notebook model (and its context /
@@ -77,18 +78,36 @@ export function EphemeralNotebook({ notebookId, runtimePodName, cellSidebarMargi
77
78
  // notebook must bind to exactly the runtime assigned to this agent, or to
78
79
  // none at all (straight path).
79
80
  const { runtimes, refetchRuntimes } = useAgentsRuntimes();
80
- const selectedRuntime = useMemo(() => {
81
+ const podResolvedRuntime = useMemo(() => {
81
82
  const preferredPod = String(runtimePodName || '').trim();
82
83
  if (!preferredPod) {
83
84
  return undefined;
84
85
  }
85
86
  return runtimes.find(rt => String(rt?.pod_name || '') === preferredPod);
86
87
  }, [runtimePodName, runtimes]);
88
+ // An explicit endpoint override wins over the pod lookup: it lets a SaaS
89
+ // browser bind the kernel through the runtimes tunnel proxy (the node's
90
+ // Jupyter server is not directly reachable, so no pod exists in the list).
91
+ const selectedRuntime = useMemo(() => {
92
+ const overrideBaseUrl = String(runtimeOverride?.baseUrl || '').trim();
93
+ if (overrideBaseUrl) {
94
+ return {
95
+ url: overrideBaseUrl,
96
+ wsUrl: String(runtimeOverride?.wsUrl || '').trim() || undefined,
97
+ token: String(runtimeOverride?.token || '').trim(),
98
+ pod_name: String(runtimeOverride?.podName || '').trim() || 'agent-node-proxy',
99
+ };
100
+ }
101
+ return podResolvedRuntime;
102
+ }, [runtimeOverride, podResolvedRuntime]);
87
103
  // While the assigned pod has not yet appeared in the runtimes list, poll the
88
104
  // list quickly instead of waiting for the default (10s) refresh interval.
89
105
  // This is what makes the "Starting notebook…" state clear promptly once the
90
- // agent runtime is ready, rather than lingering for several seconds.
91
- const needsRuntimeLookup = Boolean(String(runtimePodName || '').trim() && !selectedRuntime);
106
+ // agent runtime is ready, rather than lingering for several seconds. The
107
+ // override path binds immediately, so no polling is needed there.
108
+ const needsRuntimeLookup = Boolean(!runtimeOverride?.baseUrl &&
109
+ String(runtimePodName || '').trim() &&
110
+ !selectedRuntime);
92
111
  useEffect(() => {
93
112
  if (!needsRuntimeLookup) {
94
113
  return;
@@ -120,9 +139,11 @@ export function EphemeralNotebook({ notebookId, runtimePodName, cellSidebarMargi
120
139
  }
121
140
  try {
122
141
  const token = String(selectedRuntime?.token || '').trim();
142
+ const wsUrl = String(selectedRuntime?.wsUrl || '').trim() ||
143
+ baseUrl.replace(/^http/, 'ws');
123
144
  const serverSettings = ServerConnection.makeSettings({
124
145
  baseUrl,
125
- wsUrl: baseUrl.replace(/^http/, 'ws'),
146
+ wsUrl,
126
147
  token,
127
148
  appendToken: true,
128
149
  });
@@ -155,7 +176,12 @@ export function EphemeralNotebook({ notebookId, runtimePodName, cellSidebarMargi
155
176
  disposeServiceManager(manager);
156
177
  }
157
178
  };
158
- }, [selectedRuntime?.pod_name, selectedRuntime?.url, selectedRuntime?.token]);
179
+ }, [
180
+ selectedRuntime?.pod_name,
181
+ selectedRuntime?.url,
182
+ selectedRuntime?.token,
183
+ selectedRuntime?.wsUrl,
184
+ ]);
159
185
  // Bind strictly to the agent runtime sandbox; there is NO local fallback
160
186
  // kernel. The notebook executes on the agent's runtime or shows a waiting
161
187
  // state until the runtime is ready.
@@ -163,9 +189,17 @@ export function EphemeralNotebook({ notebookId, runtimePodName, cellSidebarMargi
163
189
  const activeKernelId = runtimeKernelId;
164
190
  const activeStartDefaultKernel = runtimeStartDefaultKernel;
165
191
  const ToolbarComponent = toolbarComponent || NotebookToolbar;
166
- const isRuntimeStarting = Boolean(String(runtimePodName || '').trim() && !activeServiceManager);
192
+ const isRuntimeStarting = Boolean((String(runtimePodName || '').trim() ||
193
+ String(runtimeOverride?.baseUrl || '').trim()) &&
194
+ !activeServiceManager);
167
195
  useProgressTask(`ephemeral-notebook-start-${notebookId}`, isRuntimeStarting);
168
196
  useEffect(() => {
197
+ // When a collaboration provider is active the shared ydoc is the single
198
+ // source of truth and is synced remotely; the local in-memory persistence
199
+ // poll would fight it, so it is disabled in that mode.
200
+ if (collaborationProvider) {
201
+ return;
202
+ }
169
203
  // Read the CURRENT live notebook model straight from the notebook store.
170
204
  // The `NotebookAdapter` exposes `notebook` (the widget, whose `.model` is
171
205
  // the `INotebookModel`) and `panel` (`panel.content.model`). There is NO
@@ -203,10 +237,12 @@ export function EphemeralNotebook({ notebookId, runtimePodName, cellSidebarMargi
203
237
  // Resolve the active theme/color-mode exactly like the notebook editor
204
238
  // (NotebookEditorPanel) so the notebook honours dark / branded themes
205
239
  // instead of always rendering light.
206
- const { colorMode, theme: themeVariant } = useThemeStore();
240
+ const { colorMode: storeColorMode, theme: storeThemeVariant } = useThemeStore();
241
+ const effectiveColorMode = colorMode ?? storeColorMode;
242
+ const effectiveThemeVariant = themeVariant ?? storeThemeVariant;
207
243
  const systemMode = useSystemColorMode();
208
- const themeConfig = getThemeConfig(themeVariant);
209
- const resolvedMode = colorMode === 'auto' ? systemMode : colorMode;
244
+ const themeConfig = getThemeConfig(effectiveThemeVariant);
245
+ const resolvedMode = effectiveColorMode === 'auto' ? systemMode : effectiveColorMode;
210
246
  const modeStyles = resolvedMode === 'dark'
211
247
  ? themeConfig.themeStyles.dark
212
248
  : themeConfig.themeStyles.light;
@@ -219,7 +255,7 @@ export function EphemeralNotebook({ notebookId, runtimePodName, cellSidebarMargi
219
255
  position: 'relative',
220
256
  overflow: 'hidden',
221
257
  bg: 'canvas.default',
222
- }, children: activeServiceManager ? (_jsx(DatalayerThemeProvider, { colorMode: colorMode, theme: themeConfig.primerTheme, themeStyles: themeConfig.themeStyles, children: _jsx(JupyterReactTheme, { colormode: resolvedMode, backgroundColor: themeBackground, children: _jsx(Box, { sx: {
258
+ }, children: activeServiceManager ? (_jsx(DatalayerThemeProvider, { colorMode: effectiveColorMode, theme: themeConfig.primerTheme, themeStyles: themeConfig.themeStyles, children: _jsx(JupyterReactTheme, { colormode: resolvedMode, backgroundColor: themeBackground, children: _jsx(Box, { sx: {
223
259
  position: 'absolute',
224
260
  inset: 0,
225
261
  display: 'flex',
@@ -247,6 +283,6 @@ export function EphemeralNotebook({ notebookId, runtimePodName, cellSidebarMargi
247
283
  flex: '1 1 auto',
248
284
  minHeight: 0,
249
285
  },
250
- }, children: _jsx(Notebook, { nbformat: initialNbformat, id: notebookId, serviceManager: activeServiceManager, startDefaultKernel: activeStartDefaultKernel, kernelId: activeKernelId, height: "100%", cellSidebarMargin: cellSidebarMargin, extensions: extensions, Toolbar: ToolbarComponent }) }) }) })) : null }));
286
+ }, children: _jsx(Notebook, { nbformat: initialNbformat, id: notebookId, serviceManager: activeServiceManager, startDefaultKernel: activeStartDefaultKernel, kernelId: activeKernelId, height: "100%", cellSidebarMargin: cellSidebarMargin, extensions: extensions, Toolbar: ToolbarComponent, collaborationProvider: collaborationProvider }) }) }) })) : null }));
251
287
  }
252
288
  export default EphemeralNotebook;
@@ -76,7 +76,7 @@ export function InputPrompt({ variant = 'text', placeholder = 'Ask anything…',
76
76
  transition: 'border-color 0.2s ease',
77
77
  '&:focus-within': {
78
78
  borderColor: 'accent.fg',
79
- boxShadow: (t) => `0 0 0 1px ${t?.colors?.accent?.fg ?? '#0969da'}`,
79
+ boxShadow: (t) => `0 0 0 1px ${t?.colors?.accent?.fg ?? '#16A085'}`,
80
80
  },
81
81
  }, children: [_jsx(InputPromptHeader, { children: headerContent }), variant === 'lexical' ? (_jsx(InputPromptLexical, { value: input, onChange: setInput, placeholder: placeholder, disabled: isLoading || disabled, readOnly: readOnly, onSubmit: handleSend, autoFocus: autoFocus })) : (_jsx(InputPromptText, { value: input, onChange: setInput, placeholder: placeholder, disabled: isLoading || disabled, readOnly: readOnly, onSubmit: handleSend, inputRef: inputRef })), _jsx(InputPromptFooter, { isLoading: isLoading, isKernelBusy: isKernelBusy, sendDisabled: !input.trim() || disabled || readOnly, onSend: handleSend, onStop: handleStop, rightContent: footerRightContent, children: footerContent })] }) }) }));
82
82
  }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Live subagent activity panel.
3
+ *
4
+ * Renders the streamed interaction timeline for a `delegate_task` tool call:
5
+ * text/thinking deltas, nested tool calls and results, and the final output.
6
+ * Events are sourced from the agent runtime store, keyed by the parent
7
+ * delegation tool call id.
8
+ *
9
+ * @module chat/tools/SubagentActivity
10
+ */
11
+ import React from 'react';
12
+ export interface SubagentActivityProps {
13
+ /** Parent `delegate_task` tool call id used to look up streamed events. */
14
+ toolCallId: string;
15
+ }
16
+ /**
17
+ * Displays the live activity timeline for a delegated subagent run.
18
+ * Renders nothing until the first event for `toolCallId` arrives.
19
+ */
20
+ export declare function SubagentActivity({ toolCallId, }: SubagentActivityProps): React.ReactElement | null;
@@ -0,0 +1,154 @@
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
+ /**
7
+ * Live subagent activity panel.
8
+ *
9
+ * Renders the streamed interaction timeline for a `delegate_task` tool call:
10
+ * text/thinking deltas, nested tool calls and results, and the final output.
11
+ * Events are sourced from the agent runtime store, keyed by the parent
12
+ * delegation tool call id.
13
+ *
14
+ * @module chat/tools/SubagentActivity
15
+ */
16
+ import { useMemo, useState } from 'react';
17
+ import { Text } from '@primer/react';
18
+ import { Box } from '@datalayer/primer-addons';
19
+ import { ChevronDownIcon, ChevronRightIcon, DependabotIcon, ToolsIcon, CheckCircleIcon, AlertIcon, LightBulbIcon, } from '@primer/octicons-react';
20
+ import { useAgentRuntimeSubagentActivity } from '../../stores/agentRuntimeStore';
21
+ /**
22
+ * Collapse the ordered event stream into render segments, merging consecutive
23
+ * text/thinking deltas into single blocks.
24
+ */
25
+ function buildSegments(events) {
26
+ const segments = [];
27
+ for (const event of events) {
28
+ const phase = event.phase;
29
+ if (phase === 'text' || phase === 'thinking') {
30
+ const kind = phase;
31
+ const last = segments[segments.length - 1];
32
+ if (last && last.kind === kind) {
33
+ last.text = (last.text ?? '') + (event.text ?? '');
34
+ }
35
+ else {
36
+ segments.push({ kind, text: event.text ?? '' });
37
+ }
38
+ }
39
+ else if (phase === 'tool_call') {
40
+ segments.push({
41
+ kind: 'tool_call',
42
+ toolName: event.toolName,
43
+ toolArgs: event.toolArgs,
44
+ });
45
+ }
46
+ else if (phase === 'tool_result') {
47
+ segments.push({
48
+ kind: 'tool_result',
49
+ toolName: event.toolName,
50
+ result: event.result,
51
+ });
52
+ }
53
+ else if (phase === 'error') {
54
+ segments.push({ kind: 'error', error: event.error });
55
+ }
56
+ // `start` and `end` phases are represented by header/status, not segments.
57
+ }
58
+ return segments;
59
+ }
60
+ /**
61
+ * Displays the live activity timeline for a delegated subagent run.
62
+ * Renders nothing until the first event for `toolCallId` arrives.
63
+ */
64
+ export function SubagentActivity({ toolCallId, }) {
65
+ const events = useAgentRuntimeSubagentActivity(toolCallId);
66
+ const [expanded, setExpanded] = useState(true);
67
+ const subagentName = useMemo(() => {
68
+ for (const event of events) {
69
+ if (event.subagentName)
70
+ return event.subagentName;
71
+ }
72
+ return 'subagent';
73
+ }, [events]);
74
+ const isDone = useMemo(() => events.some(e => e.phase === 'end' || e.phase === 'error'), [events]);
75
+ const hasError = useMemo(() => events.some(e => e.phase === 'error'), [events]);
76
+ const segments = useMemo(() => buildSegments(events), [events]);
77
+ if (events.length === 0) {
78
+ return null;
79
+ }
80
+ const StatusIcon = hasError
81
+ ? AlertIcon
82
+ : isDone
83
+ ? CheckCircleIcon
84
+ : DependabotIcon;
85
+ const statusColor = hasError
86
+ ? 'danger.fg'
87
+ : isDone
88
+ ? 'success.fg'
89
+ : 'accent.fg';
90
+ return (_jsxs(Box, { sx: {
91
+ mt: 1,
92
+ ml: 2,
93
+ borderLeft: '2px solid',
94
+ borderColor: 'border.muted',
95
+ pl: 2,
96
+ }, children: [_jsxs(Box, { as: "button", onClick: () => setExpanded(prev => !prev), sx: {
97
+ display: 'flex',
98
+ alignItems: 'center',
99
+ gap: 1,
100
+ width: '100%',
101
+ border: 'none',
102
+ background: 'transparent',
103
+ cursor: 'pointer',
104
+ p: 0,
105
+ color: 'fg.muted',
106
+ textAlign: 'left',
107
+ }, children: [expanded ? (_jsx(ChevronDownIcon, { size: 12 })) : (_jsx(ChevronRightIcon, { size: 12 })), _jsx(Box, { sx: { color: statusColor, display: 'flex' }, children: _jsx(StatusIcon, { size: 14 }) }), _jsx(Text, { sx: { fontSize: 0, fontWeight: 'bold', color: 'fg.default' }, children: subagentName }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: hasError ? 'failed' : isDone ? 'done' : 'working…' })] }), expanded && (_jsx(Box, { sx: { mt: 1, display: 'flex', flexDirection: 'column', gap: 1 }, children: segments.map((segment, index) => (_jsx(SubagentSegmentView, { segment: segment }, index))) }))] }));
108
+ }
109
+ function SubagentSegmentView({ segment, }) {
110
+ switch (segment.kind) {
111
+ case 'text':
112
+ if (!segment.text)
113
+ return null;
114
+ return (_jsx(Text, { sx: {
115
+ fontSize: 0,
116
+ color: 'fg.default',
117
+ whiteSpace: 'pre-wrap',
118
+ wordBreak: 'break-word',
119
+ }, children: segment.text }));
120
+ case 'thinking':
121
+ if (!segment.text)
122
+ return null;
123
+ return (_jsxs(Box, { sx: { display: 'flex', gap: 1, color: 'fg.muted' }, children: [_jsx(Box, { sx: { mt: '2px', flexShrink: 0 }, children: _jsx(LightBulbIcon, { size: 12 }) }), _jsx(Text, { sx: {
124
+ fontSize: 0,
125
+ fontStyle: 'italic',
126
+ whiteSpace: 'pre-wrap',
127
+ wordBreak: 'break-word',
128
+ }, children: segment.text })] }));
129
+ case 'tool_call':
130
+ return (_jsxs(Box, { sx: { display: 'flex', gap: 1, color: 'fg.muted' }, children: [_jsx(Box, { sx: { mt: '2px', flexShrink: 0 }, children: _jsx(ToolsIcon, { size: 12 }) }), _jsxs(Box, { sx: { minWidth: 0 }, children: [_jsx(Text, { sx: { fontSize: 0, fontWeight: 'bold' }, children: segment.toolName ?? 'tool' }), segment.toolArgs && Object.keys(segment.toolArgs).length > 0 && (_jsx(Text, { as: "pre", sx: {
131
+ fontSize: 0,
132
+ fontFamily: 'mono',
133
+ m: 0,
134
+ mt: '2px',
135
+ whiteSpace: 'pre-wrap',
136
+ wordBreak: 'break-word',
137
+ color: 'fg.muted',
138
+ }, children: JSON.stringify(segment.toolArgs) }))] })] }));
139
+ case 'tool_result':
140
+ if (!segment.result)
141
+ return null;
142
+ return (_jsxs(Box, { sx: { display: 'flex', gap: 1, color: 'fg.muted' }, children: [_jsx(Box, { sx: { mt: '2px', flexShrink: 0, color: 'success.fg' }, children: _jsx(CheckCircleIcon, { size: 12 }) }), _jsx(Text, { as: "pre", sx: {
143
+ fontSize: 0,
144
+ fontFamily: 'mono',
145
+ m: 0,
146
+ whiteSpace: 'pre-wrap',
147
+ wordBreak: 'break-word',
148
+ }, children: segment.result })] }));
149
+ case 'error':
150
+ return (_jsxs(Box, { sx: { display: 'flex', gap: 1, color: 'danger.fg' }, children: [_jsx(Box, { sx: { mt: '2px', flexShrink: 0 }, children: _jsx(AlertIcon, { size: 12 }) }), _jsx(Text, { sx: { fontSize: 0 }, children: segment.error ?? 'Subagent failed' })] }));
151
+ default:
152
+ return null;
153
+ }
154
+ }
@@ -4,5 +4,6 @@
4
4
  * @module chat/tools
5
5
  */
6
6
  export { ToolCallDisplay, type ToolCallDisplayProps, type ErrorType, } from './ToolCallDisplay';
7
+ export { SubagentActivity, type SubagentActivityProps, } from './SubagentActivity';
7
8
  export { ToolApprovalBanner, type ToolApprovalBannerProps, type PendingApproval, } from './ToolApprovalBanner';
8
9
  export { ToolApprovalDialog, useToolApprovalDialog, type ToolApprovalDialogProps, } from './ToolApprovalDialog';
@@ -8,5 +8,6 @@
8
8
  * @module chat/tools
9
9
  */
10
10
  export { ToolCallDisplay, } from './ToolCallDisplay';
11
+ export { SubagentActivity, } from './SubagentActivity';
11
12
  export { ToolApprovalBanner, } from './ToolApprovalBanner';
12
13
  export { ToolApprovalDialog, useToolApprovalDialog, } from './ToolApprovalDialog';
@@ -53,7 +53,7 @@ export function TokenUsageBar({ agentUsage, padding }) {
53
53
  const colorMap = {
54
54
  'System Prompts': '#8250df',
55
55
  'Tool Definitions': '#bf8700',
56
- 'User Messages': '#0969da',
56
+ 'User Messages': '#16A085',
57
57
  'Assistant Messages': '#1a7f37',
58
58
  'Tool Usage': '#cf222e',
59
59
  };
@@ -86,7 +86,7 @@ export function TokenUsageBar({ agentUsage, padding }) {
86
86
  categories.push({
87
87
  name: 'Messages',
88
88
  value: messageTokens,
89
- color: '#0969da',
89
+ color: '#16A085',
90
90
  });
91
91
  }
92
92
  const toolResultTokens = (agentUsage.toolCallTokens || 0) + (agentUsage.toolReturnTokens || 0);
@@ -99,7 +99,7 @@ export function TokenUsageBar({ agentUsage, padding }) {
99
99
  }
100
100
  }
101
101
  // Tiny filled pie chart options
102
- const pieColor = pct > 90 ? '#cf222e' : pct > 70 ? '#bf8700' : '#0969da';
102
+ const pieColor = pct > 90 ? '#cf222e' : pct > 70 ? '#bf8700' : '#16A085';
103
103
  const freeSliceColor = 'var(--bgColor-muted, #f6f8fa)';
104
104
  const freeSliceOverlayColor = 'var(--borderColor-default, #d1d9e0)';
105
105
  const miniPieOption = {
@@ -13,6 +13,7 @@ import type { SpaceDTO, UpdateSpaceRequest } from '../models/SpaceDTO';
13
13
  import type { NotebookDTO } from '../models/NotebookDTO';
14
14
  import type { LexicalDTO } from '../models/LexicalDTO';
15
15
  import type { ProjectDTO, ProjectDefaultItems } from '../models/ProjectDTO';
16
+ import type { RuntimeMemory, ListRuntimeMemoriesOptions } from '../api/runtimes/runtimes';
16
17
  declare const AgentRuntimesClientWithMixins: {
17
18
  new (...args: any[]): {
18
19
  getMySpaces(): Promise<SpaceDTO[]>;
@@ -54,6 +55,8 @@ declare const AgentRuntimesClientWithMixins: {
54
55
  listRuntimes(): Promise<RuntimeDTO[]>;
55
56
  getRuntime(podName: string): Promise<RuntimeDTO>;
56
57
  deleteRuntime(podName: string): Promise<void>;
58
+ listRuntimeMemories(options?: ListRuntimeMemoriesOptions): Promise<RuntimeMemory[]>;
59
+ getRuntimeMemory(memoryId: string): Promise<RuntimeMemory>;
57
60
  terminateAllRuntimes(): Promise<PromiseSettledResult<void>[]>;
58
61
  createSnapshot(podName: string, name: string, description: string, stop?: boolean): Promise<CodeSandboxSnapshotDTO>;
59
62
  listSnapshots(): Promise<CodeSandboxSnapshotDTO[]>;
@@ -86,6 +89,8 @@ export interface AgentRuntimesClient extends DatalayerCoreClient {
86
89
  ensureRuntime(environmentName?: string, creditsLimit?: number, waitForReady?: boolean, maxWaitTime?: number, reuseExisting?: boolean, snapshotId?: string): Promise<RuntimeDTO>;
87
90
  createRuntime(environmentName: string, type: 'notebook' | 'terminal' | 'job', givenName: string, minutesLimit: number, fromSnapshotId?: string): Promise<RuntimeDTO>;
88
91
  listRuntimes(): Promise<RuntimeDTO[]>;
92
+ listRuntimeMemories(options?: ListRuntimeMemoriesOptions): Promise<RuntimeMemory[]>;
93
+ getRuntimeMemory(memoryId: string): Promise<RuntimeMemory>;
89
94
  getRuntime(podName: string): Promise<RuntimeDTO>;
90
95
  deleteRuntime(podName: string): Promise<void>;
91
96
  terminateAllRuntimes(): Promise<PromiseSettledResult<void>[]>;
@@ -339,7 +339,9 @@ export function AgentsMixin(Base) {
339
339
  method: 'POST',
340
340
  token,
341
341
  body: {
342
- environment_name: data.environmentName || 'ai-agents-env',
342
+ environment: {
343
+ name: data.environmentName || 'ai-agents-env',
344
+ },
343
345
  given_name: data.givenName || 'Agent',
344
346
  credits_limit: data.creditsLimit || 10,
345
347
  type: data.type || 'notebook',
@@ -1,3 +1,4 @@
1
+ import type { RuntimeMemory, ListRuntimeMemoriesOptions } from '../../api/runtimes/runtimes';
1
2
  import type { Constructor } from '@datalayer/core/lib/client/utils/mixins';
2
3
  import { EnvironmentDTO } from '../../models/EnvironmentDTO';
3
4
  import { RuntimeDTO } from '../../models/RuntimeDTO';
@@ -53,6 +54,17 @@ export declare function RuntimesMixin<TBase extends Constructor>(Base: TBase): {
53
54
  * @param podName - Runtime pod name
54
55
  */
55
56
  deleteRuntime(podName: string): Promise<void>;
57
+ /**
58
+ * List persisted memories for the authenticated caller.
59
+ *
60
+ * Backend scoping rules apply: non-admin callers are limited to their own
61
+ * personal account.
62
+ */
63
+ listRuntimeMemories(options?: ListRuntimeMemoriesOptions): Promise<RuntimeMemory[]>;
64
+ /**
65
+ * Fetch one persisted memory by id.
66
+ */
67
+ getRuntimeMemory(memoryId: string): Promise<RuntimeMemory>;
56
68
  /**
57
69
  * Terminate all runtimes.
58
70
  * Lists all runtimes and deletes them in parallel.
@@ -69,7 +69,9 @@ export function RuntimesMixin(Base) {
69
69
  const runtimesUrl = this.getRuntimesUrl();
70
70
  const creditsLimit = this.calculateCreditsFromMinutes(minutesLimit, env.burningRate);
71
71
  const data = {
72
- environment_name: environmentName,
72
+ environment: {
73
+ name: environmentName,
74
+ },
73
75
  type,
74
76
  given_name: givenName,
75
77
  credits_limit: creditsLimit,
@@ -113,6 +115,27 @@ export function RuntimesMixin(Base) {
113
115
  const runtimesUrl = this.getRuntimesUrl();
114
116
  await runtimes.deleteRuntime(token, podName, runtimesUrl);
115
117
  }
118
+ /**
119
+ * List persisted memories for the authenticated caller.
120
+ *
121
+ * Backend scoping rules apply: non-admin callers are limited to their own
122
+ * personal account.
123
+ */
124
+ async listRuntimeMemories(options = {}) {
125
+ const token = this.getToken();
126
+ const runtimesUrl = this.getRuntimesUrl();
127
+ const response = await runtimes.listRuntimeMemories(token, options, runtimesUrl);
128
+ return response.memories;
129
+ }
130
+ /**
131
+ * Fetch one persisted memory by id.
132
+ */
133
+ async getRuntimeMemory(memoryId) {
134
+ const token = this.getToken();
135
+ const runtimesUrl = this.getRuntimesUrl();
136
+ const response = await runtimes.getRuntimeMemory(token, memoryId, runtimesUrl);
137
+ return response.memory;
138
+ }
116
139
  /**
117
140
  * Terminate all runtimes.
118
141
  * Lists all runtimes and deletes them in parallel.
@@ -5,6 +5,7 @@ export interface AgentSummaryData {
5
5
  specId?: string;
6
6
  status?: string;
7
7
  baseUrl?: string;
8
+ sandboxBaseUrl?: string;
8
9
  agentId?: string;
9
10
  isReady?: boolean;
10
11
  error?: string;
@@ -3,9 +3,10 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  * Copyright (c) 2025-2026 Datalayer, Inc.
4
4
  * Distributed under the terms of the Modified BSD License.
5
5
  */
6
- import { useState } from 'react';
6
+ import { useEffect, useRef, useState } from 'react';
7
7
  import { Spinner } from '@primer/react';
8
8
  import { Box } from '@datalayer/primer-addons';
9
+ import { strip } from '@datalayer/core/lib/utils';
9
10
  /**
10
11
  * Statuses that represent an agent actively being created/started. The spinner
11
12
  * only shows for these; app-navigation states ("selected", "switching") and
@@ -19,20 +20,61 @@ const CREATING_STATUSES = new Set([
19
20
  'pending',
20
21
  'resuming',
21
22
  ]);
23
+ const MAX_AGENT_NAME_LENGTH = 32;
24
+ const toSandboxStatusUrl = (value) => {
25
+ const raw = String(value || '').trim();
26
+ if (!raw) {
27
+ return undefined;
28
+ }
29
+ try {
30
+ const parsed = new URL(raw);
31
+ parsed.pathname = `${parsed.pathname.replace(/\/$/, '')}/api`;
32
+ return parsed.toString();
33
+ }
34
+ catch {
35
+ const normalized = raw.replace(/\/$/, '');
36
+ return normalized.endsWith('/api') ? normalized : `${normalized}/api`;
37
+ }
38
+ };
22
39
  /**
23
40
  * Compact agent summary badge with a hover overlay for details.
24
41
  */
25
42
  export const AgentSummary = ({ summary, title = 'Active Agent', }) => {
26
43
  const [isHovering, setIsHovering] = useState(false);
27
- const runtimeStatusUrl = summary?.baseUrl
44
+ const closeTimerRef = useRef(null);
45
+ const clearCloseTimer = () => {
46
+ if (closeTimerRef.current !== null) {
47
+ window.clearTimeout(closeTimerRef.current);
48
+ closeTimerRef.current = null;
49
+ }
50
+ };
51
+ const handleMouseEnter = () => {
52
+ clearCloseTimer();
53
+ setIsHovering(true);
54
+ };
55
+ const handleMouseLeave = () => {
56
+ clearCloseTimer();
57
+ closeTimerRef.current = window.setTimeout(() => {
58
+ setIsHovering(false);
59
+ closeTimerRef.current = null;
60
+ }, 300);
61
+ };
62
+ useEffect(() => {
63
+ return () => {
64
+ clearCloseTimer();
65
+ };
66
+ }, []);
67
+ // Agent status is served by the agent-runtimes API server.
68
+ const apiBase = summary?.baseUrl
28
69
  ? (() => {
29
70
  const normalized = summary.baseUrl.replace(/\/$/, '');
30
- if (normalized.endsWith('/api/v1')) {
31
- return `${normalized}/runtime/status`;
32
- }
33
- return `${normalized}/api/v1/runtime/status`;
71
+ return normalized.endsWith('/api/v1')
72
+ ? normalized
73
+ : `${normalized}/api/v1`;
34
74
  })()
35
75
  : undefined;
76
+ const agentStatusUrl = apiBase ? `${apiBase}/runtime/status` : undefined;
77
+ const sandboxStatusUrl = toSandboxStatusUrl(summary?.sandboxBaseUrl);
36
78
  if (!summary) {
37
79
  return null;
38
80
  }
@@ -43,9 +85,17 @@ export const AgentSummary = ({ summary, title = 'Active Agent', }) => {
43
85
  !summary.agentId &&
44
86
  CREATING_STATUSES.has(normalizedStatus);
45
87
  const hasError = Boolean(summary.error);
46
- return (_jsxs(Box, { sx: { position: 'relative' }, onMouseEnter: () => setIsHovering(true), onMouseLeave: () => setIsHovering(false), children: [_jsxs(Box, { as: "button", type: "button", sx: {
88
+ // The agent is optional: some examples run only a code sandbox. Show the
89
+ // agent status when there is an agent (or one is being created).
90
+ const hasAgent = Boolean(summary.agentId) || isCreating;
91
+ const shortAgentName = strip(summary.agentName, MAX_AGENT_NAME_LENGTH);
92
+ const badgeLabel = `${summary.agentName} · ${summary.location}`;
93
+ const badgeLabelShort = `${shortAgentName} · ${summary.location}`;
94
+ return (_jsxs(Box, { sx: { position: 'relative' }, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: [_jsxs(Box, { as: "button", type: "button", title: badgeLabel, sx: {
47
95
  px: 2,
48
96
  py: '6px',
97
+ maxWidth: 280,
98
+ minWidth: 0,
49
99
  border: '1px solid',
50
100
  borderColor: hasError ? 'danger.emphasis' : 'border.default',
51
101
  borderRadius: 2,
@@ -56,7 +106,13 @@ export const AgentSummary = ({ summary, title = 'Active Agent', }) => {
56
106
  display: 'flex',
57
107
  alignItems: 'center',
58
108
  gap: 1,
59
- }, children: [isCreating && _jsx(Spinner, { size: "small", sx: { width: 12, height: 12 } }), hasError && _jsx("span", { "aria-hidden": true, children: "\u26A0" }), summary.agentName, " \u00B7 ", summary.location] }), isHovering && (_jsxs(Box, { sx: {
109
+ }, children: [isCreating && _jsx(Spinner, { size: "small", sx: { width: 12, height: 12 } }), hasError && _jsx("span", { "aria-hidden": true, children: "\u26A0" }), _jsx(Box, { as: "span", sx: {
110
+ display: 'block',
111
+ minWidth: 0,
112
+ overflow: 'hidden',
113
+ textOverflow: 'ellipsis',
114
+ whiteSpace: 'nowrap',
115
+ }, children: badgeLabelShort })] }), isHovering && (_jsxs(Box, { sx: {
60
116
  position: 'absolute',
61
117
  top: 'calc(100% + 8px)',
62
118
  right: 0,
@@ -70,13 +126,13 @@ export const AgentSummary = ({ summary, title = 'Active Agent', }) => {
70
126
  zIndex: 120,
71
127
  fontSize: 0,
72
128
  color: 'fg.default',
73
- }, children: [_jsx(Box, { sx: { mb: 1, fontWeight: 600 }, children: title }), _jsxs(Box, { sx: { color: 'fg.muted' }, children: ["Name: ", summary.agentName] }), _jsxs(Box, { sx: { color: 'fg.muted' }, children: ["Location: ", summary.location] }), _jsxs(Box, { sx: { color: 'fg.muted' }, children: ["Spec: ", summary.specId || '—'] }), _jsxs(Box, { sx: { color: 'fg.muted' }, children: ["Status: ", isCreating ? 'creating' : summary.status || '—'] }), _jsxs(Box, { sx: { color: 'fg.muted', wordBreak: 'break-all' }, children: ["Base URL: ", summary.baseUrl || '—'] }), _jsxs(Box, { sx: {
129
+ }, children: [_jsx(Box, { sx: { mb: 1, fontWeight: 600 }, children: title }), _jsxs(Box, { sx: { color: 'fg.muted' }, children: ["Name: ", summary.agentName] }), _jsxs(Box, { sx: { color: 'fg.muted' }, children: ["Location: ", summary.location] }), _jsxs(Box, { sx: { color: 'fg.muted' }, children: ["Spec: ", summary.specId || '—'] }), _jsxs(Box, { sx: { color: 'fg.muted' }, children: ["Status: ", isCreating ? 'creating' : summary.status || '—'] }), _jsxs(Box, { sx: { color: 'fg.muted', wordBreak: 'break-all' }, children: ["Agent base URL: ", summary.baseUrl || '—'] }), _jsxs(Box, { sx: { color: 'fg.muted', wordBreak: 'break-all' }, children: ["Code sandbox base URL:", ' ', summary.sandboxBaseUrl ? (_jsx(Box, { as: "a", href: summary.sandboxBaseUrl, target: "_blank", rel: "noopener noreferrer", sx: { color: 'accent.fg', textDecoration: 'underline' }, children: summary.sandboxBaseUrl })) : ('—')] }), _jsxs(Box, { sx: {
74
130
  color: 'fg.muted',
75
131
  wordBreak: 'break-all',
76
132
  display: 'flex',
77
133
  alignItems: 'center',
78
134
  gap: 1,
79
- }, children: ["Agent ID:", ' ', summary.agentId ? (summary.agentId) : isCreating ? (_jsxs(_Fragment, { children: [_jsx(Spinner, { size: "small", sx: { width: 12, height: 12 } }), "Creating\u2026"] })) : ('—')] }), _jsxs(Box, { sx: { color: 'fg.muted', wordBreak: 'break-all' }, children: ["Runtime status:", ' ', runtimeStatusUrl ? (_jsx(Box, { as: "a", href: runtimeStatusUrl, target: "_blank", rel: "noopener noreferrer", sx: { color: 'accent.fg', textDecoration: 'underline' }, children: runtimeStatusUrl })) : ('n/a')] }), hasError && (_jsxs(Box, { sx: {
135
+ }, children: ["Agent ID:", ' ', summary.agentId ? (summary.agentId) : isCreating ? (_jsxs(_Fragment, { children: [_jsx(Spinner, { size: "small", sx: { width: 12, height: 12 } }), "Creating\u2026"] })) : ('—')] }), _jsxs(Box, { sx: { color: 'fg.muted', wordBreak: 'break-all' }, children: ["Agent status:", ' ', hasAgent && agentStatusUrl ? (_jsx(Box, { as: "a", href: agentStatusUrl, target: "_blank", rel: "noopener noreferrer", sx: { color: 'accent.fg', textDecoration: 'underline' }, children: agentStatusUrl })) : ('n/a')] }), _jsxs(Box, { sx: { color: 'fg.muted', wordBreak: 'break-all' }, children: ["Code sandbox status:", ' ', sandboxStatusUrl ? (_jsx(Box, { as: "a", href: sandboxStatusUrl, target: "_blank", rel: "noopener noreferrer", sx: { color: 'accent.fg', textDecoration: 'underline' }, children: sandboxStatusUrl })) : ('n/a')] }), hasError && (_jsxs(Box, { sx: {
80
136
  mt: 1,
81
137
  p: 1,
82
138
  border: '1px solid',