@datalayer/agent-runtimes 1.0.19 → 1.0.21
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 +11 -3
- package/lib/AgentNode.js +141 -40
- package/lib/App.js +2 -2
- package/lib/agent-node/AgentNodeGallery.js +188 -25
- package/lib/api/runtimes/runtimes.d.ts +57 -0
- package/lib/api/runtimes/runtimes.js +55 -1
- package/lib/chat/Chat.d.ts +1 -1
- package/lib/chat/Chat.js +2 -2
- package/lib/chat/base/ChatBase.js +50 -13
- package/lib/chat/document/EphemeralDocument.d.ts +5 -1
- package/lib/chat/document/EphemeralDocument.js +7 -5
- package/lib/chat/header/ChatHeaderBase.d.ts +3 -1
- package/lib/chat/header/ChatHeaderBase.js +5 -3
- package/lib/chat/messages/ChatMessageList.d.ts +19 -0
- package/lib/chat/messages/ChatMessageList.js +180 -9
- package/lib/chat/notebook/EphemeralNotebook.d.ts +5 -1
- package/lib/chat/notebook/EphemeralNotebook.js +9 -6
- package/lib/chat/prompt/InputPrompt.js +1 -1
- package/lib/chat/tools/SubagentActivity.d.ts +20 -0
- package/lib/chat/tools/SubagentActivity.js +154 -0
- package/lib/chat/tools/index.d.ts +1 -0
- package/lib/chat/tools/index.js +1 -0
- package/lib/chat/usage/TokenUsageBar.js +3 -3
- package/lib/client/AgentRuntimesClient.d.ts +5 -0
- package/lib/client/AgentsMixin.js +3 -1
- package/lib/client/mixins/RuntimesMixin.d.ts +12 -0
- package/lib/client/mixins/RuntimesMixin.js +24 -1
- package/lib/components/AgentSummary.d.ts +1 -0
- package/lib/components/AgentSummary.js +66 -10
- package/lib/components/runtimes/RuntimePickerBase.js +64 -29
- package/lib/components/runtimes/RuntimePickerCell.js +1 -1
- package/lib/components/runtimes/RuntimePickerNotebook.js +1 -1
- package/lib/components/runtimes/RuntimeSimplePicker.js +3 -2
- package/lib/components/runtimes/RuntimeUtils.js +2 -2
- package/lib/context/GraphFlowChart.js +1 -1
- package/lib/context/TokenUsageChart.js +1 -1
- package/lib/context/TurnGraphChart.js +2 -2
- package/lib/examples/A2UiAgentExample.js +4 -4
- package/lib/examples/A2UiComponentsGalleryExample.js +3 -3
- package/lib/examples/A2UiContactCardExample.js +3 -3
- package/lib/examples/A2UiRestaurantExample.js +2 -2
- package/lib/examples/A2UiViewerExample.js +3 -3
- package/lib/examples/AgUiAgenticExample.js +3 -3
- package/lib/examples/AgUiBackendToolRenderingExample.js +2 -2
- package/lib/examples/AgUiHaikuGenUiExample.js +3 -3
- package/lib/examples/AgUiHumanInTheLoopExample.js +5 -5
- package/lib/examples/AgUiSharedStateExample.js +3 -3
- package/lib/examples/AgUiToolsBasedGenUiExample.js +7 -5
- package/lib/examples/AgentCheckpointsExample.js +6 -5
- package/lib/examples/{AgentSandboxExample.d.ts → AgentCodeSandboxExample.d.ts} +4 -4
- package/lib/examples/{AgentSandboxExample.js → AgentCodeSandboxExample.js} +10 -9
- package/lib/examples/AgentCodeSandboxesExample.js +2 -2
- package/lib/examples/AgentCodemodeExample.js +3 -3
- package/lib/examples/AgentCompactionExample.d.ts +12 -0
- package/lib/examples/AgentCompactionExample.js +244 -0
- package/lib/examples/AgentEvalsExample.js +10 -20
- package/lib/examples/AgentGuardrailsExample.js +2 -2
- package/lib/examples/AgentHooksExample.js +1 -1
- package/lib/examples/AgentInferenceProviderExample.js +6 -8
- package/lib/examples/AgentLoopExample.js +3 -3
- package/lib/examples/AgentMCPExample.js +3 -3
- package/lib/examples/AgentMemoryExample.d.ts +4 -2
- package/lib/examples/AgentMemoryExample.js +93 -10
- package/lib/examples/AgentMonitoringExample.js +2 -2
- package/lib/examples/AgentNotificationsExample.js +1 -1
- package/lib/examples/AgentOtelExample.d.ts +2 -3
- package/lib/examples/AgentOtelExample.js +7 -12
- package/lib/examples/AgentOutputsExample.js +5 -5
- package/lib/examples/AgentParametersExample.js +3 -3
- package/lib/examples/AgentSkillsExample.js +4 -4
- package/lib/examples/AgentSubagentsExample.d.ts +2 -2
- package/lib/examples/AgentSubagentsExample.js +43 -34
- package/lib/examples/AgentToolApprovalsExample.js +1 -1
- package/lib/examples/AgentTriggersExample.js +9 -11
- package/lib/examples/AgentspecsExample.js +13 -5
- package/lib/examples/CellExample.d.ts +6 -0
- package/lib/examples/CellExample.js +84 -0
- package/lib/examples/ChatCustomExample.js +1 -1
- package/lib/examples/ChatExample.js +1 -1
- package/lib/examples/ChatStandaloneExample.js +2 -2
- package/lib/examples/HomeExample.js +29 -28
- package/lib/examples/Lexical2Example.js +2 -2
- package/lib/examples/LexicalAgentExample.js +1 -1
- package/lib/examples/LexicalAgentSidebarExample.js +2 -2
- package/lib/examples/NotebookAgentSidebarExample.d.ts +1 -1
- package/lib/examples/NotebookAgentSidebarExample.js +5 -4
- package/lib/examples/NotebookCollaborationExample.js +3 -3
- package/lib/examples/NotebookExample.js +2 -2
- package/lib/examples/components/ExampleWrapper.js +5 -0
- package/lib/examples/components/Header.js +2 -2
- package/lib/examples/components/HeaderControls.js +1 -1
- package/lib/examples/components/LexicalEditor.js +1 -1
- package/lib/examples/components/MainContent.js +1 -1
- package/lib/examples/components/MockFileBrowser.js +8 -8
- package/lib/examples/components/TimeTravel.js +1 -1
- package/lib/examples/components/haiku/HaikuDisplay.js +2 -2
- package/lib/examples/example-selector.js +4 -3
- package/lib/examples/hooks/useExampleAgentRuntime.js +11 -1
- package/lib/examples/hooks/useExampleAgentRuntimes.d.ts +7 -0
- package/lib/examples/hooks/useExampleAgentRuntimes.js +82 -0
- package/lib/examples/index.d.ts +2 -1
- package/lib/examples/index.js +2 -1
- package/lib/examples/main.js +359 -46
- package/lib/examples/utils/agentSummaryStore.d.ts +17 -0
- package/lib/examples/utils/themedProvider.js +32 -2
- package/lib/examples/utils/useExampleAgentRuntimesUrl.js +2 -1
- package/lib/examples2/NotebookMutationsKernel.js +6 -3
- package/lib/examples2/NotebookMutationsServiceManager.js +6 -3
- package/lib/examples2/ReactRouterExample.js +5 -5
- package/lib/examples2/main.js +38 -0
- package/lib/hooks/useAIAgentsWebSocket.js +35 -0
- package/lib/hooks/useAgentRuntimes.d.ts +22 -0
- package/lib/hooks/useAgentRuntimes.js +192 -5
- package/lib/hooks/useCheckpoints.js +2 -2
- package/lib/hooks/useToolApprovals.d.ts +10 -4
- package/lib/hooks/useToolApprovals.js +11 -5
- package/lib/models/Runtime.d.ts +10 -8
- package/lib/models/RuntimeDTO.d.ts +5 -3
- package/lib/models/RuntimeDTO.js +3 -3
- package/lib/runtimes/actions.js +3 -1
- package/lib/runtimes/apis.d.ts +7 -0
- package/lib/specs/agents/agents.d.ts +51 -0
- package/lib/specs/agents/agents.js +3389 -94
- package/lib/specs/models.d.ts +2 -0
- package/lib/specs/models.js +37 -0
- package/lib/stores/agentRuntimeStore.d.ts +32 -1
- package/lib/stores/agentRuntimeStore.js +65 -1
- package/lib/stores/index.d.ts +1 -1
- package/lib/stores/index.js +1 -1
- package/lib/types/agents.d.ts +9 -3
- package/lib/types/agentspecs.d.ts +2 -0
- package/lib/types/chat.d.ts +12 -0
- package/lib/types/models.d.ts +5 -0
- package/lib/types/stream.d.ts +58 -1
- package/package.json +4 -4
- package/scripts/codegen/__pycache__/versioning.cpython-313.pyc +0 -0
- package/scripts/codegen/generate_agents.py +7 -0
- package/scripts/codegen/generate_models.py +22 -9
- package/scripts/gen_workers.py +340 -0
- package/lib/examples/CellSimpleExample.d.ts +0 -6
- package/lib/examples/CellSimpleExample.js +0 -20
|
@@ -40,6 +40,10 @@ export interface EphemeralDocumentProps {
|
|
|
40
40
|
runtimePodName?: string;
|
|
41
41
|
/** Optional explicit runtime endpoint (agent-node tunnel proxy path). */
|
|
42
42
|
runtimeOverride?: EphemeralRuntimeOverride;
|
|
43
|
+
/** Optional theme variant override from host chat context. */
|
|
44
|
+
themeVariant?: string;
|
|
45
|
+
/** Optional color mode override from host chat context. */
|
|
46
|
+
colorMode?: 'light' | 'dark' | 'auto';
|
|
43
47
|
/** Optional persisted Lexical editor state (JSON string) to restore. */
|
|
44
48
|
content?: string;
|
|
45
49
|
/** Callback fired when the document's editor state changes. */
|
|
@@ -63,5 +67,5 @@ export interface EphemeralDocumentProps {
|
|
|
63
67
|
/**
|
|
64
68
|
* Renders an in-memory Lexical document backed by a sandbox kernel.
|
|
65
69
|
*/
|
|
66
|
-
export declare function EphemeralDocument({ documentId, runtimePodName, runtimeOverride, content, onContentChange, onToolsReady, onKernelChange, collaboration, }: EphemeralDocumentProps): import("react/jsx-runtime").JSX.Element;
|
|
70
|
+
export declare function EphemeralDocument({ documentId, runtimePodName, runtimeOverride, themeVariant, colorMode, content, onContentChange, onToolsReady, onKernelChange, collaboration, }: EphemeralDocumentProps): import("react/jsx-runtime").JSX.Element;
|
|
67
71
|
export default EphemeralDocument;
|
|
@@ -108,7 +108,7 @@ function DocumentToolbar({ setIsLinkEditMode, }) {
|
|
|
108
108
|
/**
|
|
109
109
|
* Renders an in-memory Lexical document backed by a sandbox kernel.
|
|
110
110
|
*/
|
|
111
|
-
export function EphemeralDocument({ documentId, runtimePodName, runtimeOverride, content, onContentChange, onToolsReady, onKernelChange, collaboration, }) {
|
|
111
|
+
export function EphemeralDocument({ documentId, runtimePodName, runtimeOverride, themeVariant, colorMode, content, onContentChange, onToolsReady, onKernelChange, collaboration, }) {
|
|
112
112
|
// Real-time collaboration is active only when both a WebSocket endpoint and a
|
|
113
113
|
// room id are supplied. In that mode the shared Loro CRDT is the single source
|
|
114
114
|
// of truth: local content-seeding and undo history are disabled so the local
|
|
@@ -296,10 +296,12 @@ export function EphemeralDocument({ documentId, runtimePodName, runtimeOverride,
|
|
|
296
296
|
// Resolve the active theme/color-mode exactly like the notebook editor so
|
|
297
297
|
// the document honours dark / branded themes instead of always rendering
|
|
298
298
|
// light.
|
|
299
|
-
const { colorMode, theme:
|
|
299
|
+
const { colorMode: storeColorMode, theme: storeThemeVariant } = useThemeStore();
|
|
300
|
+
const effectiveColorMode = colorMode ?? storeColorMode;
|
|
301
|
+
const effectiveThemeVariant = themeVariant ?? storeThemeVariant;
|
|
300
302
|
const systemMode = useSystemColorMode();
|
|
301
|
-
const themeConfig = getThemeConfig(
|
|
302
|
-
const resolvedMode =
|
|
303
|
+
const themeConfig = getThemeConfig(effectiveThemeVariant);
|
|
304
|
+
const resolvedMode = effectiveColorMode === 'auto' ? systemMode : effectiveColorMode;
|
|
303
305
|
const modeStyles = resolvedMode === 'dark'
|
|
304
306
|
? themeConfig.themeStyles.dark
|
|
305
307
|
: themeConfig.themeStyles.light;
|
|
@@ -311,7 +313,7 @@ export function EphemeralDocument({ documentId, runtimePodName, runtimeOverride,
|
|
|
311
313
|
position: 'relative',
|
|
312
314
|
overflow: 'hidden',
|
|
313
315
|
bg: 'canvas.default',
|
|
314
|
-
}, children: activeServiceManager ? (_jsx(DatalayerThemeProvider, { colorMode:
|
|
316
|
+
}, children: activeServiceManager ? (_jsx(DatalayerThemeProvider, { colorMode: effectiveColorMode, theme: themeConfig.primerTheme, themeStyles: themeConfig.themeStyles, children: _jsx(JupyterReactTheme, { colormode: resolvedMode, backgroundColor: themeBackground, children: _jsx(Box, { sx: {
|
|
315
317
|
position: 'absolute',
|
|
316
318
|
inset: 0,
|
|
317
319
|
display: 'flex',
|
|
@@ -26,6 +26,8 @@ export interface ChatBaseHeaderProps {
|
|
|
26
26
|
padding: number;
|
|
27
27
|
/** Optional kernel indicator state override from notebook runtime. */
|
|
28
28
|
kernelIndicatorState?: ExecutionState;
|
|
29
|
+
/** Horizontal placement for the kernel indicator in the title row. */
|
|
30
|
+
kernelIndicatorPlacement?: 'left' | 'center' | 'right';
|
|
29
31
|
/**
|
|
30
32
|
* Runtime status from agent-runtimes sandbox status stream.
|
|
31
33
|
* Uses the same execution-state model as KernelIndicator.
|
|
@@ -69,5 +71,5 @@ export interface ChatBaseHeaderProps {
|
|
|
69
71
|
/** Callback fired when the user changes the companion-surface mode. */
|
|
70
72
|
onEphemeralSurfaceModeChange?: (mode: EphemeralSurfaceMode) => void;
|
|
71
73
|
}
|
|
72
|
-
export declare function ChatBaseHeader({ title, subtitle, brandIcon, headerContent, headerActions, showInformation, onInformationClick, padding, kernelIndicatorState, runtimeStatus, kernel, kernelEnvironmentName, kernelCpu, kernelMemory, kernelGpu, headerButtons, messageCount, onNewChat, onClear, chatViewMode, onChatViewModeChange, showEphemeralSurfaceControl, enableEphemeralNotebookOption, enableEphemeralDocumentOption, ephemeralSurfaceMode, onEphemeralSurfaceModeChange, }: ChatBaseHeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
74
|
+
export declare function ChatBaseHeader({ title, subtitle, brandIcon, headerContent, headerActions, showInformation, onInformationClick, padding, kernelIndicatorState, kernelIndicatorPlacement, runtimeStatus, kernel, kernelEnvironmentName, kernelCpu, kernelMemory, kernelGpu, headerButtons, messageCount, onNewChat, onClear, chatViewMode, onChatViewModeChange, showEphemeralSurfaceControl, enableEphemeralNotebookOption, enableEphemeralDocumentOption, ephemeralSurfaceMode, onEphemeralSurfaceModeChange, }: ChatBaseHeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
73
75
|
export {};
|
|
@@ -29,11 +29,13 @@ export function toRuntimeExecutionState(runtimeStatus) {
|
|
|
29
29
|
// ---------------------------------------------------------------------------
|
|
30
30
|
// Component
|
|
31
31
|
// ---------------------------------------------------------------------------
|
|
32
|
-
export function ChatBaseHeader({ title, subtitle, brandIcon, headerContent, headerActions, showInformation, onInformationClick, padding, kernelIndicatorState, runtimeStatus, kernel, kernelEnvironmentName, kernelCpu, kernelMemory, kernelGpu, headerButtons, messageCount, onNewChat, onClear, chatViewMode, onChatViewModeChange, showEphemeralSurfaceControl = false, enableEphemeralNotebookOption = false, enableEphemeralDocumentOption = false, ephemeralSurfaceMode = 'none', onEphemeralSurfaceModeChange, }) {
|
|
32
|
+
export function ChatBaseHeader({ title, subtitle, brandIcon, headerContent, headerActions, showInformation, onInformationClick, padding, kernelIndicatorState, kernelIndicatorPlacement = 'left', runtimeStatus, kernel, kernelEnvironmentName, kernelCpu, kernelMemory, kernelGpu, headerButtons, messageCount, onNewChat, onClear, chatViewMode, onChatViewModeChange, showEphemeralSurfaceControl = false, enableEphemeralNotebookOption = false, enableEphemeralDocumentOption = false, ephemeralSurfaceMode = 'none', onEphemeralSurfaceModeChange, }) {
|
|
33
33
|
const effectiveIndicatorState = kernelIndicatorState ?? toRuntimeExecutionState(runtimeStatus);
|
|
34
|
+
const kernelIndicatorElement = kernel ? (_jsx(KernelIndicator, { kernel: kernel, environmentName: kernelEnvironmentName, cpu: kernelCpu, memory: kernelMemory, gpu: kernelGpu, position: "s", bordered: false })) : (_jsx(KernelIndicator, { state: effectiveIndicatorState ?? 'undefined', environmentName: kernelEnvironmentName, cpu: kernelCpu, memory: kernelMemory, gpu: kernelGpu, position: "s", bordered: false }));
|
|
34
35
|
return (_jsx(Box, { sx: {
|
|
35
36
|
display: 'flex',
|
|
36
37
|
flexDirection: 'column',
|
|
38
|
+
bg: 'canvas.default',
|
|
37
39
|
borderBottom: '1px solid',
|
|
38
40
|
borderColor: 'border.default',
|
|
39
41
|
}, children: _jsxs(Box, { sx: {
|
|
@@ -47,7 +49,7 @@ export function ChatBaseHeader({ title, subtitle, brandIcon, headerContent, head
|
|
|
47
49
|
gap: 2,
|
|
48
50
|
minWidth: 0,
|
|
49
51
|
flex: '1 1 auto',
|
|
50
|
-
}, children: [brandIcon || _jsx(AiAgentIcon, { colored: true, size: 20 }),
|
|
52
|
+
}, children: [kernelIndicatorPlacement === 'left' && kernelIndicatorElement, brandIcon || _jsx(AiAgentIcon, { colored: true, size: 20 }), (title || subtitle) && (_jsxs(Box, { sx: {
|
|
51
53
|
display: 'flex',
|
|
52
54
|
flexDirection: 'column',
|
|
53
55
|
minWidth: 0,
|
|
@@ -62,7 +64,7 @@ export function ChatBaseHeader({ title, subtitle, brandIcon, headerContent, head
|
|
|
62
64
|
color: 'fg.muted',
|
|
63
65
|
minWidth: 0,
|
|
64
66
|
maxWidth: '100%',
|
|
65
|
-
}, children: _jsx(Truncate, { title: subtitle, maxWidth: "40ch", children: subtitle }) }))] })), headerContent, showInformation && (_jsx(IconButton, { icon: InfoIcon, "aria-label": "Information", variant: "invisible", size: "small", onClick: onInformationClick }))] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1, flexShrink: 0 }, children: [headerButtons?.showNewChat && (_jsx(IconButton, { icon: PlusIcon, "aria-label": "New chat", variant: "invisible", size: "small", onClick: onNewChat })), headerButtons?.showClear && messageCount > 0 && (_jsx(IconButton, { icon: TrashIcon, "aria-label": "Clear messages", variant: "invisible", size: "small", onClick: onClear })), headerButtons?.showSettings && (_jsx(IconButton, { icon: GearIcon, "aria-label": "Settings", variant: "invisible", size: "small", onClick: headerButtons.onSettings })), showEphemeralSurfaceControl && (_jsx(Box, { sx: {
|
|
67
|
+
}, children: _jsx(Truncate, { title: subtitle, maxWidth: "40ch", children: subtitle }) }))] })), headerContent, showInformation && (_jsx(IconButton, { icon: InfoIcon, "aria-label": "Information", variant: "invisible", size: "small", onClick: onInformationClick })), kernelIndicatorPlacement === 'center' && kernelIndicatorElement] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1, flexShrink: 0 }, children: [kernelIndicatorPlacement === 'right' && kernelIndicatorElement, headerButtons?.showNewChat && (_jsx(IconButton, { icon: PlusIcon, "aria-label": "New chat", variant: "invisible", size: "small", onClick: onNewChat })), headerButtons?.showClear && messageCount > 0 && (_jsx(IconButton, { icon: TrashIcon, "aria-label": "Clear messages", variant: "invisible", size: "small", onClick: onClear })), headerButtons?.showSettings && (_jsx(IconButton, { icon: GearIcon, "aria-label": "Settings", variant: "invisible", size: "small", onClick: headerButtons.onSettings })), showEphemeralSurfaceControl && (_jsx(Box, { sx: {
|
|
66
68
|
display: 'inline-flex',
|
|
67
69
|
alignItems: 'center',
|
|
68
70
|
bg: 'neutral.muted',
|
|
@@ -46,4 +46,23 @@ export interface ChatMessageListProps {
|
|
|
46
46
|
*/
|
|
47
47
|
approvalConfig?: ToolApprovalConfig;
|
|
48
48
|
}
|
|
49
|
+
type SubagentAvatarConfig = Required<Pick<AvatarConfig, 'userAvatar' | 'assistantAvatar' | 'showAvatars' | 'avatarSize' | 'userAvatarBg' | 'assistantAvatarBg'>>;
|
|
50
|
+
export interface SubagentChatPanelProps {
|
|
51
|
+
/** Parent `delegate_task` tool call id used to look up streamed events. */
|
|
52
|
+
toolCallId: string;
|
|
53
|
+
/** Subagent name used as a fallback when the tool-call id does not match. */
|
|
54
|
+
subagentName?: string;
|
|
55
|
+
/** Fixed viewport height in pixels. */
|
|
56
|
+
height?: number;
|
|
57
|
+
/** Avatar configuration for the embedded message list. */
|
|
58
|
+
avatarConfig?: SubagentAvatarConfig;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Fixed-height, chat-style viewport for a single delegated subagent run.
|
|
62
|
+
* Reuses {@link ChatMessageList} so subagent output is formatted exactly like
|
|
63
|
+
* the primary agent, including tool-call cards and inline approvals. Tool
|
|
64
|
+
* approval decisions are sent over the shared monitoring socket.
|
|
65
|
+
*/
|
|
66
|
+
export declare function SubagentChatPanel({ toolCallId, subagentName: subagentNameHint, height, avatarConfig, }: SubagentChatPanelProps): React.ReactElement | null;
|
|
49
67
|
export declare function ChatMessageList({ displayItems, isLoading, isStreaming, showLoadingIndicator, hideMessagesAfterToolUI, avatarConfig, padding, renderToolResult, emptyContent, messagesEndRef, onRespond, approvalConfig, }: ChatMessageListProps): import("react/jsx-runtime").JSX.Element;
|
|
68
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
/*
|
|
3
3
|
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
4
4
|
* Distributed under the terms of the Modified BSD License.
|
|
@@ -9,14 +9,15 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
9
9
|
*
|
|
10
10
|
* @module chat/messages/MessageList
|
|
11
11
|
*/
|
|
12
|
-
import { useState, useCallback, useMemo, } from 'react';
|
|
13
|
-
import { Text } from '@primer/react';
|
|
12
|
+
import { useState, useCallback, useEffect, useMemo, useRef, } from 'react';
|
|
13
|
+
import { Text, Spinner } from '@primer/react';
|
|
14
|
+
import { DependabotIcon, PersonIcon } from '@primer/octicons-react';
|
|
14
15
|
import { Box } from '@datalayer/primer-addons';
|
|
15
16
|
import { Streamdown } from 'streamdown';
|
|
16
17
|
import { streamdownMarkdownStyles, streamdownCodeBlockStyles, } from '../styles/streamdownStyles';
|
|
17
18
|
import { ToolCallDisplay } from '../tools/ToolCallDisplay';
|
|
18
19
|
import { isToolCallMessage, getMessageText } from '../../utils';
|
|
19
|
-
import { useAgentRuntimeStore } from '../../stores/agentRuntimeStore';
|
|
20
|
+
import { useAgentRuntimeStore, useAgentRuntimeSubagentActivityByToolCall, agentRuntimeStore, } from '../../stores/agentRuntimeStore';
|
|
20
21
|
// ---------------------------------------------------------------------------
|
|
21
22
|
// Normalize helper
|
|
22
23
|
// ---------------------------------------------------------------------------
|
|
@@ -70,6 +71,171 @@ function normalizeAssistantMarkdown(text) {
|
|
|
70
71
|
});
|
|
71
72
|
return out;
|
|
72
73
|
}
|
|
74
|
+
const SUBAGENT_AVATAR_CONFIG = {
|
|
75
|
+
userAvatar: _jsx(PersonIcon, { size: 14 }),
|
|
76
|
+
assistantAvatar: _jsx(DependabotIcon, { size: 14 }),
|
|
77
|
+
showAvatars: true,
|
|
78
|
+
avatarSize: 24,
|
|
79
|
+
userAvatarBg: 'neutral.muted',
|
|
80
|
+
assistantAvatarBg: 'accent.emphasis',
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Convert streamed subagent activity into the `DisplayItem[]` shape the main
|
|
84
|
+
* chat renders: text deltas merge into markdown messages, and tool events pair
|
|
85
|
+
* into tool-call cards (with approval controls when the runtime requests one).
|
|
86
|
+
* Reasoning (`thinking`) is omitted to mirror the primary agent view.
|
|
87
|
+
*/
|
|
88
|
+
function buildSubagentDisplayItems(events, keyPrefix) {
|
|
89
|
+
const items = [];
|
|
90
|
+
const openByName = new Map();
|
|
91
|
+
let textBuffer = '';
|
|
92
|
+
let seq = 0;
|
|
93
|
+
const flushText = () => {
|
|
94
|
+
if (textBuffer.trim()) {
|
|
95
|
+
items.push({
|
|
96
|
+
id: `${keyPrefix}:msg:${seq++}`,
|
|
97
|
+
role: 'assistant',
|
|
98
|
+
content: textBuffer,
|
|
99
|
+
createdAt: new Date(0),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
textBuffer = '';
|
|
103
|
+
};
|
|
104
|
+
for (const event of events) {
|
|
105
|
+
switch (event.phase) {
|
|
106
|
+
case 'text':
|
|
107
|
+
textBuffer += event.text ?? '';
|
|
108
|
+
break;
|
|
109
|
+
case 'tool_call': {
|
|
110
|
+
flushText();
|
|
111
|
+
const id = `${keyPrefix}:tool:${seq++}`;
|
|
112
|
+
const toolCall = {
|
|
113
|
+
id,
|
|
114
|
+
type: 'tool-call',
|
|
115
|
+
toolCallId: id,
|
|
116
|
+
toolName: event.toolName ?? 'tool',
|
|
117
|
+
args: event.toolArgs ?? {},
|
|
118
|
+
status: 'inProgress',
|
|
119
|
+
};
|
|
120
|
+
items.push(toolCall);
|
|
121
|
+
if (event.toolName) {
|
|
122
|
+
openByName.set(event.toolName, toolCall);
|
|
123
|
+
}
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
case 'tool_result': {
|
|
127
|
+
flushText();
|
|
128
|
+
const open = event.toolName
|
|
129
|
+
? openByName.get(event.toolName)
|
|
130
|
+
: undefined;
|
|
131
|
+
if (open) {
|
|
132
|
+
open.result = event.result;
|
|
133
|
+
open.status = 'complete';
|
|
134
|
+
if (event.toolName) {
|
|
135
|
+
openByName.delete(event.toolName);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
const id = `${keyPrefix}:tool:${seq++}`;
|
|
140
|
+
items.push({
|
|
141
|
+
id,
|
|
142
|
+
type: 'tool-call',
|
|
143
|
+
toolCallId: id,
|
|
144
|
+
toolName: event.toolName ?? 'tool',
|
|
145
|
+
args: {},
|
|
146
|
+
result: event.result,
|
|
147
|
+
status: 'complete',
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
case 'error':
|
|
153
|
+
flushText();
|
|
154
|
+
items.push({
|
|
155
|
+
id: `${keyPrefix}:msg:${seq++}`,
|
|
156
|
+
role: 'assistant',
|
|
157
|
+
content: `**\u26a0\ufe0f ${event.error ?? 'Subagent failed'}**`,
|
|
158
|
+
createdAt: new Date(0),
|
|
159
|
+
});
|
|
160
|
+
break;
|
|
161
|
+
// `start`, `thinking`, and `end` do not produce display items.
|
|
162
|
+
default:
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
flushText();
|
|
167
|
+
return items;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Fixed-height, chat-style viewport for a single delegated subagent run.
|
|
171
|
+
* Reuses {@link ChatMessageList} so subagent output is formatted exactly like
|
|
172
|
+
* the primary agent, including tool-call cards and inline approvals. Tool
|
|
173
|
+
* approval decisions are sent over the shared monitoring socket.
|
|
174
|
+
*/
|
|
175
|
+
export function SubagentChatPanel({ toolCallId, subagentName: subagentNameHint, height = 280, avatarConfig = SUBAGENT_AVATAR_CONFIG, }) {
|
|
176
|
+
const events = useAgentRuntimeSubagentActivityByToolCall(toolCallId, subagentNameHint);
|
|
177
|
+
const displayItems = useMemo(() => buildSubagentDisplayItems(events, toolCallId), [events, toolCallId]);
|
|
178
|
+
const messagesEndRef = useRef(null);
|
|
179
|
+
const subagentName = useMemo(() => {
|
|
180
|
+
for (const event of events) {
|
|
181
|
+
if (event.subagentName)
|
|
182
|
+
return event.subagentName;
|
|
183
|
+
}
|
|
184
|
+
return subagentNameHint ?? 'subagent';
|
|
185
|
+
}, [events, subagentNameHint]);
|
|
186
|
+
const isDone = useMemo(() => events.some(e => e.phase === 'end' || e.phase === 'error'), [events]);
|
|
187
|
+
const hasError = useMemo(() => events.some(e => e.phase === 'error'), [events]);
|
|
188
|
+
// Forward inline tool-approval decisions to the shared monitoring socket.
|
|
189
|
+
const handleRespond = useCallback(async (_toolCallId, result) => {
|
|
190
|
+
if (result && typeof result === 'object') {
|
|
191
|
+
const record = result;
|
|
192
|
+
if (record.type === 'tool-approval-decision' &&
|
|
193
|
+
typeof record.approved === 'boolean' &&
|
|
194
|
+
typeof record.approvalId === 'string') {
|
|
195
|
+
agentRuntimeStore
|
|
196
|
+
.getState()
|
|
197
|
+
.sendDecision(record.approvalId, record.approved);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}, []);
|
|
201
|
+
useEffect(() => {
|
|
202
|
+
messagesEndRef.current?.scrollIntoView({ block: 'end' });
|
|
203
|
+
}, [displayItems]);
|
|
204
|
+
if (events.length === 0) {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
return (_jsxs(Box, { sx: {
|
|
208
|
+
mt: 2,
|
|
209
|
+
display: 'flex',
|
|
210
|
+
flexDirection: 'column',
|
|
211
|
+
height,
|
|
212
|
+
width: '100%',
|
|
213
|
+
border: '1px solid',
|
|
214
|
+
borderColor: 'border.default',
|
|
215
|
+
borderRadius: 2,
|
|
216
|
+
overflow: 'hidden',
|
|
217
|
+
bg: 'canvas.default',
|
|
218
|
+
}, children: [_jsxs(Box, { sx: {
|
|
219
|
+
display: 'flex',
|
|
220
|
+
alignItems: 'center',
|
|
221
|
+
gap: 1,
|
|
222
|
+
px: 2,
|
|
223
|
+
py: 1,
|
|
224
|
+
borderBottom: '1px solid',
|
|
225
|
+
borderColor: 'border.muted',
|
|
226
|
+
bg: 'canvas.subtle',
|
|
227
|
+
flexShrink: 0,
|
|
228
|
+
}, children: [_jsx(Box, { sx: {
|
|
229
|
+
display: 'flex',
|
|
230
|
+
color: hasError ? 'danger.fg' : isDone ? 'success.fg' : 'accent.fg',
|
|
231
|
+
}, children: _jsx(DependabotIcon, { size: 14 }) }), _jsx(Text, { sx: { fontSize: 0, fontWeight: 'bold', color: 'fg.default' }, children: subagentName }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', ml: 'auto' }, children: hasError ? 'failed' : isDone ? 'done' : 'working\u2026' })] }), _jsx(Box, { sx: { flex: 1, minHeight: 0, overflowY: 'auto' }, children: _jsx(ChatMessageList, { displayItems: displayItems, isLoading: !isDone, isStreaming: !isDone, showLoadingIndicator: !isDone, hideMessagesAfterToolUI: false, avatarConfig: avatarConfig, padding: 2, onRespond: handleRespond, messagesEndRef: messagesEndRef, emptyContent: _jsxs(Box, { sx: {
|
|
232
|
+
display: 'flex',
|
|
233
|
+
alignItems: 'center',
|
|
234
|
+
gap: 1,
|
|
235
|
+
color: 'fg.muted',
|
|
236
|
+
p: 2,
|
|
237
|
+
}, children: [_jsx(Spinner, { size: "small" }), _jsx(Text, { sx: { fontSize: 0 }, children: "Starting\u2026" })] }) }) })] }));
|
|
238
|
+
}
|
|
73
239
|
// ---------------------------------------------------------------------------
|
|
74
240
|
// DefaultToolCallRenderer — reads approvals from the Zustand store,
|
|
75
241
|
// sends decisions over the shared WebSocket (no REST polling).
|
|
@@ -175,11 +341,16 @@ function DefaultToolCallRenderer({ item, onRespond, }) {
|
|
|
175
341
|
: externalDecision
|
|
176
342
|
? 'external'
|
|
177
343
|
: undefined;
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
344
|
+
// `normalizeName` strips underscores, so compare against the normalized form.
|
|
345
|
+
const isSubagentDelegation = normalizedToolName === 'delegatetask';
|
|
346
|
+
const delegatedSubagentName = typeof item.args?.subagent_name === 'string'
|
|
347
|
+
? item.args.subagent_name
|
|
348
|
+
: undefined;
|
|
349
|
+
return (_jsxs(_Fragment, { children: [_jsx(ToolCallDisplay, { toolCallId: item.toolCallId, toolName: item.toolName, args: item.args, result: item.result, status: item.status, error: item.error, executionError: item.executionError, codeError: item.codeError, exitCode: item.exitCode, approvalRequired: hasApproval, approvalState: approvalState, approvalDecisionSource: approvalDecisionSource, onApprove: effectiveApprovalId && !decision && !externalDecision
|
|
350
|
+
? () => makeDecision('approve')
|
|
351
|
+
: undefined, onDeny: effectiveApprovalId && !decision && !externalDecision
|
|
352
|
+
? () => makeDecision('reject')
|
|
353
|
+
: undefined, approvalLoading: false }), isSubagentDelegation && (_jsx(SubagentChatPanel, { toolCallId: item.toolCallId, subagentName: delegatedSubagentName }))] }));
|
|
183
354
|
}
|
|
184
355
|
// ---------------------------------------------------------------------------
|
|
185
356
|
// Component
|
|
@@ -43,6 +43,10 @@ export interface EphemeralNotebookProps {
|
|
|
43
43
|
onNbformatChange?: (content: INotebookContent) => void;
|
|
44
44
|
/** Optional toolbar component override. */
|
|
45
45
|
toolbarComponent?: EphemeralNotebookToolbarComponent;
|
|
46
|
+
/** Optional theme variant override from host chat context. */
|
|
47
|
+
themeVariant?: string;
|
|
48
|
+
/** Optional color mode override from host chat context. */
|
|
49
|
+
colorMode?: 'light' | 'dark' | 'auto';
|
|
46
50
|
/**
|
|
47
51
|
* Optional real-time collaboration provider. When supplied, the notebook
|
|
48
52
|
* joins a shared collaborative room (the ydoc becomes the source of truth)
|
|
@@ -55,5 +59,5 @@ export interface EphemeralNotebookProps {
|
|
|
55
59
|
/**
|
|
56
60
|
* Renders an in-memory notebook backed by a sandbox kernel.
|
|
57
61
|
*/
|
|
58
|
-
export declare function EphemeralNotebook({ notebookId, runtimePodName, runtimeOverride, cellSidebarMargin, nbformat, onNbformatChange, toolbarComponent, collaborationProvider, }: EphemeralNotebookProps): import("react/jsx-runtime").JSX.Element;
|
|
62
|
+
export declare function EphemeralNotebook({ notebookId, runtimePodName, runtimeOverride, cellSidebarMargin, nbformat, onNbformatChange, toolbarComponent, themeVariant, colorMode, collaborationProvider, }: EphemeralNotebookProps): import("react/jsx-runtime").JSX.Element;
|
|
59
63
|
export default EphemeralNotebook;
|
|
@@ -55,7 +55,7 @@ const EPHEMERAL_NOTEBOOK_CONTENT = {
|
|
|
55
55
|
/**
|
|
56
56
|
* Renders an in-memory notebook backed by a sandbox kernel.
|
|
57
57
|
*/
|
|
58
|
-
export function EphemeralNotebook({ notebookId, runtimePodName, runtimeOverride, cellSidebarMargin = 120, nbformat, onNbformatChange, toolbarComponent, collaborationProvider, }) {
|
|
58
|
+
export function EphemeralNotebook({ notebookId, runtimePodName, runtimeOverride, cellSidebarMargin = 120, nbformat, onNbformatChange, toolbarComponent, themeVariant, colorMode, collaborationProvider, }) {
|
|
59
59
|
// The `nbformat` passed to the `Notebook` component MUST stay a stable
|
|
60
60
|
// reference for the lifetime of a given `notebookId`: the underlying
|
|
61
61
|
// `useNotebookModel` hook rebuilds the notebook model (and its context /
|
|
@@ -139,7 +139,8 @@ export function EphemeralNotebook({ notebookId, runtimePodName, runtimeOverride,
|
|
|
139
139
|
}
|
|
140
140
|
try {
|
|
141
141
|
const token = String(selectedRuntime?.token || '').trim();
|
|
142
|
-
const wsUrl = String(selectedRuntime?.wsUrl || '').trim() ||
|
|
142
|
+
const wsUrl = String(selectedRuntime?.wsUrl || '').trim() ||
|
|
143
|
+
baseUrl.replace(/^http/, 'ws');
|
|
143
144
|
const serverSettings = ServerConnection.makeSettings({
|
|
144
145
|
baseUrl,
|
|
145
146
|
wsUrl,
|
|
@@ -236,10 +237,12 @@ export function EphemeralNotebook({ notebookId, runtimePodName, runtimeOverride,
|
|
|
236
237
|
// Resolve the active theme/color-mode exactly like the notebook editor
|
|
237
238
|
// (NotebookEditorPanel) so the notebook honours dark / branded themes
|
|
238
239
|
// instead of always rendering light.
|
|
239
|
-
const { colorMode, theme:
|
|
240
|
+
const { colorMode: storeColorMode, theme: storeThemeVariant } = useThemeStore();
|
|
241
|
+
const effectiveColorMode = colorMode ?? storeColorMode;
|
|
242
|
+
const effectiveThemeVariant = themeVariant ?? storeThemeVariant;
|
|
240
243
|
const systemMode = useSystemColorMode();
|
|
241
|
-
const themeConfig = getThemeConfig(
|
|
242
|
-
const resolvedMode =
|
|
244
|
+
const themeConfig = getThemeConfig(effectiveThemeVariant);
|
|
245
|
+
const resolvedMode = effectiveColorMode === 'auto' ? systemMode : effectiveColorMode;
|
|
243
246
|
const modeStyles = resolvedMode === 'dark'
|
|
244
247
|
? themeConfig.themeStyles.dark
|
|
245
248
|
: themeConfig.themeStyles.light;
|
|
@@ -252,7 +255,7 @@ export function EphemeralNotebook({ notebookId, runtimePodName, runtimeOverride,
|
|
|
252
255
|
position: 'relative',
|
|
253
256
|
overflow: 'hidden',
|
|
254
257
|
bg: 'canvas.default',
|
|
255
|
-
}, children: activeServiceManager ? (_jsx(DatalayerThemeProvider, { colorMode:
|
|
258
|
+
}, children: activeServiceManager ? (_jsx(DatalayerThemeProvider, { colorMode: effectiveColorMode, theme: themeConfig.primerTheme, themeStyles: themeConfig.themeStyles, children: _jsx(JupyterReactTheme, { colormode: resolvedMode, backgroundColor: themeBackground, children: _jsx(Box, { sx: {
|
|
256
259
|
position: 'absolute',
|
|
257
260
|
inset: 0,
|
|
258
261
|
display: 'flex',
|
|
@@ -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 ?? '#
|
|
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';
|
package/lib/chat/tools/index.js
CHANGED
|
@@ -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';
|