@datalayer/agent-runtimes 1.0.18 → 1.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/lib/AgentNode.js +440 -41
- package/lib/agent-node/AgentNodeGallery.d.ts +80 -0
- package/lib/agent-node/AgentNodeGallery.js +464 -0
- package/lib/agent-notebook-main.js +0 -1
- package/lib/chat/Chat.d.ts +14 -1
- package/lib/chat/Chat.js +22 -7
- package/lib/chat/base/ChatBase.js +38 -12
- package/lib/chat/document/EphemeralDocument.d.ts +45 -1
- package/lib/chat/document/EphemeralDocument.js +135 -11
- package/lib/chat/notebook/EphemeralNotebook.d.ts +36 -1
- package/lib/chat/notebook/EphemeralNotebook.js +42 -9
- package/lib/identity/AgentIdentity.js +1 -2
- package/lib/shims/primerAddonsBox.d.ts +1 -0
- package/lib/shims/primerAddonsBox.js +7 -0
- package/lib/types/chat.d.ts +71 -0
- package/package.json +4 -3
- package/scripts/codegen/__pycache__/generate_sandbox_agents.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/versioning.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 {
|
|
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, 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
|
|
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
|
-
|
|
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,10 @@ 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() || baseUrl.replace(/^http/, 'ws');
|
|
123
143
|
const serverSettings = ServerConnection.makeSettings({
|
|
124
144
|
baseUrl,
|
|
125
|
-
wsUrl
|
|
145
|
+
wsUrl,
|
|
126
146
|
token,
|
|
127
147
|
appendToken: true,
|
|
128
148
|
});
|
|
@@ -155,7 +175,12 @@ export function EphemeralNotebook({ notebookId, runtimePodName, cellSidebarMargi
|
|
|
155
175
|
disposeServiceManager(manager);
|
|
156
176
|
}
|
|
157
177
|
};
|
|
158
|
-
}, [
|
|
178
|
+
}, [
|
|
179
|
+
selectedRuntime?.pod_name,
|
|
180
|
+
selectedRuntime?.url,
|
|
181
|
+
selectedRuntime?.token,
|
|
182
|
+
selectedRuntime?.wsUrl,
|
|
183
|
+
]);
|
|
159
184
|
// Bind strictly to the agent runtime sandbox; there is NO local fallback
|
|
160
185
|
// kernel. The notebook executes on the agent's runtime or shows a waiting
|
|
161
186
|
// state until the runtime is ready.
|
|
@@ -163,9 +188,17 @@ export function EphemeralNotebook({ notebookId, runtimePodName, cellSidebarMargi
|
|
|
163
188
|
const activeKernelId = runtimeKernelId;
|
|
164
189
|
const activeStartDefaultKernel = runtimeStartDefaultKernel;
|
|
165
190
|
const ToolbarComponent = toolbarComponent || NotebookToolbar;
|
|
166
|
-
const isRuntimeStarting = Boolean(String(runtimePodName || '').trim()
|
|
191
|
+
const isRuntimeStarting = Boolean((String(runtimePodName || '').trim() ||
|
|
192
|
+
String(runtimeOverride?.baseUrl || '').trim()) &&
|
|
193
|
+
!activeServiceManager);
|
|
167
194
|
useProgressTask(`ephemeral-notebook-start-${notebookId}`, isRuntimeStarting);
|
|
168
195
|
useEffect(() => {
|
|
196
|
+
// When a collaboration provider is active the shared ydoc is the single
|
|
197
|
+
// source of truth and is synced remotely; the local in-memory persistence
|
|
198
|
+
// poll would fight it, so it is disabled in that mode.
|
|
199
|
+
if (collaborationProvider) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
169
202
|
// Read the CURRENT live notebook model straight from the notebook store.
|
|
170
203
|
// The `NotebookAdapter` exposes `notebook` (the widget, whose `.model` is
|
|
171
204
|
// the `INotebookModel`) and `panel` (`panel.content.model`). There is NO
|
|
@@ -247,6 +280,6 @@ export function EphemeralNotebook({ notebookId, runtimePodName, cellSidebarMargi
|
|
|
247
280
|
flex: '1 1 auto',
|
|
248
281
|
minHeight: 0,
|
|
249
282
|
},
|
|
250
|
-
}, children: _jsx(Notebook, { nbformat: initialNbformat, id: notebookId, serviceManager: activeServiceManager, startDefaultKernel: activeStartDefaultKernel, kernelId: activeKernelId, height: "100%", cellSidebarMargin: cellSidebarMargin, extensions: extensions, Toolbar: ToolbarComponent }) }) }) })) : null }));
|
|
283
|
+
}, 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
284
|
}
|
|
252
285
|
export default EphemeralNotebook;
|
|
@@ -201,11 +201,10 @@ export function IdentityCard({ identity, providerConfig, showExpirationDetails =
|
|
|
201
201
|
justifyContent: 'center',
|
|
202
202
|
backgroundColor: display.color,
|
|
203
203
|
color: 'white',
|
|
204
|
-
flexShrink: 0,
|
|
205
204
|
}, children: _jsx(display.icon, { size: 16 }) }), _jsxs(Box, { sx: { flex: 1, display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Text, { sx: { fontWeight: 'semibold', fontSize: 1 }, children: display.name }), _jsx(Label, { size: "small", variant: "secondary", children: identity.authType === 'token' ? 'API Key' : 'OAuth' })] }), tokenStatus.isExpired ? (_jsx(Label, { variant: "danger", size: "small", children: _jsxs(Box, { sx: { display: 'inline-flex', alignItems: 'center', gap: 1 }, children: [_jsx(AlertIcon, { size: 12 }), " Expired"] }) })) : tokenStatus.isExpiringSoon ? (_jsx(Label, { variant: "attention", size: "small", children: _jsxs(Box, { sx: { display: 'inline-flex', alignItems: 'center', gap: 1 }, children: [_jsx(ClockIcon, { size: 12 }), " Expiring Soon"] }) })) : (_jsx(Label, { variant: "success", size: "small", children: _jsxs(Box, { sx: { display: 'inline-flex', alignItems: 'center', gap: 1 }, children: [_jsx(CheckCircleFillIcon, { size: 12 }), " Connected"] }) }))] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [identity.userInfo?.avatarUrl && (_jsx("a", { href: identity.userInfo.profileUrl ||
|
|
206
205
|
(provider === 'github'
|
|
207
206
|
? `https://github.com/${identity.userInfo.username}`
|
|
208
|
-
: undefined), target: "_blank", rel: "noopener noreferrer", style: { display: 'block', lineHeight: 0
|
|
207
|
+
: undefined), target: "_blank", rel: "noopener noreferrer", style: { display: 'block', lineHeight: 0 }, children: _jsx(Avatar, { src: identity.userInfo.avatarUrl, size: 32, alt: identity.userInfo.name ||
|
|
209
208
|
identity.userInfo.username ||
|
|
210
209
|
display.name, sx: { cursor: 'pointer' } }) })), _jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [identity.userInfo?.name && (_jsx(Text, { sx: { fontWeight: 'semibold', fontSize: 1, display: 'block' }, children: identity.userInfo.name })), (identity.userInfo?.username || identity.userInfo?.email) && (_jsx("a", { href: identity.userInfo.profileUrl ||
|
|
211
210
|
(provider === 'github'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Box as default, Box } from '@primer/react';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
// Runtime shim: force the primer-addons Box export to use Primer's Box.
|
|
6
|
+
// This prevents `sx` from being forwarded to raw DOM nodes as an attribute.
|
|
7
|
+
export { Box as default, Box } from '@primer/react';
|
package/lib/types/chat.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* @module types/chat
|
|
5
5
|
*/
|
|
6
6
|
import type { ComponentType, ReactNode } from 'react';
|
|
7
|
+
import type { ICollaborationProvider } from '@datalayer/jupyter-react';
|
|
7
8
|
import type { ChatMessage, MessageHandler } from './messages';
|
|
8
9
|
import type { Protocol, ProtocolConfig } from './protocol';
|
|
9
10
|
import type { McpServerSelection } from './inference';
|
|
@@ -12,6 +13,8 @@ import type { AgentRuntimeConfig } from './config';
|
|
|
12
13
|
import type { SandboxWsStatus } from './sandbox';
|
|
13
14
|
import type { FrontendToolDefinition } from './tools';
|
|
14
15
|
import type { PoweredByTagProps } from '../chat/display/PoweredByTag';
|
|
16
|
+
import type { EphemeralRuntimeOverride } from '../chat/notebook/EphemeralNotebook';
|
|
17
|
+
import type { EphemeralDocumentCollaboration } from '../chat/document/EphemeralDocument';
|
|
15
18
|
/**
|
|
16
19
|
* Context passed to tool-call pre-hooks.
|
|
17
20
|
* Fires when a tool call starts executing (backend or frontend).
|
|
@@ -366,6 +369,34 @@ export interface ChatCommonProps {
|
|
|
366
369
|
* Defaults to the toolbar from `@datalayer/jupyter-react` when omitted.
|
|
367
370
|
*/
|
|
368
371
|
ephemeralNotebookToolbar?: EphemeralNotebookToolbarComponent;
|
|
372
|
+
/**
|
|
373
|
+
* Optional real-time collaboration provider for the ephemeral notebook.
|
|
374
|
+
* When supplied, the notebook joins a shared collaborative room so its state
|
|
375
|
+
* transits over RTC (e.g. between an Agent Node and the SaaS UI) instead of
|
|
376
|
+
* the tunnel. The caller owns the provider lifecycle.
|
|
377
|
+
*/
|
|
378
|
+
ephemeralNotebookCollaborationProvider?: ICollaborationProvider;
|
|
379
|
+
/**
|
|
380
|
+
* Explicit collaboration room / document id for the ephemeral notebook. When
|
|
381
|
+
* set it becomes the notebook id directly so multiple peers (e.g. an Agent
|
|
382
|
+
* Node and the SaaS UI) share the same collaborative room and tool scope.
|
|
383
|
+
*/
|
|
384
|
+
ephemeralNotebookCollaborationDocumentId?: string;
|
|
385
|
+
/**
|
|
386
|
+
* Optional real-time collaboration configuration for the ephemeral document
|
|
387
|
+
* (Lexical). When supplied the document joins a shared Loro room over
|
|
388
|
+
* WebSocket so its rich-text state transits over RTC (e.g. between an Agent
|
|
389
|
+
* Node and the SaaS UI). Its `roomId` also becomes the document id so all
|
|
390
|
+
* peers share the same collaborative room and lexical tool scope.
|
|
391
|
+
*/
|
|
392
|
+
ephemeralDocumentCollaboration?: EphemeralDocumentCollaboration;
|
|
393
|
+
/**
|
|
394
|
+
* Explicit runtime endpoint for the ephemeral notebook kernel. When set, the
|
|
395
|
+
* notebook binds its kernel to this endpoint directly instead of resolving a
|
|
396
|
+
* pod from the user's runtimes list — used to reach an Agent Node's Jupyter
|
|
397
|
+
* server through the runtimes tunnel HTTP/WebSocket proxy.
|
|
398
|
+
*/
|
|
399
|
+
ephemeralRuntimeOverride?: EphemeralRuntimeOverride;
|
|
369
400
|
/** Pre-hook: fires when a tool call starts executing */
|
|
370
401
|
onToolCallStart?: (context: ToolCallStartContext) => void;
|
|
371
402
|
/** Post-hook: fires when a tool result is received */
|
|
@@ -419,6 +450,18 @@ export interface ChatCommonProps {
|
|
|
419
450
|
* so the indicator reflects the surface's runtime.
|
|
420
451
|
*/
|
|
421
452
|
kernel?: import('@jupyterlab/services/lib/kernel/kernel').IKernelConnection | null;
|
|
453
|
+
/**
|
|
454
|
+
* Optional environment name displayed in the chat header's kernel
|
|
455
|
+
* indicator details (e.g. the agent's local sandbox name). Defaults to
|
|
456
|
+
* the indicator's own "browser-runtime" placeholder when omitted.
|
|
457
|
+
*/
|
|
458
|
+
kernelEnvironmentName?: string;
|
|
459
|
+
/** Optional CPU info displayed in the chat header's kernel indicator. */
|
|
460
|
+
kernelCpu?: string;
|
|
461
|
+
/** Optional memory info displayed in the chat header's kernel indicator. */
|
|
462
|
+
kernelMemory?: string;
|
|
463
|
+
/** Optional GPU info displayed in the chat header's kernel indicator. */
|
|
464
|
+
kernelGpu?: string;
|
|
422
465
|
/**
|
|
423
466
|
* Disable ChatBase's internal JupyterReactTheme wrapper.
|
|
424
467
|
* Use this when the host page already provides a JupyterReactTheme boundary.
|
|
@@ -746,6 +789,34 @@ export interface ChatBaseProps {
|
|
|
746
789
|
* Defaults to the toolbar from `@datalayer/jupyter-react` when omitted.
|
|
747
790
|
*/
|
|
748
791
|
ephemeralNotebookToolbar?: EphemeralNotebookToolbarComponent;
|
|
792
|
+
/**
|
|
793
|
+
* Optional real-time collaboration provider for the ephemeral notebook.
|
|
794
|
+
* When supplied, the notebook joins a shared collaborative room so its state
|
|
795
|
+
* transits over RTC (e.g. between an Agent Node and the SaaS UI) instead of
|
|
796
|
+
* the tunnel. The caller owns the provider lifecycle.
|
|
797
|
+
*/
|
|
798
|
+
ephemeralNotebookCollaborationProvider?: ICollaborationProvider;
|
|
799
|
+
/**
|
|
800
|
+
* Explicit collaboration room / document id for the ephemeral notebook. When
|
|
801
|
+
* set it becomes the notebook id directly so multiple peers (e.g. an Agent
|
|
802
|
+
* Node and the SaaS UI) share the same collaborative room and tool scope.
|
|
803
|
+
*/
|
|
804
|
+
ephemeralNotebookCollaborationDocumentId?: string;
|
|
805
|
+
/**
|
|
806
|
+
* Optional real-time collaboration configuration for the ephemeral document
|
|
807
|
+
* (Lexical). When supplied the document joins a shared Loro room over
|
|
808
|
+
* WebSocket so its rich-text state transits over RTC (e.g. between an Agent
|
|
809
|
+
* Node and the SaaS UI). Its `roomId` also becomes the document id so all
|
|
810
|
+
* peers share the same collaborative room and lexical tool scope.
|
|
811
|
+
*/
|
|
812
|
+
ephemeralDocumentCollaboration?: EphemeralDocumentCollaboration;
|
|
813
|
+
/**
|
|
814
|
+
* Explicit runtime endpoint for the ephemeral notebook kernel. When set, the
|
|
815
|
+
* notebook binds its kernel to this endpoint directly instead of resolving a
|
|
816
|
+
* pod from the user's runtimes list — used to reach an Agent Node's Jupyter
|
|
817
|
+
* server through the runtimes tunnel HTTP/WebSocket proxy.
|
|
818
|
+
*/
|
|
819
|
+
ephemeralRuntimeOverride?: EphemeralRuntimeOverride;
|
|
749
820
|
/**
|
|
750
821
|
* Callback when the agent requests authorization for an external service.
|
|
751
822
|
* This is called when a tool needs OAuth access to a service like GitHub.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datalayer/agent-runtimes",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.19",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"workspaces": [
|
|
6
6
|
".",
|
|
@@ -102,8 +102,8 @@
|
|
|
102
102
|
"start:acp:vite": "VITE_ACP_WS_URL=ws://localhost:8765/api/v1/acp/ws vite",
|
|
103
103
|
"start:agent": "run-p server:start start:agent:vite",
|
|
104
104
|
"start:agent:vite": "vite --open /html/agent.html",
|
|
105
|
-
"start:agent-node": "run-p server:start:agent-node start:agent-node:vite",
|
|
106
|
-
"start:agent-node:vite": "vite --open /html/agent-node.html",
|
|
105
|
+
"start:agent-node": "run-p server:start:agent-node jupyter:start start:agent-node:vite",
|
|
106
|
+
"start:agent-node:vite": "VITE_JUPYTER_SANDBOX_URL=${VITE_JUPYTER_SANDBOX_URL:-http://localhost:8888/api/jupyter-server?token=60c1661cc408f978c309d04157af55c9588ff9557c9380e4fb50785750703da6} vite --open /html/agent-node.html",
|
|
107
107
|
"start:agent-notebook": "run-p server:start jupyter:start start:agent-notebook:vite",
|
|
108
108
|
"start:agent-notebook:vite": "vite --open /html/agent-notebook.html",
|
|
109
109
|
"start:agent-document": "run-p server:start jupyter:start start:agent-document:vite",
|
|
@@ -141,6 +141,7 @@
|
|
|
141
141
|
"@datalayer/core": "^1.1.48",
|
|
142
142
|
"@datalayer/icons-react": "^1.0.7",
|
|
143
143
|
"@datalayer/jupyter-lexical": "^1.0.16",
|
|
144
|
+
"@datalayer/lexical-loro": "^1.0.0",
|
|
144
145
|
"@datalayer/jupyter-react": "^2.0.7",
|
|
145
146
|
"@datalayer/primer-addons": "^1.0.15",
|
|
146
147
|
"@datalayer/primer-rjsf": "^1.0.3",
|
|
Binary file
|
|
Binary file
|