@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
package/lib/chat/Chat.js
CHANGED
|
@@ -136,7 +136,7 @@ function getProtocolType(protocol) {
|
|
|
136
136
|
* />
|
|
137
137
|
* ```
|
|
138
138
|
*/
|
|
139
|
-
export function Chat({ protocol: transport, extensions: _extensions, baseUrl = 'http://localhost:8765', wsUrl, agentId, authToken: authTokenProp, placeholder = 'Type your message...', title, subtitle, brandIcon, autoConnect = true, streaming: _streaming = true, onMessageSent: _onMessageSent, onMessageReceived: _onMessageReceived, onDisconnect, onLogout: _onLogout, onCollapsePanel: _onCollapsePanel, className, height = '600px', showHeader = true, showNewChatButton = true, showClearButton = true, showModelSelector = true, showToolsMenu = true, showInput = true, disableInputPrompt = false, overlay, launching = false, launchingMessage, showSkillsMenu = true, codemodeEnabled = false, onToggleCodemode, showTokenUsage = true, initialModel, availableModels, mcpServers, initialSkills, clearOnMount: _clearOnMount = true, suggestions, submitOnSuggestionClick = true, description, headerContent, headerActions, autoFocus = false, identityProviders, onIdentityConnect, onIdentityDisconnect, runtimeId, historyEndpoint, pendingPrompt, errorBanner, showInformation = true, chatViewMode, onChatViewModeChange, frontendTools, enableEphemeralNotebook, initialEphemeralNotebookOpen, onEphemeralNotebookOpenChange, enableEphemeralDocument, initialEphemeralSurfaceMode, onEphemeralSurfaceModeChange, collapsed = false, onExpandFromCollapsed, ephemeralNotebookToolbar, onToolCallStart, onToolCallComplete, renderToolResult, hideMessagesAfterToolUI = false, contextSnapshot, mcpStatusData, codemodeStatusData, sandboxStatusData, kernel, showToolApprovalBanner, pendingApprovals, onApproveApproval, onRejectApproval, disableInternalJupyterTheme = false, }) {
|
|
139
|
+
export function Chat({ protocol: transport, extensions: _extensions, baseUrl = 'http://localhost:8765', endpoint: endpointProp, configEndpoint: configEndpointProp, enableConfigQuery: enableConfigQueryProp, wsUrl, agentId, authToken: authTokenProp, placeholder = 'Type your message...', title, subtitle, brandIcon, autoConnect = true, streaming: _streaming = true, onMessageSent: _onMessageSent, onMessageReceived: _onMessageReceived, onDisconnect, onLogout: _onLogout, onCollapsePanel: _onCollapsePanel, className, height = '600px', showHeader = true, showNewChatButton = true, showClearButton = true, showModelSelector = true, showToolsMenu = true, showInput = true, disableInputPrompt = false, overlay, launching = false, launchingMessage, showSkillsMenu = true, codemodeEnabled = false, onToggleCodemode, showTokenUsage = true, initialModel, availableModels, mcpServers, initialSkills, clearOnMount: _clearOnMount = true, suggestions, submitOnSuggestionClick = true, description, headerContent, headerActions, autoFocus = false, identityProviders, onIdentityConnect, onIdentityDisconnect, runtimeId, historyEndpoint, pendingPrompt, errorBanner, showInformation = true, chatViewMode, onChatViewModeChange, frontendTools, enableEphemeralNotebook, initialEphemeralNotebookOpen, onEphemeralNotebookOpenChange, enableEphemeralDocument, initialEphemeralSurfaceMode, onEphemeralSurfaceModeChange, collapsed = false, onExpandFromCollapsed, ephemeralNotebookToolbar, ephemeralNotebookCollaborationProvider, ephemeralNotebookCollaborationDocumentId, ephemeralDocumentCollaboration, ephemeralRuntimeOverride, onToolCallStart, onToolCallComplete, renderToolResult, hideMessagesAfterToolUI = false, contextSnapshot, mcpStatusData, codemodeStatusData, sandboxStatusData, kernel, kernelEnvironmentName, kernelCpu, kernelMemory, kernelGpu, showToolApprovalBanner, pendingApprovals, onApproveApproval, onRejectApproval, disableInternalJupyterTheme = false, }) {
|
|
140
140
|
const resolvedAgentId = typeof agentId === 'string' && agentId.trim().length > 0
|
|
141
141
|
? agentId.trim()
|
|
142
142
|
: 'default';
|
|
@@ -220,6 +220,10 @@ export function Chat({ protocol: transport, extensions: _extensions, baseUrl = '
|
|
|
220
220
|
break;
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
|
+
// Explicit endpoint override (e.g. Agent Node tunnel proxy).
|
|
224
|
+
if (endpointProp) {
|
|
225
|
+
endpoint = endpointProp;
|
|
226
|
+
}
|
|
223
227
|
return {
|
|
224
228
|
config: {
|
|
225
229
|
type: getProtocolType(transport),
|
|
@@ -228,12 +232,14 @@ export function Chat({ protocol: transport, extensions: _extensions, baseUrl = '
|
|
|
228
232
|
authToken,
|
|
229
233
|
options,
|
|
230
234
|
// Enable config query for all protocols to fetch models and tools
|
|
231
|
-
enableConfigQuery: true,
|
|
235
|
+
enableConfigQuery: enableConfigQueryProp ?? true,
|
|
232
236
|
// For Jupyter-based transports, use Jupyter requestAPI (configEndpoint undefined)
|
|
233
237
|
// For FastAPI-based transports, use direct fetch to the configure endpoint
|
|
234
|
-
configEndpoint:
|
|
235
|
-
?
|
|
236
|
-
:
|
|
238
|
+
configEndpoint: configEndpointProp !== undefined
|
|
239
|
+
? configEndpointProp
|
|
240
|
+
: transport === 'vercel-ai-jupyter'
|
|
241
|
+
? undefined // Use Jupyter requestAPI
|
|
242
|
+
: `${baseUrl}/api/v1/configure`,
|
|
237
243
|
},
|
|
238
244
|
error: null,
|
|
239
245
|
};
|
|
@@ -245,7 +251,16 @@ export function Chat({ protocol: transport, extensions: _extensions, baseUrl = '
|
|
|
245
251
|
error: err instanceof Error ? err.message : 'Failed to configure',
|
|
246
252
|
};
|
|
247
253
|
}
|
|
248
|
-
}, [
|
|
254
|
+
}, [
|
|
255
|
+
transport,
|
|
256
|
+
baseUrl,
|
|
257
|
+
wsUrl,
|
|
258
|
+
resolvedAgentId,
|
|
259
|
+
authTokenProp,
|
|
260
|
+
endpointProp,
|
|
261
|
+
configEndpointProp,
|
|
262
|
+
enableConfigQueryProp,
|
|
263
|
+
]);
|
|
249
264
|
const protocolConfig = protocolConfigResult.config;
|
|
250
265
|
// Set initialized once protocol config is built
|
|
251
266
|
useEffect(() => {
|
|
@@ -349,6 +364,6 @@ export function Chat({ protocol: transport, extensions: _extensions, baseUrl = '
|
|
|
349
364
|
? 'attention.fg'
|
|
350
365
|
: 'danger.fg',
|
|
351
366
|
flex: 1,
|
|
352
|
-
}, children: errorBanner.message })] })), _jsx(ChatBase, { title: title, subtitle: subtitle, brandIcon: brandIcon, showHeader: showHeader, protocol: protocolConfig, placeholder: placeholder, description: description, suggestions: suggestions, submitOnSuggestionClick: submitOnSuggestionClick, autoFocus: autoFocus, runtimeId: runtimeId, historyEndpoint: historyEndpoint, pendingPrompt: pendingPrompt, showInformation: showInformation, onInformationClick: handleInformationClick, headerContent: headerContent, headerActions: headerActions, showModelSelector: showModelSelector, showToolsMenu: showToolsMenu, showInput: showInput, disableInputPrompt: disableInputPrompt, overlay: overlay, launching: launching, launchingMessage: launchingMessage, autoConnect: autoConnect, showSkillsMenu: showSkillsMenu, showTokenUsage: showTokenUsage, codemodeEnabled: codemodeEnabled, onToggleCodemode: onToggleCodemode, initialModel: initialModel, availableModels: availableModels, mcpServers: mcpServers, initialSkills: initialSkills, connectedIdentities: identitiesForChat, onNewChat: handleNewChat, onMessagesChange: handleMessagesChange, headerButtons: headerButtons, avatarConfig: avatarConfig, backgroundColor: "canvas.default", focusTrigger: focusTrigger, chatViewMode: chatViewMode, onChatViewModeChange: onChatViewModeChange, frontendTools: frontendTools, enableEphemeralNotebook: enableEphemeralNotebook, initialEphemeralNotebookOpen: initialEphemeralNotebookOpen, onEphemeralNotebookOpenChange: onEphemeralNotebookOpenChange, enableEphemeralDocument: enableEphemeralDocument, initialEphemeralSurfaceMode: initialEphemeralSurfaceMode, onEphemeralSurfaceModeChange: onEphemeralSurfaceModeChange, collapsed: collapsed, onExpandFromCollapsed: onExpandFromCollapsed, ephemeralNotebookToolbar: ephemeralNotebookToolbar, onToolCallStart: onToolCallStart, onToolCallComplete: onToolCallComplete, renderToolResult: renderToolResult, hideMessagesAfterToolUI: hideMessagesAfterToolUI, contextSnapshot: contextSnapshot, mcpStatusData: mcpStatusData, sandboxStatusData: sandboxStatusData, kernel: kernel, showToolApprovalBanner: showToolApprovalBanner, pendingApprovals: pendingApprovals, onApproveApproval: onApproveApproval, onRejectApproval: onRejectApproval, disableInternalJupyterTheme: disableInternalJupyterTheme })] })] }) }) }));
|
|
367
|
+
}, children: errorBanner.message })] })), _jsx(ChatBase, { title: title, subtitle: subtitle, brandIcon: brandIcon, showHeader: showHeader, protocol: protocolConfig, placeholder: placeholder, description: description, suggestions: suggestions, submitOnSuggestionClick: submitOnSuggestionClick, autoFocus: autoFocus, runtimeId: runtimeId, historyEndpoint: historyEndpoint, pendingPrompt: pendingPrompt, showInformation: showInformation, onInformationClick: handleInformationClick, headerContent: headerContent, headerActions: headerActions, showModelSelector: showModelSelector, showToolsMenu: showToolsMenu, showInput: showInput, disableInputPrompt: disableInputPrompt, overlay: overlay, launching: launching, launchingMessage: launchingMessage, autoConnect: autoConnect, showSkillsMenu: showSkillsMenu, showTokenUsage: showTokenUsage, codemodeEnabled: codemodeEnabled, onToggleCodemode: onToggleCodemode, initialModel: initialModel, availableModels: availableModels, mcpServers: mcpServers, initialSkills: initialSkills, connectedIdentities: identitiesForChat, onNewChat: handleNewChat, onMessagesChange: handleMessagesChange, headerButtons: headerButtons, avatarConfig: avatarConfig, backgroundColor: "canvas.default", focusTrigger: focusTrigger, chatViewMode: chatViewMode, onChatViewModeChange: onChatViewModeChange, frontendTools: frontendTools, enableEphemeralNotebook: enableEphemeralNotebook, initialEphemeralNotebookOpen: initialEphemeralNotebookOpen, onEphemeralNotebookOpenChange: onEphemeralNotebookOpenChange, enableEphemeralDocument: enableEphemeralDocument, initialEphemeralSurfaceMode: initialEphemeralSurfaceMode, onEphemeralSurfaceModeChange: onEphemeralSurfaceModeChange, collapsed: collapsed, onExpandFromCollapsed: onExpandFromCollapsed, ephemeralNotebookToolbar: ephemeralNotebookToolbar, ephemeralNotebookCollaborationProvider: ephemeralNotebookCollaborationProvider, ephemeralNotebookCollaborationDocumentId: ephemeralNotebookCollaborationDocumentId, ephemeralDocumentCollaboration: ephemeralDocumentCollaboration, ephemeralRuntimeOverride: ephemeralRuntimeOverride, onToolCallStart: onToolCallStart, onToolCallComplete: onToolCallComplete, renderToolResult: renderToolResult, hideMessagesAfterToolUI: hideMessagesAfterToolUI, contextSnapshot: contextSnapshot, mcpStatusData: mcpStatusData, sandboxStatusData: sandboxStatusData, kernel: kernel, kernelEnvironmentName: kernelEnvironmentName, kernelCpu: kernelCpu, kernelMemory: kernelMemory, kernelGpu: kernelGpu, showToolApprovalBanner: showToolApprovalBanner, pendingApprovals: pendingApprovals, onApproveApproval: onApproveApproval, onRejectApproval: onRejectApproval, disableInternalJupyterTheme: disableInternalJupyterTheme })] })] }) }) }));
|
|
353
368
|
}
|
|
354
369
|
export default Chat;
|
|
@@ -17,11 +17,11 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
17
17
|
*/
|
|
18
18
|
import { useContext } from 'react';
|
|
19
19
|
import React, { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
|
|
20
|
-
import { Text, Spinner, IconButton } from '@primer/react';
|
|
20
|
+
import { Box, Text, Spinner, IconButton } from '@primer/react';
|
|
21
21
|
import { SkeletonText } from '@primer/react/experimental';
|
|
22
22
|
import { SidebarExpandIcon } from '@primer/octicons-react';
|
|
23
23
|
import { notebookStore, JupyterReactTheme } from '@datalayer/jupyter-react';
|
|
24
|
-
import {
|
|
24
|
+
import { setupPrimerPortals, useThemeStore, getColorPalette, getThemeConfig, useSystemColorMode, } from '@datalayer/primer-addons';
|
|
25
25
|
import { AlertIcon, PersonIcon } from '@primer/octicons-react';
|
|
26
26
|
import { AiAgentIcon } from '@datalayer/icons-react';
|
|
27
27
|
import { QueryClientProvider, QueryClientContext } from '@tanstack/react-query';
|
|
@@ -472,7 +472,7 @@ function ChatBaseInner({ title, subtitle, showHeader = false, showTokenUsage = t
|
|
|
472
472
|
// Mode selection
|
|
473
473
|
useStore: useStoreMode = true, protocol: protocolRaw, onSendMessage, enableStreaming = false,
|
|
474
474
|
// Extended props
|
|
475
|
-
brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, emptyState, renderToolResult, footerContent, showInformation = false, onInformationClick, headerContent, children, borderRadius, backgroundColor, border, boxShadow, compact = false, placeholder, description = 'Start a conversation with the AI agent.', onStateUpdate, onNewChat, onClear, onMessagesChange, autoFocus = false, suggestions, submitOnSuggestionClick = true, hideMessagesAfterToolUI = false, focusTrigger, frontendTools: frontendToolsProp, enableEphemeralNotebook = false, initialEphemeralNotebookOpen = true, onEphemeralNotebookOpenChange, enableEphemeralDocument = false, initialEphemeralSurfaceMode, onEphemeralSurfaceModeChange, collapsed = false, onExpandFromCollapsed, ephemeralNotebookToolbar,
|
|
475
|
+
brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, emptyState, renderToolResult, footerContent, showInformation = false, onInformationClick, headerContent, children, borderRadius, backgroundColor, border, boxShadow, compact = false, placeholder, description = 'Start a conversation with the AI agent.', onStateUpdate, onNewChat, onClear, onMessagesChange, autoFocus = false, suggestions, submitOnSuggestionClick = true, hideMessagesAfterToolUI = false, focusTrigger, frontendTools: frontendToolsProp, enableEphemeralNotebook = false, initialEphemeralNotebookOpen = true, onEphemeralNotebookOpenChange, enableEphemeralDocument = false, initialEphemeralSurfaceMode, onEphemeralSurfaceModeChange, collapsed = false, onExpandFromCollapsed, ephemeralNotebookToolbar, ephemeralNotebookCollaborationProvider, ephemeralNotebookCollaborationDocumentId, ephemeralDocumentCollaboration, ephemeralRuntimeOverride,
|
|
476
476
|
// Tool invocation hooks
|
|
477
477
|
onToolCallStart, onToolCallComplete,
|
|
478
478
|
// Identity/Authorization props
|
|
@@ -510,9 +510,15 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
|
|
|
510
510
|
// agent id. This keeps the persisted notebook model addressable by the same
|
|
511
511
|
// key when navigating away from and back to the same runtime page.
|
|
512
512
|
const notebookScopeId = runtimeId || protocolConfig?.agentId || activeAgentId;
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
513
|
+
// When an explicit collaboration document id is supplied (e.g. an Agent Node
|
|
514
|
+
// sharing a room with the SaaS UI), it becomes the notebook id directly so
|
|
515
|
+
// BOTH peers join the same collaborative room AND the agent's notebook tools
|
|
516
|
+
// (scoped by this same id) drive the shared notebook. Otherwise fall back to
|
|
517
|
+
// the derived, per-runtime in-memory id.
|
|
518
|
+
const ephemeralNotebookId = ephemeralNotebookCollaborationDocumentId ||
|
|
519
|
+
(notebookScopeId
|
|
520
|
+
? `ephemeral-notebook-${notebookScopeId}`
|
|
521
|
+
: generatedNotebookIdRef.current);
|
|
516
522
|
const persistedEphemeralNbformat = useAgentRuntimeStore(s => s.getEphemeralNotebookModel(ephemeralNotebookId));
|
|
517
523
|
const setEphemeralNotebookModel = useAgentRuntimeStore(s => s.setEphemeralNotebookModel);
|
|
518
524
|
const handleEphemeralNotebookChange = useCallback((model) => {
|
|
@@ -520,10 +526,14 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
|
|
|
520
526
|
}, [ephemeralNotebookId, setEphemeralNotebookModel]);
|
|
521
527
|
// ── Ephemeral document (in-memory Lexical) ──────────────────────────────
|
|
522
528
|
// Scoped to the same stable runtime identity as the notebook so the persisted
|
|
523
|
-
// document survives navigation away from and back to the runtime page.
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
529
|
+
// document survives navigation away from and back to the runtime page. When a
|
|
530
|
+
// collaboration room is supplied it becomes the document id directly so BOTH
|
|
531
|
+
// peers join the same Loro room AND the agent's lexical tools (scoped by this
|
|
532
|
+
// same id) drive the shared document.
|
|
533
|
+
const ephemeralDocumentId = ephemeralDocumentCollaboration?.roomId ||
|
|
534
|
+
(notebookScopeId
|
|
535
|
+
? `ephemeral-document-${notebookScopeId}`
|
|
536
|
+
: `ephemeral-document-${generatedNotebookIdRef.current}`);
|
|
527
537
|
const persistedEphemeralDocument = useAgentRuntimeStore(s => s.getEphemeralDocumentModel(ephemeralDocumentId));
|
|
528
538
|
const setEphemeralDocumentModel = useAgentRuntimeStore(s => s.setEphemeralDocumentModel);
|
|
529
539
|
const handleEphemeralDocumentChange = useCallback((model) => {
|
|
@@ -576,6 +586,18 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
|
|
|
576
586
|
unsubscribe();
|
|
577
587
|
};
|
|
578
588
|
}, [notebookVisible, ephemeralNotebookId]);
|
|
589
|
+
// Track the ephemeral document's live kernel connection the same way, so the
|
|
590
|
+
// header's kernel indicator reflects the real connected kernel while the
|
|
591
|
+
// document surface is active (instead of the "disconnected" placeholder).
|
|
592
|
+
const [documentKernel, setDocumentKernel] = useState(null);
|
|
593
|
+
const handleDocumentKernelChange = useCallback((next) => {
|
|
594
|
+
setDocumentKernel(prev => (prev?.id === next?.id ? prev : next));
|
|
595
|
+
}, []);
|
|
596
|
+
useEffect(() => {
|
|
597
|
+
if (!documentVisible) {
|
|
598
|
+
setDocumentKernel(null);
|
|
599
|
+
}
|
|
600
|
+
}, [documentVisible]);
|
|
579
601
|
// When a companion surface is shown, the chat can be docked as a sidebar
|
|
580
602
|
// (default) or floated over it, driven by the header view-mode toggle.
|
|
581
603
|
const surfaceChatFloating = surfaceVisible &&
|
|
@@ -2827,7 +2849,11 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
|
|
|
2827
2849
|
// no ephemeral notebook is shown) or INSIDE the chat body column (when the
|
|
2828
2850
|
// notebook is visible) so the header always follows the chat body across all
|
|
2829
2851
|
// view modes (docked sidebar, floating popup, floating-small).
|
|
2830
|
-
const chatHeaderElement = showHeader ? (_jsx(ChatBaseHeader, { title: title, subtitle: subtitle, brandIcon: brandIcon, headerContent: headerContent, headerActions: headerActions, showInformation: showInformation, onInformationClick: onInformationClick, padding: padding, kernelIndicatorState: kernelIndicatorState, runtimeStatus: sandboxStatusData ?? sandboxStatusQuery.data, kernel: notebookVisible
|
|
2852
|
+
const chatHeaderElement = showHeader ? (_jsx(ChatBaseHeader, { title: title, subtitle: subtitle, brandIcon: brandIcon, headerContent: headerContent, headerActions: headerActions, showInformation: showInformation, onInformationClick: onInformationClick, padding: padding, kernelIndicatorState: kernelIndicatorState, runtimeStatus: sandboxStatusData ?? sandboxStatusQuery.data, kernel: notebookVisible
|
|
2853
|
+
? (notebookKernel ?? kernel)
|
|
2854
|
+
: documentVisible
|
|
2855
|
+
? (documentKernel ?? kernel)
|
|
2856
|
+
: kernel, kernelEnvironmentName: kernelEnvironmentName, kernelCpu: kernelCpu, kernelMemory: kernelMemory, kernelGpu: kernelGpu, headerButtons: headerButtons, messageCount: messages.length, onNewChat: handleNewChat, onClear: handleClear, chatViewMode: chatViewMode, onChatViewModeChange: onChatViewModeChange, showEphemeralSurfaceControl: enableEphemeralNotebook || enableEphemeralDocument, enableEphemeralNotebookOption: enableEphemeralNotebook, enableEphemeralDocumentOption: enableEphemeralDocument, ephemeralSurfaceMode: ephemeralSurfaceMode, onEphemeralSurfaceModeChange: handleEphemeralSurfaceModeChange })) : null;
|
|
2831
2857
|
// ========================================================================
|
|
2832
2858
|
// Render
|
|
2833
2859
|
// ========================================================================
|
|
@@ -2874,7 +2900,7 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
|
|
|
2874
2900
|
borderRight: '1px solid',
|
|
2875
2901
|
borderColor: 'border.default',
|
|
2876
2902
|
}),
|
|
2877
|
-
}, children: launching || overlay ? (_jsx(CompanionSurfaceSkeleton, { mode: notebookVisible ? 'notebook' : 'document' })) : notebookVisible ? (_jsx(EphemeralNotebook, { notebookId: ephemeralNotebookId, runtimePodName: runtimeId || activeAgentId, nbformat: persistedEphemeralNbformat ?? undefined, onNbformatChange: handleEphemeralNotebookChange, toolbarComponent: ephemeralNotebookToolbar })) : (_jsx(React.Suspense, { fallback: null, children: _jsx(EphemeralDocument, { documentId: ephemeralDocumentId, runtimePodName: runtimeId || activeAgentId, content: persistedEphemeralDocument ?? undefined, onContentChange: handleEphemeralDocumentChange, onToolsReady: handleDocumentToolsReady }) })) }), !surfaceCollapsed && (_jsxs(Box, { sx: surfaceChatFloating
|
|
2903
|
+
}, children: launching || overlay ? (_jsx(CompanionSurfaceSkeleton, { mode: notebookVisible ? 'notebook' : 'document' })) : notebookVisible ? (_jsx(EphemeralNotebook, { notebookId: ephemeralNotebookId, runtimePodName: runtimeId || activeAgentId, runtimeOverride: ephemeralRuntimeOverride, nbformat: persistedEphemeralNbformat ?? undefined, onNbformatChange: handleEphemeralNotebookChange, toolbarComponent: ephemeralNotebookToolbar, collaborationProvider: ephemeralNotebookCollaborationProvider })) : (_jsx(React.Suspense, { fallback: null, children: _jsx(EphemeralDocument, { documentId: ephemeralDocumentId, runtimePodName: runtimeId || activeAgentId, runtimeOverride: ephemeralRuntimeOverride, content: persistedEphemeralDocument ?? undefined, onContentChange: handleEphemeralDocumentChange, onToolsReady: handleDocumentToolsReady, onKernelChange: handleDocumentKernelChange, collaboration: ephemeralDocumentCollaboration }) })) }), !surfaceCollapsed && (_jsxs(Box, { sx: surfaceChatFloating
|
|
2878
2904
|
? {
|
|
2879
2905
|
position: 'absolute',
|
|
2880
2906
|
right: 16,
|
|
@@ -1,6 +1,35 @@
|
|
|
1
|
+
import type { IKernelConnection } from '@jupyterlab/services/lib/kernel/kernel';
|
|
1
2
|
import type { FrontendToolDefinition } from '../../types/tools';
|
|
3
|
+
import type { EphemeralRuntimeOverride } from '../notebook/EphemeralNotebook';
|
|
2
4
|
import '@datalayer/jupyter-react/lib/css/PrismCss';
|
|
3
5
|
import '@datalayer/jupyter-lexical/style/index.css';
|
|
6
|
+
/**
|
|
7
|
+
* Real-time collaboration configuration for the ephemeral document.
|
|
8
|
+
*
|
|
9
|
+
* When supplied, the Lexical editor state is backed by a shared Loro CRDT
|
|
10
|
+
* document synchronised over a WebSocket (the spacer lexical endpoint) instead
|
|
11
|
+
* of the in-memory / persisted local state. Every peer that joins the same
|
|
12
|
+
* `roomId` edits the same document — this is how an Agent Node and the SaaS UI
|
|
13
|
+
* share a live document. The caller owns the room/identity lifecycle.
|
|
14
|
+
*/
|
|
15
|
+
export interface EphemeralDocumentCollaboration {
|
|
16
|
+
/**
|
|
17
|
+
* WebSocket URL of the Loro collaboration server, e.g.
|
|
18
|
+
* `wss://…/api/spacer/v1/lexical/ws`.
|
|
19
|
+
*/
|
|
20
|
+
websocketUrl: string;
|
|
21
|
+
/** Shared collaboration room / document id joined by all peers. */
|
|
22
|
+
roomId: string;
|
|
23
|
+
/** Optional identity used for collaborative presence (cursors / awareness). */
|
|
24
|
+
identity?: {
|
|
25
|
+
userId?: string;
|
|
26
|
+
handle?: string;
|
|
27
|
+
displayName?: string;
|
|
28
|
+
initials?: string;
|
|
29
|
+
avatarUrl?: string;
|
|
30
|
+
color?: string;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
4
33
|
export interface EphemeralDocumentProps {
|
|
5
34
|
/**
|
|
6
35
|
* Document identifier. Must match the id passed to `useLexicalTools` so the
|
|
@@ -9,15 +38,30 @@ export interface EphemeralDocumentProps {
|
|
|
9
38
|
documentId: string;
|
|
10
39
|
/** Preferred runtime pod name to bind the document kernel to. */
|
|
11
40
|
runtimePodName?: string;
|
|
41
|
+
/** Optional explicit runtime endpoint (agent-node tunnel proxy path). */
|
|
42
|
+
runtimeOverride?: EphemeralRuntimeOverride;
|
|
12
43
|
/** Optional persisted Lexical editor state (JSON string) to restore. */
|
|
13
44
|
content?: string;
|
|
14
45
|
/** Callback fired when the document's editor state changes. */
|
|
15
46
|
onContentChange?: (content: string) => void;
|
|
16
47
|
/** Reports the lexical frontend tools so the parent can pass them to the agent. */
|
|
17
48
|
onToolsReady?: (tools: FrontendToolDefinition[]) => void;
|
|
49
|
+
/**
|
|
50
|
+
* Reports the document's live sandbox kernel connection upward so the chat
|
|
51
|
+
* header can render the same rich kernel indicator (kernel id, client id,
|
|
52
|
+
* status) as the notebook surface. Emits `null` while the kernel is absent.
|
|
53
|
+
*/
|
|
54
|
+
onKernelChange?: (kernel: IKernelConnection | null) => void;
|
|
55
|
+
/**
|
|
56
|
+
* Optional real-time collaboration configuration. When supplied the document
|
|
57
|
+
* joins a shared Loro room over WebSocket so its state transits over RTC
|
|
58
|
+
* (e.g. between an Agent Node and the SaaS UI) rather than living purely in
|
|
59
|
+
* local memory.
|
|
60
|
+
*/
|
|
61
|
+
collaboration?: EphemeralDocumentCollaboration;
|
|
18
62
|
}
|
|
19
63
|
/**
|
|
20
64
|
* Renders an in-memory Lexical document backed by a sandbox kernel.
|
|
21
65
|
*/
|
|
22
|
-
export declare function EphemeralDocument({ documentId, runtimePodName, content, onContentChange, onToolsReady, }: EphemeralDocumentProps): import("react/jsx-runtime").JSX.Element;
|
|
66
|
+
export declare function EphemeralDocument({ documentId, runtimePodName, runtimeOverride, content, onContentChange, onToolsReady, onKernelChange, collaboration, }: EphemeralDocumentProps): import("react/jsx-runtime").JSX.Element;
|
|
23
67
|
export default EphemeralDocument;
|
|
@@ -37,9 +37,11 @@ import { ListPlugin } from '@lexical/react/LexicalListPlugin';
|
|
|
37
37
|
import { CheckListPlugin } from '@lexical/react/LexicalCheckListPlugin';
|
|
38
38
|
import { LinkPlugin } from '@lexical/react/LexicalLinkPlugin';
|
|
39
39
|
import { ServerConnection, ServiceManager } from '@jupyterlab/services';
|
|
40
|
-
import {
|
|
40
|
+
import { DatalayerThemeProvider, getThemeConfig, useSystemColorMode, useThemeStore, } from '@datalayer/primer-addons';
|
|
41
|
+
import { Box, Spinner, Text } from '@primer/react';
|
|
41
42
|
import { JupyterReactTheme, Kernel, disposeServiceManager, } from '@datalayer/jupyter-react';
|
|
42
43
|
import { ComponentPickerMenuPlugin, JupyterCellPlugin, JupyterInputOutputPlugin, DraggableBlockPlugin, ImagesPlugin, ExcalidrawPlugin, TablePlugin, TableCellResizerPlugin, TableActionMenuPlugin, TableHoverActionsV2Plugin, CollapsiblePlugin, HorizontalRulePlugin, EquationsPlugin, YouTubePlugin, AutoLinkPlugin, AutoEmbedPlugin, LexicalConfigProvider, LexicalStatePlugin, FloatingTextFormatToolbarPlugin, CodeActionMenuPlugin, ListMaxIndentLevelPlugin, ToolbarPlugin, ToolbarContext, CommentsProvider, commentTheme, } from '@datalayer/jupyter-lexical';
|
|
44
|
+
import { createWebsocketProvider, LoroCollaborationPlugin, } from '@datalayer/lexical-loro';
|
|
43
45
|
import { editorConfig } from '../../examples/lexical/editorConfig';
|
|
44
46
|
import { useLexicalTools } from '../../tools/adapters/agent-runtimes/lexicalHooks';
|
|
45
47
|
import { useAgentsRuntimes } from '../../hooks/useAgentRuntimes';
|
|
@@ -106,7 +108,32 @@ function DocumentToolbar({ setIsLinkEditMode, }) {
|
|
|
106
108
|
/**
|
|
107
109
|
* Renders an in-memory Lexical document backed by a sandbox kernel.
|
|
108
110
|
*/
|
|
109
|
-
export function EphemeralDocument({ documentId, runtimePodName, content, onContentChange, onToolsReady, }) {
|
|
111
|
+
export function EphemeralDocument({ documentId, runtimePodName, runtimeOverride, content, onContentChange, onToolsReady, onKernelChange, collaboration, }) {
|
|
112
|
+
// Real-time collaboration is active only when both a WebSocket endpoint and a
|
|
113
|
+
// room id are supplied. In that mode the shared Loro CRDT is the single source
|
|
114
|
+
// of truth: local content-seeding and undo history are disabled so the local
|
|
115
|
+
// editor never diverges from (or fights) the collaborative document.
|
|
116
|
+
const isCollaborative = Boolean(collaboration?.websocketUrl && collaboration?.roomId);
|
|
117
|
+
const collaborationName = collaboration?.identity?.displayName ||
|
|
118
|
+
collaboration?.identity?.handle ||
|
|
119
|
+
collaboration?.identity?.userId;
|
|
120
|
+
const collaborationColor = collaboration?.identity?.color;
|
|
121
|
+
const collaborationAwarenessData = useMemo(() => ({
|
|
122
|
+
user: {
|
|
123
|
+
id: collaboration?.identity?.userId,
|
|
124
|
+
username: collaboration?.identity?.userId ||
|
|
125
|
+
collaboration?.identity?.handle ||
|
|
126
|
+
collaboration?.identity?.displayName,
|
|
127
|
+
name: collaboration?.identity?.handle ||
|
|
128
|
+
collaboration?.identity?.displayName,
|
|
129
|
+
display_name: collaboration?.identity?.displayName ||
|
|
130
|
+
collaboration?.identity?.handle,
|
|
131
|
+
initials: collaboration?.identity?.initials,
|
|
132
|
+
color: collaboration?.identity?.color,
|
|
133
|
+
avatar_url: collaboration?.identity?.avatarUrl,
|
|
134
|
+
handle: collaboration?.identity?.handle,
|
|
135
|
+
},
|
|
136
|
+
}), [collaboration?.identity]);
|
|
110
137
|
// Capture the restore seed ONCE per documentId so parent re-renders don't
|
|
111
138
|
// remount the composer (which reads `initialConfig` a single time).
|
|
112
139
|
const initialContentRef = useRef(content);
|
|
@@ -127,14 +154,38 @@ export function EphemeralDocument({ documentId, runtimePodName, content, onConte
|
|
|
127
154
|
// Resolve the runtime sandbox strictly by its assigned pod name. There is
|
|
128
155
|
// deliberately NO local fallback kernel: the ephemeral document executes on
|
|
129
156
|
// exactly the runtime assigned to this agent, or shows a waiting state.
|
|
130
|
-
const { runtimes } = useAgentsRuntimes();
|
|
157
|
+
const { runtimes, refetchRuntimes } = useAgentsRuntimes();
|
|
131
158
|
const selectedRuntime = useMemo(() => {
|
|
159
|
+
const overrideBaseUrl = String(runtimeOverride?.baseUrl || '').trim();
|
|
160
|
+
if (overrideBaseUrl) {
|
|
161
|
+
return {
|
|
162
|
+
url: overrideBaseUrl,
|
|
163
|
+
wsUrl: String(runtimeOverride?.wsUrl || '').trim() || undefined,
|
|
164
|
+
token: String(runtimeOverride?.token || '').trim(),
|
|
165
|
+
pod_name: String(runtimeOverride?.podName || '').trim() || 'agent-node-proxy',
|
|
166
|
+
};
|
|
167
|
+
}
|
|
132
168
|
const preferredPod = String(runtimePodName || '').trim();
|
|
133
169
|
if (!preferredPod) {
|
|
134
170
|
return undefined;
|
|
135
171
|
}
|
|
136
172
|
return runtimes.find(rt => String(rt?.pod_name || '') === preferredPod);
|
|
137
|
-
}, [runtimePodName, runtimes]);
|
|
173
|
+
}, [runtimeOverride, runtimePodName, runtimes]);
|
|
174
|
+
const needsRuntimeLookup = Boolean(!runtimeOverride?.baseUrl &&
|
|
175
|
+
String(runtimePodName || '').trim() &&
|
|
176
|
+
!selectedRuntime);
|
|
177
|
+
useEffect(() => {
|
|
178
|
+
if (!needsRuntimeLookup) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
void refetchRuntimes();
|
|
182
|
+
const intervalId = window.setInterval(() => {
|
|
183
|
+
void refetchRuntimes();
|
|
184
|
+
}, 1000);
|
|
185
|
+
return () => {
|
|
186
|
+
window.clearInterval(intervalId);
|
|
187
|
+
};
|
|
188
|
+
}, [needsRuntimeLookup, refetchRuntimes]);
|
|
138
189
|
const [runtimeServiceManager, setRuntimeServiceManager] = useState(null);
|
|
139
190
|
// Kernel started inside the agent runtime sandbox and threaded into the
|
|
140
191
|
// Jupyter cell plugins, mirroring how the document editor wires its kernel.
|
|
@@ -154,9 +205,11 @@ export function EphemeralDocument({ documentId, runtimePodName, content, onConte
|
|
|
154
205
|
}
|
|
155
206
|
try {
|
|
156
207
|
const token = String(selectedRuntime?.token || '').trim();
|
|
208
|
+
const wsUrl = String(selectedRuntime?.wsUrl || '').trim() ||
|
|
209
|
+
baseUrl.replace(/^http/, 'ws');
|
|
157
210
|
const serverSettings = ServerConnection.makeSettings({
|
|
158
211
|
baseUrl,
|
|
159
|
-
wsUrl
|
|
212
|
+
wsUrl,
|
|
160
213
|
token,
|
|
161
214
|
appendToken: true,
|
|
162
215
|
});
|
|
@@ -195,8 +248,19 @@ export function EphemeralDocument({ documentId, runtimePodName, content, onConte
|
|
|
195
248
|
}
|
|
196
249
|
};
|
|
197
250
|
}, [selectedRuntime?.pod_name, selectedRuntime?.url, selectedRuntime?.token]);
|
|
251
|
+
// Surface the document's live kernel connection to the parent so the chat
|
|
252
|
+
// header's kernel indicator reflects the real connected kernel instead of
|
|
253
|
+
// the "disconnected"/"no-kernel" placeholder while the document is active.
|
|
254
|
+
useEffect(() => {
|
|
255
|
+
onKernelChange?.(documentKernel?.connection ?? null);
|
|
256
|
+
return () => {
|
|
257
|
+
onKernelChange?.(null);
|
|
258
|
+
};
|
|
259
|
+
}, [documentKernel, onKernelChange]);
|
|
198
260
|
const activeServiceManager = runtimeServiceManager;
|
|
199
|
-
const isRuntimeStarting = Boolean(String(runtimePodName || '').trim()
|
|
261
|
+
const isRuntimeStarting = Boolean((String(runtimePodName || '').trim() ||
|
|
262
|
+
String(runtimeOverride?.baseUrl || '').trim()) &&
|
|
263
|
+
!activeServiceManager);
|
|
200
264
|
useProgressTask(`ephemeral-document-start-${documentId}`, isRuntimeStarting);
|
|
201
265
|
// Build the initial config once per documentId, without the demo content so
|
|
202
266
|
// the document starts empty and is either restored or filled by the agent.
|
|
@@ -252,19 +316,79 @@ export function EphemeralDocument({ documentId, runtimePodName, content, onConte
|
|
|
252
316
|
inset: 0,
|
|
253
317
|
display: 'flex',
|
|
254
318
|
flexDirection: 'column',
|
|
255
|
-
|
|
319
|
+
minHeight: 0,
|
|
320
|
+
overflow: 'hidden',
|
|
256
321
|
overscrollBehaviorY: 'contain',
|
|
257
322
|
padding: 2,
|
|
258
323
|
'& .editor-shell': {
|
|
259
324
|
backgroundColor: themeBackground,
|
|
260
325
|
borderRadius: 2,
|
|
326
|
+
display: 'flex',
|
|
327
|
+
flexDirection: 'column',
|
|
328
|
+
minHeight: 0,
|
|
329
|
+
flex: 1,
|
|
330
|
+
// The shared lexical stylesheet caps `.editor-shell` at
|
|
331
|
+
// `max-width: 1100px; margin: 20px auto`, which leaves the
|
|
332
|
+
// ephemeral document narrow and centered. Take the full
|
|
333
|
+
// wrapping box width instead (matching the ui LiterateEditor,
|
|
334
|
+
// whose root is `.editor-container` with no such cap).
|
|
335
|
+
maxWidth: 'none',
|
|
336
|
+
width: '100%',
|
|
337
|
+
margin: 0,
|
|
338
|
+
},
|
|
339
|
+
'& .editor-container': {
|
|
340
|
+
backgroundColor: themeBackground,
|
|
341
|
+
display: 'flex',
|
|
342
|
+
flexDirection: 'column',
|
|
343
|
+
minHeight: 0,
|
|
344
|
+
flex: 1,
|
|
345
|
+
width: '100%',
|
|
346
|
+
maxWidth: 'none',
|
|
347
|
+
margin: 0,
|
|
348
|
+
},
|
|
349
|
+
'& .editor-inner': {
|
|
350
|
+
backgroundColor: themeBackground,
|
|
351
|
+
display: 'flex',
|
|
352
|
+
flexDirection: 'column',
|
|
353
|
+
minHeight: 0,
|
|
354
|
+
flex: 1,
|
|
355
|
+
width: '100%',
|
|
356
|
+
maxWidth: 'none',
|
|
357
|
+
},
|
|
358
|
+
'& .editor-scroller': {
|
|
359
|
+
minHeight: 0,
|
|
360
|
+
flex: 1,
|
|
361
|
+
overflow: 'auto',
|
|
362
|
+
width: '100%',
|
|
363
|
+
maxWidth: 'none',
|
|
364
|
+
},
|
|
365
|
+
'& .editor': {
|
|
366
|
+
minHeight: '100%',
|
|
367
|
+
width: '100%',
|
|
368
|
+
maxWidth: 'none',
|
|
369
|
+
margin: 0,
|
|
370
|
+
},
|
|
371
|
+
'& .editor-input': {
|
|
372
|
+
backgroundColor: themeBackground,
|
|
373
|
+
width: '100%',
|
|
374
|
+
maxWidth: 'none',
|
|
375
|
+
margin: 0,
|
|
376
|
+
},
|
|
377
|
+
'& .ContentEditable__root': {
|
|
378
|
+
width: '100%',
|
|
379
|
+
maxWidth: 'none',
|
|
380
|
+
margin: 0,
|
|
261
381
|
},
|
|
262
|
-
'& .editor-container': { backgroundColor: themeBackground },
|
|
263
|
-
'& .editor-inner': { backgroundColor: themeBackground },
|
|
264
|
-
'& .editor-input': { backgroundColor: themeBackground },
|
|
265
382
|
'& [role="toolbar"][aria-label="Editor toolbar"]': {
|
|
266
383
|
backgroundColor: themeBackground,
|
|
267
384
|
},
|
|
268
|
-
}, children: _jsx(LexicalConfigProvider, { lexicalId: documentId, serviceManager: activeServiceManager, children: _jsx(LexicalComposer, { initialConfig: initialConfig, children: _jsx(CommentsProvider, { children: _jsx(ToolbarContext, { children: _jsxs("div", { className: "editor-shell", children: [_jsx(DocumentToolbar, { setIsLinkEditMode: setIsLinkEditMode }), _jsx("div", { className: "editor-container", children: _jsxs("div", { className: "editor-inner", children: [_jsx(LexicalStatePlugin, {}), _jsx(RichTextPlugin, { contentEditable: _jsx("div", { className: "editor-scroller", children: _jsx("div", { className: "editor", ref: onAnchorRef, children: _jsx(ContentEditable, { className: "editor-input", "aria-label": "Ephemeral document editor" }) }) }), ErrorBoundary: LexicalErrorBoundary }), _jsx(OnChangePlugin, { onChange: handleChange }), _jsx(HistoryPlugin, {}), _jsx(AutoFocusPlugin, {}), _jsx(ListPlugin, {}), _jsx(CheckListPlugin, {}), _jsx(LinkPlugin, {}), _jsx(AutoLinkPlugin, {}), _jsx(ListMaxIndentLevelPlugin, { maxDepth: 7 }), _jsx(MarkdownShortcutPlugin, { transformers: TRANSFORMERS }), _jsx(LoadContentPlugin, { content: initialContent }), _jsx(CodeHighlightPlugin, {}), _jsx(ImagesPlugin, { captionsEnabled: false }), _jsx(ExcalidrawPlugin, {}), _jsx(TablePlugin, {}), _jsx(TableCellResizerPlugin, {}), _jsx(TableActionMenuPlugin, {}), _jsx(TableHoverActionsV2Plugin, {}), _jsx(CollapsiblePlugin, {}), _jsx(HorizontalRulePlugin, {}), _jsx(EquationsPlugin, {}), _jsx(YouTubePlugin, {}), _jsx(AutoEmbedPlugin, {}), _jsx(JupyterCellPlugin, {}), _jsx(ComponentPickerMenuPlugin, { kernel: documentKernel }), _jsx(JupyterInputOutputPlugin, { kernel: documentKernel }), floatingAnchorElem && (_jsxs(_Fragment, { children: [_jsx(DraggableBlockPlugin, { anchorElem: floatingAnchorElem }), _jsx(FloatingTextFormatToolbarPlugin, { anchorElem: floatingAnchorElem, setIsLinkEditMode: setIsLinkEditMode }), _jsx(CodeActionMenuPlugin, { anchorElem: floatingAnchorElem })] }))] }) })] }) }) }) }) }) }) }) })) :
|
|
385
|
+
}, children: _jsx(LexicalConfigProvider, { lexicalId: documentId, serviceManager: activeServiceManager, children: _jsx(LexicalComposer, { initialConfig: initialConfig, children: _jsx(CommentsProvider, { children: _jsx(ToolbarContext, { children: _jsxs("div", { className: "editor-shell", children: [_jsx(DocumentToolbar, { setIsLinkEditMode: setIsLinkEditMode }), _jsx("div", { className: "editor-container", children: _jsxs("div", { className: "editor-inner", children: [isCollaborative && collaboration ? (_jsx(LoroCollaborationPlugin, { id: collaboration.roomId, providerFactory: createWebsocketProvider, websocketUrl: collaboration.websocketUrl, shouldBootstrap: true, showCollaborators: true, username: collaborationName, cursorColor: collaborationColor, awarenessData: collaborationAwarenessData })) : null, _jsx(LexicalStatePlugin, {}), _jsx(RichTextPlugin, { contentEditable: _jsx("div", { className: "editor-scroller", children: _jsx("div", { className: "editor", ref: onAnchorRef, children: _jsx(ContentEditable, { className: "editor-input", "aria-label": "Ephemeral document editor" }) }) }), ErrorBoundary: LexicalErrorBoundary }), !isCollaborative && (_jsx(OnChangePlugin, { onChange: handleChange })), !isCollaborative && _jsx(HistoryPlugin, {}), _jsx(AutoFocusPlugin, {}), _jsx(ListPlugin, {}), _jsx(CheckListPlugin, {}), _jsx(LinkPlugin, {}), _jsx(AutoLinkPlugin, {}), _jsx(ListMaxIndentLevelPlugin, { maxDepth: 7 }), _jsx(MarkdownShortcutPlugin, { transformers: TRANSFORMERS }), !isCollaborative && (_jsx(LoadContentPlugin, { content: initialContent })), _jsx(CodeHighlightPlugin, {}), _jsx(ImagesPlugin, { captionsEnabled: false }), _jsx(ExcalidrawPlugin, {}), _jsx(TablePlugin, {}), _jsx(TableCellResizerPlugin, {}), _jsx(TableActionMenuPlugin, {}), _jsx(TableHoverActionsV2Plugin, {}), _jsx(CollapsiblePlugin, {}), _jsx(HorizontalRulePlugin, {}), _jsx(EquationsPlugin, {}), _jsx(YouTubePlugin, {}), _jsx(AutoEmbedPlugin, {}), _jsx(JupyterCellPlugin, {}), _jsx(ComponentPickerMenuPlugin, { kernel: documentKernel }), _jsx(JupyterInputOutputPlugin, { kernel: documentKernel }), floatingAnchorElem && (_jsxs(_Fragment, { children: [_jsx(DraggableBlockPlugin, { anchorElem: floatingAnchorElem }), _jsx(FloatingTextFormatToolbarPlugin, { anchorElem: floatingAnchorElem, setIsLinkEditMode: setIsLinkEditMode }), _jsx(CodeActionMenuPlugin, { anchorElem: floatingAnchorElem })] }))] }) })] }) }) }) }) }) }) }) })) : isRuntimeStarting ? (_jsxs(Box, { sx: {
|
|
386
|
+
height: '100%',
|
|
387
|
+
display: 'flex',
|
|
388
|
+
alignItems: 'center',
|
|
389
|
+
justifyContent: 'center',
|
|
390
|
+
gap: 2,
|
|
391
|
+
color: 'fg.muted',
|
|
392
|
+
}, children: [_jsx(Spinner, { size: "small" }), _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "Starting document..." })] })) : null }));
|
|
269
393
|
}
|
|
270
394
|
export default EphemeralDocument;
|
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
import type { INotebookContent } from '@jupyterlab/nbformat';
|
|
2
|
+
import type { ICollaborationProvider } from '@datalayer/jupyter-react';
|
|
2
3
|
import type { EphemeralNotebookToolbarComponent } from '../../types/chat';
|
|
4
|
+
/**
|
|
5
|
+
* Explicit runtime endpoint used to bind an ephemeral notebook kernel without
|
|
6
|
+
* resolving a pod from the user's runtimes list. Used to reach an agent node's
|
|
7
|
+
* Jupyter server through the runtimes tunnel HTTP/WebSocket proxy.
|
|
8
|
+
*/
|
|
9
|
+
export interface EphemeralRuntimeOverride {
|
|
10
|
+
/** REST base URL, e.g. `https://.../agent-nodes/{nodeId}/jupyter`. */
|
|
11
|
+
baseUrl: string;
|
|
12
|
+
/**
|
|
13
|
+
* WebSocket base URL. Defaults to `baseUrl` with the http(s) scheme swapped
|
|
14
|
+
* for ws(s). For the tunnel proxy this is the `/ws` sibling of `baseUrl`.
|
|
15
|
+
*/
|
|
16
|
+
wsUrl?: string;
|
|
17
|
+
/** Bearer/JWT token appended to requests (`appendToken`). */
|
|
18
|
+
token?: string;
|
|
19
|
+
/** Stable identifier used for the sandbox service-manager registry. */
|
|
20
|
+
podName?: string;
|
|
21
|
+
}
|
|
3
22
|
export interface EphemeralNotebookProps {
|
|
4
23
|
/**
|
|
5
24
|
* Notebook identifier. Must match the id passed to `useNotebookTools` so the
|
|
@@ -8,6 +27,14 @@ export interface EphemeralNotebookProps {
|
|
|
8
27
|
notebookId: string;
|
|
9
28
|
/** Preferred runtime pod name to bind the notebook kernel to. */
|
|
10
29
|
runtimePodName?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Explicit runtime endpoint override. When supplied, the notebook binds its
|
|
32
|
+
* kernel to this endpoint directly instead of resolving a pod from the user's
|
|
33
|
+
* runtimes list. This is how a SaaS browser reaches an agent node's Jupyter
|
|
34
|
+
* server through the runtimes tunnel proxy: `baseUrl` points at
|
|
35
|
+
* `.../agent-nodes/{nodeId}/jupyter` and `wsUrl` at its `/ws` sibling.
|
|
36
|
+
*/
|
|
37
|
+
runtimeOverride?: EphemeralRuntimeOverride;
|
|
11
38
|
/** Left margin reserved for the cell sidebar (default 120). */
|
|
12
39
|
cellSidebarMargin?: number;
|
|
13
40
|
/** Optional persisted notebook model to restore when mounting. */
|
|
@@ -16,9 +43,17 @@ export interface EphemeralNotebookProps {
|
|
|
16
43
|
onNbformatChange?: (content: INotebookContent) => void;
|
|
17
44
|
/** Optional toolbar component override. */
|
|
18
45
|
toolbarComponent?: EphemeralNotebookToolbarComponent;
|
|
46
|
+
/**
|
|
47
|
+
* Optional real-time collaboration provider. When supplied, the notebook
|
|
48
|
+
* joins a shared collaborative room (the ydoc becomes the source of truth)
|
|
49
|
+
* and the local in-memory persistence poll is disabled. This is how a node's
|
|
50
|
+
* notebook state transits to the SaaS UI (and back) over RTC instead of the
|
|
51
|
+
* tunnel.
|
|
52
|
+
*/
|
|
53
|
+
collaborationProvider?: ICollaborationProvider;
|
|
19
54
|
}
|
|
20
55
|
/**
|
|
21
56
|
* Renders an in-memory notebook backed by a sandbox kernel.
|
|
22
57
|
*/
|
|
23
|
-
export declare function EphemeralNotebook({ notebookId, runtimePodName, cellSidebarMargin, nbformat, onNbformatChange, toolbarComponent, }: EphemeralNotebookProps): import("react/jsx-runtime").JSX.Element;
|
|
58
|
+
export declare function EphemeralNotebook({ notebookId, runtimePodName, runtimeOverride, cellSidebarMargin, nbformat, onNbformatChange, toolbarComponent, collaborationProvider, }: EphemeralNotebookProps): import("react/jsx-runtime").JSX.Element;
|
|
24
59
|
export default EphemeralNotebook;
|