@datalayer/agent-runtimes 1.0.5 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +157 -10
- package/lib/AgentNode.d.ts +3 -0
- package/lib/AgentNode.js +676 -0
- package/lib/agent-node/themeStore.d.ts +3 -0
- package/lib/agent-node/themeStore.js +156 -0
- package/lib/agent-node-main.d.ts +1 -0
- package/lib/agent-node-main.js +14 -0
- package/lib/chat/Chat.js +16 -10
- package/lib/chat/ChatFloating.js +1 -1
- package/lib/chat/ChatSidebar.js +81 -49
- package/lib/chat/base/ChatBase.js +388 -74
- package/lib/chat/display/FloatingBrandButton.js +8 -1
- package/lib/chat/header/ChatHeader.d.ts +3 -1
- package/lib/chat/header/ChatHeader.js +15 -12
- package/lib/chat/header/ChatHeaderBase.d.ts +29 -9
- package/lib/chat/header/ChatHeaderBase.js +26 -3
- package/lib/chat/indicators/SandboxStatusIndicator.js +82 -47
- package/lib/chat/messages/ChatMessageList.js +46 -1
- package/lib/chat/messages/ChatMessages.js +6 -2
- package/lib/chat/prompt/InputFooter.d.ts +3 -1
- package/lib/chat/prompt/InputFooter.js +8 -5
- package/lib/chat/prompt/InputPrompt.d.ts +3 -1
- package/lib/chat/prompt/InputPrompt.js +2 -2
- package/lib/chat/prompt/InputPromptFooter.d.ts +3 -1
- package/lib/chat/prompt/InputPromptFooter.js +3 -3
- package/lib/client/AgentsMixin.js +14 -0
- package/lib/config/AgentConfiguration.d.ts +22 -0
- package/lib/config/AgentConfiguration.js +319 -64
- package/lib/examples/AgUiSharedStateExample.js +2 -1
- package/lib/examples/AgentCheckpointsExample.js +3 -3
- package/lib/examples/AgentCodemodeExample.d.ts +3 -3
- package/lib/examples/AgentCodemodeExample.js +24 -12
- package/lib/examples/AgentEvalsExample.js +330 -40
- package/lib/examples/AgentGuardrailsExample.js +16 -5
- package/lib/examples/AgentHooksExample.js +27 -9
- package/lib/examples/AgentInferenceProviderExample.d.ts +3 -0
- package/lib/examples/AgentInferenceProviderExample.js +329 -0
- package/lib/examples/AgentMCPExample.js +6 -5
- package/lib/examples/AgentMemoryExample.d.ts +1 -2
- package/lib/examples/AgentMemoryExample.js +71 -22
- package/lib/examples/AgentMonitoringExample.js +5 -5
- package/lib/examples/AgentNotificationsExample.d.ts +1 -2
- package/lib/examples/AgentNotificationsExample.js +71 -22
- package/lib/examples/AgentOtelExample.js +31 -40
- package/lib/examples/AgentOutputsExample.d.ts +1 -1
- package/lib/examples/AgentOutputsExample.js +67 -16
- package/lib/examples/AgentParametersExample.js +10 -8
- package/lib/examples/AgentSandboxExample.d.ts +1 -1
- package/lib/examples/AgentSandboxExample.js +7 -6
- package/lib/examples/AgentSkillsExample.js +6 -6
- package/lib/examples/AgentSubagentsExample.d.ts +1 -1
- package/lib/examples/AgentSubagentsExample.js +6 -6
- package/lib/examples/AgentToolApprovalsExample.js +27 -11
- package/lib/examples/AgentTriggersExample.js +5 -5
- package/lib/examples/{AgentSpecsExample.d.ts → AgentspecsExample.d.ts} +2 -2
- package/lib/examples/AgentspecsExample.js +1096 -0
- package/lib/examples/ChatCustomExample.js +6 -5
- package/lib/examples/ChatExample.js +6 -5
- package/lib/examples/Lexical2Example.js +1 -1
- package/lib/examples/LexicalAgentExample.js +1 -1
- package/lib/examples/NotebookAgentExample.js +3 -3
- package/lib/examples/components/ExampleWrapper.d.ts +6 -7
- package/lib/examples/components/ExampleWrapper.js +27 -10
- package/lib/examples/example-selector.js +2 -1
- package/lib/examples/index.d.ts +2 -1
- package/lib/examples/index.js +2 -1
- package/lib/examples/lexical/initial-content.json +6 -6
- package/lib/examples/main.js +56 -16
- package/lib/examples/utils/agentId.d.ts +1 -1
- package/lib/examples/utils/agentId.js +1 -1
- package/lib/examples/utils/useExampleAgentRuntimesUrl.d.ts +5 -0
- package/lib/examples/utils/useExampleAgentRuntimesUrl.js +19 -0
- package/lib/hooks/useAIAgentsWebSocket.js +35 -0
- package/lib/hooks/useAgentRuntimes.d.ts +32 -3
- package/lib/hooks/useAgentRuntimes.js +114 -19
- package/lib/index.d.ts +1 -1
- package/lib/specs/agents/agents.d.ts +20 -13
- package/lib/specs/agents/agents.js +1267 -581
- package/lib/specs/benchmarks.d.ts +20 -0
- package/lib/specs/benchmarks.js +205 -0
- package/lib/specs/envvars.d.ts +0 -1
- package/lib/specs/envvars.js +0 -11
- package/lib/specs/evals.d.ts +10 -9
- package/lib/specs/evals.js +128 -88
- package/lib/specs/index.d.ts +0 -1
- package/lib/specs/index.js +0 -1
- package/lib/specs/models.d.ts +0 -2
- package/lib/specs/models.js +0 -15
- package/lib/specs/skills.d.ts +0 -1
- package/lib/specs/skills.js +0 -18
- package/lib/stores/agentRuntimeStore.d.ts +5 -1
- package/lib/stores/agentRuntimeStore.js +22 -8
- package/lib/stores/conversationStore.js +2 -2
- package/lib/types/agents-lifecycle.d.ts +18 -0
- package/lib/types/agents.d.ts +6 -0
- package/lib/types/agentspecs.d.ts +4 -0
- package/lib/types/benchmarks.d.ts +43 -0
- package/lib/types/benchmarks.js +5 -0
- package/lib/types/chat.d.ts +16 -0
- package/lib/types/evals.d.ts +26 -17
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.js +1 -0
- package/package.json +9 -5
- package/scripts/codegen/__pycache__/generate_agents.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_benchmarks.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_evals.cpython-313.pyc +0 -0
- package/scripts/codegen/generate_agents.py +89 -43
- package/scripts/codegen/generate_benchmarks.py +441 -0
- package/scripts/codegen/generate_evals.py +94 -16
- package/scripts/codegen/generate_events.py +0 -1
- package/lib/examples/AgentSpecsExample.js +0 -694
|
@@ -10,6 +10,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
10
10
|
* @module chat/display/FloatingBrandButton
|
|
11
11
|
*/
|
|
12
12
|
import { useState } from 'react';
|
|
13
|
+
import { createPortal } from 'react-dom';
|
|
13
14
|
import { Box, IconButton, Tooltip, Text } from '@primer/react';
|
|
14
15
|
import { CommentDiscussionIcon, XIcon } from '@primer/octicons-react';
|
|
15
16
|
/**
|
|
@@ -26,7 +27,7 @@ export function FloatingBrandButton({ isOpen, onToggle, position = 'bottom-right
|
|
|
26
27
|
'top-left': { top: 20, left: 20 },
|
|
27
28
|
};
|
|
28
29
|
const posStyle = positionStyles[position];
|
|
29
|
-
|
|
30
|
+
const floatingButton = (_jsx(Box, { className: className, sx: {
|
|
30
31
|
position: 'fixed',
|
|
31
32
|
zIndex: 1000,
|
|
32
33
|
...posStyle,
|
|
@@ -84,5 +85,11 @@ export function FloatingBrandButton({ isOpen, onToggle, position = 'bottom-right
|
|
|
84
85
|
},
|
|
85
86
|
},
|
|
86
87
|
} }))] }) }));
|
|
88
|
+
// Render in a body portal so fixed positioning stays anchored to the
|
|
89
|
+
// actual viewport edge even inside transformed containers.
|
|
90
|
+
if (typeof document !== 'undefined' && document.body) {
|
|
91
|
+
return createPortal(floatingButton, document.body);
|
|
92
|
+
}
|
|
93
|
+
return floatingButton;
|
|
87
94
|
}
|
|
88
95
|
export default FloatingBrandButton;
|
|
@@ -9,6 +9,8 @@ export interface ChatHeaderProps {
|
|
|
9
9
|
description?: string;
|
|
10
10
|
/** Current connection state */
|
|
11
11
|
connectionState: ConnectionState;
|
|
12
|
+
/** Marks the runtime as actively executing for busy-state animation. */
|
|
13
|
+
runtimeBusy?: boolean;
|
|
12
14
|
/** Callback when reconnect is clicked */
|
|
13
15
|
onReconnect?: () => void;
|
|
14
16
|
/** Callback when disconnect is clicked */
|
|
@@ -36,5 +38,5 @@ export interface ChatHeaderProps {
|
|
|
36
38
|
* />
|
|
37
39
|
* ```
|
|
38
40
|
*/
|
|
39
|
-
export declare function ChatHeader({ title, description, connectionState, onReconnect, onDisconnect, onLogout, onCollapsePanel, }: ChatHeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
41
|
+
export declare function ChatHeader({ title, description, connectionState, runtimeBusy, onReconnect, onDisconnect, onLogout, onCollapsePanel, }: ChatHeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
40
42
|
export default ChatHeader;
|
|
@@ -10,6 +10,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
10
10
|
*/
|
|
11
11
|
import { Text, IconButton, Button } from '@primer/react';
|
|
12
12
|
import { Box } from '@datalayer/primer-addons';
|
|
13
|
+
import { KernelIndicator, KERNEL_STATE_VISUALS, } from '@datalayer/jupyter-react';
|
|
13
14
|
import { SyncIcon, SignOutIcon, SidebarCollapseIcon, } from '@primer/octicons-react';
|
|
14
15
|
/**
|
|
15
16
|
* Chat header component with connection status indicator.
|
|
@@ -29,13 +30,17 @@ import { SyncIcon, SignOutIcon, SidebarCollapseIcon, } from '@primer/octicons-re
|
|
|
29
30
|
* />
|
|
30
31
|
* ```
|
|
31
32
|
*/
|
|
32
|
-
export function ChatHeader({ title, description, connectionState, onReconnect, onDisconnect, onLogout, onCollapsePanel, }) {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
export function ChatHeader({ title, description, connectionState, runtimeBusy = false, onReconnect, onDisconnect, onLogout, onCollapsePanel, }) {
|
|
34
|
+
const indicatorState = connectionState === 'connected'
|
|
35
|
+
? runtimeBusy
|
|
36
|
+
? 'connected-busy'
|
|
37
|
+
: 'connected-idle'
|
|
38
|
+
: connectionState === 'connecting'
|
|
39
|
+
? 'connecting'
|
|
40
|
+
: connectionState === 'error'
|
|
41
|
+
? 'connected-dead'
|
|
42
|
+
: 'disconnected';
|
|
43
|
+
const statusColor = KERNEL_STATE_VISUALS[indicatorState].color;
|
|
39
44
|
const labels = {
|
|
40
45
|
connected: 'Connected',
|
|
41
46
|
connecting: 'Connecting...',
|
|
@@ -56,11 +61,9 @@ export function ChatHeader({ title, description, connectionState, onReconnect, o
|
|
|
56
61
|
gap: 2,
|
|
57
62
|
fontSize: 0,
|
|
58
63
|
}, children: [_jsx(Box, { sx: {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
backgroundColor: colors[connectionState],
|
|
63
|
-
} }), _jsx(Text, { sx: { color: colors[connectionState] }, children: labels[connectionState] }), (connectionState === 'disconnected' ||
|
|
64
|
+
display: 'inline-flex',
|
|
65
|
+
alignItems: 'center',
|
|
66
|
+
}, children: _jsx(KernelIndicator, { state: indicatorState }) }), _jsx(Text, { sx: { color: statusColor }, children: labels[connectionState] }), (connectionState === 'disconnected' ||
|
|
64
67
|
connectionState === 'error') &&
|
|
65
68
|
onReconnect && (_jsx(IconButton, { icon: SyncIcon, "aria-label": "Reconnect", size: "small", variant: "invisible", onClick: onReconnect }))] }), onDisconnect && connectionState === 'connected' && (_jsx(Button, { variant: "invisible", size: "small", onClick: onDisconnect, children: "Disconnect" })), onLogout && (_jsx(IconButton, { icon: SignOutIcon, "aria-label": "Logout", size: "small", variant: "invisible", onClick: onLogout })), onCollapsePanel && (_jsx(IconButton, { icon: SidebarCollapseIcon, "aria-label": "Collapse panel", size: "small", variant: "invisible", onClick: onCollapsePanel }))] }) }));
|
|
66
69
|
}
|
|
@@ -8,8 +8,13 @@
|
|
|
8
8
|
* @module chat/header/ChatHeaderBase
|
|
9
9
|
*/
|
|
10
10
|
import { type ReactNode } from 'react';
|
|
11
|
+
import { type ExecutionState } from '@datalayer/jupyter-react';
|
|
12
|
+
import type { IKernelConnection } from '@jupyterlab/services/lib/kernel/kernel';
|
|
11
13
|
import type { ChatViewMode, HeaderButtonsConfig } from '../../types/chat';
|
|
14
|
+
import type { SandboxStatusData } from '../../types/context';
|
|
12
15
|
import type { SandboxWsStatus } from '../../types/sandbox';
|
|
16
|
+
type RuntimeStatus = SandboxStatusData | SandboxWsStatus;
|
|
17
|
+
export declare function toRuntimeExecutionState(runtimeStatus?: RuntimeStatus | null): ExecutionState | undefined;
|
|
13
18
|
export interface ChatBaseHeaderProps {
|
|
14
19
|
title?: string;
|
|
15
20
|
subtitle?: string;
|
|
@@ -19,14 +24,28 @@ export interface ChatBaseHeaderProps {
|
|
|
19
24
|
showInformation?: boolean;
|
|
20
25
|
onInformationClick?: () => void;
|
|
21
26
|
padding: number;
|
|
22
|
-
/**
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
/** Optional kernel indicator state override from notebook runtime. */
|
|
28
|
+
kernelIndicatorState?: ExecutionState;
|
|
29
|
+
/**
|
|
30
|
+
* Runtime status from agent-runtimes sandbox status stream.
|
|
31
|
+
* Uses the same execution-state model as KernelIndicator.
|
|
32
|
+
*/
|
|
33
|
+
runtimeStatus?: RuntimeStatus | null;
|
|
34
|
+
/**
|
|
35
|
+
* Live kernel connection from the notebook runtime. When provided,
|
|
36
|
+
* the chat header renders the same `<KernelIndicator>` as the notebook
|
|
37
|
+
* toolbar — subscribing to the kernel's live signals so the colour and
|
|
38
|
+
* tooltip stay in sync with the notebook indicator.
|
|
39
|
+
*/
|
|
40
|
+
kernel?: IKernelConnection | null;
|
|
41
|
+
/** Optional environment name shown in indicator details. */
|
|
42
|
+
kernelEnvironmentName?: string;
|
|
43
|
+
/** Optional CPU info shown in indicator details. */
|
|
44
|
+
kernelCpu?: string;
|
|
45
|
+
/** Optional memory info shown in indicator details. */
|
|
46
|
+
kernelMemory?: string;
|
|
47
|
+
/** Optional GPU info shown in indicator details. */
|
|
48
|
+
kernelGpu?: string;
|
|
30
49
|
/** Header button configuration */
|
|
31
50
|
headerButtons?: HeaderButtonsConfig;
|
|
32
51
|
/** Current count of messages (used to conditionally show clear button) */
|
|
@@ -40,4 +59,5 @@ export interface ChatBaseHeaderProps {
|
|
|
40
59
|
/** Callback when view mode changes */
|
|
41
60
|
onChatViewModeChange?: (mode: ChatViewMode) => void;
|
|
42
61
|
}
|
|
43
|
-
export declare function ChatBaseHeader({ title, subtitle, brandIcon, headerContent, headerActions, showInformation, onInformationClick, padding,
|
|
62
|
+
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, }: ChatBaseHeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
63
|
+
export {};
|
|
@@ -1,13 +1,36 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Heading, IconButton, Text, Truncate } from '@primer/react';
|
|
3
3
|
import { Box } from '@datalayer/primer-addons';
|
|
4
|
+
import { KernelIndicator } from '@datalayer/jupyter-react';
|
|
4
5
|
import { PlusIcon, TrashIcon, GearIcon, CommentDiscussionIcon, DeviceMobileIcon, SidebarExpandIcon, InfoIcon, } from '@primer/octicons-react';
|
|
5
6
|
import { AiAgentIcon } from '@datalayer/icons-react';
|
|
6
|
-
|
|
7
|
+
export function toRuntimeExecutionState(runtimeStatus) {
|
|
8
|
+
if (!runtimeStatus) {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
if ('available' in runtimeStatus && runtimeStatus.available === false) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
if (runtimeStatus.variant === 'unavailable' ||
|
|
15
|
+
runtimeStatus.variant === 'error') {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
if (runtimeStatus.sandbox_running === false) {
|
|
19
|
+
return 'disconnected';
|
|
20
|
+
}
|
|
21
|
+
if (runtimeStatus.is_executing === true) {
|
|
22
|
+
return 'connected-busy';
|
|
23
|
+
}
|
|
24
|
+
if (runtimeStatus.sandbox_running === true) {
|
|
25
|
+
return 'connected-idle';
|
|
26
|
+
}
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
7
29
|
// ---------------------------------------------------------------------------
|
|
8
30
|
// Component
|
|
9
31
|
// ---------------------------------------------------------------------------
|
|
10
|
-
export function ChatBaseHeader({ title, subtitle, brandIcon, headerContent, headerActions, showInformation, onInformationClick, padding,
|
|
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, }) {
|
|
33
|
+
const effectiveIndicatorState = kernelIndicatorState ?? toRuntimeExecutionState(runtimeStatus);
|
|
11
34
|
return (_jsx(Box, { sx: {
|
|
12
35
|
display: 'flex',
|
|
13
36
|
flexDirection: 'column',
|
|
@@ -39,7 +62,7 @@ export function ChatBaseHeader({ title, subtitle, brandIcon, headerContent, head
|
|
|
39
62
|
color: 'fg.muted',
|
|
40
63
|
minWidth: 0,
|
|
41
64
|
maxWidth: '100%',
|
|
42
|
-
}, 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: [_jsx(
|
|
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: [kernel ? (_jsx(KernelIndicator, { kernel: kernel, environmentName: kernelEnvironmentName, cpu: kernelCpu, memory: kernelMemory, gpu: kernelGpu, position: "sw", bordered: false })) : (_jsx(KernelIndicator, { state: effectiveIndicatorState ?? 'undefined', environmentName: kernelEnvironmentName, cpu: kernelCpu, memory: kernelMemory, gpu: kernelGpu, position: "sw", bordered: false })), 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 })), chatViewMode && onChatViewModeChange && (_jsx(Box, { sx: {
|
|
43
66
|
display: 'inline-flex',
|
|
44
67
|
alignItems: 'center',
|
|
45
68
|
bg: 'neutral.muted',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
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.
|
|
@@ -7,12 +7,12 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
7
7
|
* SandboxStatusIndicator — Round coloured dot that shows the
|
|
8
8
|
* real-time sandbox execution status via a WebSocket connection.
|
|
9
9
|
*
|
|
10
|
-
* Aggregate logic
|
|
10
|
+
* Aggregate logic (mapped to jupyter-react KernelIndicator states)
|
|
11
11
|
* ───────────────
|
|
12
|
-
* - variant === "unavailable" →
|
|
13
|
-
* - sandbox_running === false →
|
|
14
|
-
* - is_executing === false →
|
|
15
|
-
* - is_executing === true →
|
|
12
|
+
* - variant === "unavailable" → connected-unknown
|
|
13
|
+
* - sandbox_running === false → disconnected
|
|
14
|
+
* - is_executing === false → connected-idle
|
|
15
|
+
* - is_executing === true → connected-busy (themed fade animation)
|
|
16
16
|
*
|
|
17
17
|
* The component connects to the `/configure/sandbox/ws` WebSocket
|
|
18
18
|
* and receives status updates in real time. It can also send
|
|
@@ -21,9 +21,14 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
21
21
|
* @module chat/indicators/SandboxStatusIndicator
|
|
22
22
|
*/
|
|
23
23
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
24
|
-
import { Tooltip } from '@primer/react';
|
|
25
24
|
import { Box } from '@datalayer/primer-addons';
|
|
26
|
-
import {
|
|
25
|
+
import { SANDBOX_STATUS_LABELS } from '../../types/sandbox';
|
|
26
|
+
const SANDBOX_INDICATOR_COLORS = {
|
|
27
|
+
unavailable: 'fg.muted',
|
|
28
|
+
stopped: 'fg.muted',
|
|
29
|
+
idle: 'success.fg',
|
|
30
|
+
executing: 'attention.fg',
|
|
31
|
+
};
|
|
27
32
|
/* ── Helpers ───────────────────────────────────────────── */
|
|
28
33
|
function getWsUrl(apiBase, authToken, agentId) {
|
|
29
34
|
if (typeof window === 'undefined')
|
|
@@ -63,27 +68,39 @@ function deriveAggregate(status) {
|
|
|
63
68
|
return 'executing';
|
|
64
69
|
return 'idle';
|
|
65
70
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
71
|
+
function renderSandboxGlyph(aggregate) {
|
|
72
|
+
return (_jsx(Box, { as: "span", sx: {
|
|
73
|
+
display: 'inline-block',
|
|
74
|
+
width: 10,
|
|
75
|
+
height: 10,
|
|
76
|
+
borderRadius: '50%',
|
|
77
|
+
bg: SANDBOX_INDICATOR_COLORS[aggregate],
|
|
78
|
+
...(aggregate === 'executing' && {
|
|
79
|
+
animation: 'sandbox-busy-fade 1.2s ease-in-out infinite',
|
|
80
|
+
'@keyframes sandbox-busy-fade': {
|
|
81
|
+
'0%': {
|
|
82
|
+
opacity: 1,
|
|
83
|
+
transform: 'scale(1)',
|
|
84
|
+
filter: 'saturate(1)',
|
|
85
|
+
},
|
|
86
|
+
'50%': {
|
|
87
|
+
opacity: 0.45,
|
|
88
|
+
transform: 'scale(0.92)',
|
|
89
|
+
filter: 'saturate(0.75)',
|
|
90
|
+
},
|
|
91
|
+
'100%': {
|
|
92
|
+
opacity: 1,
|
|
93
|
+
transform: 'scale(1)',
|
|
94
|
+
filter: 'saturate(1)',
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
}),
|
|
98
|
+
} }));
|
|
82
99
|
}
|
|
83
100
|
/* ── Component ─────────────────────────────────────────── */
|
|
84
101
|
export function SandboxStatusIndicator({ apiBase, authToken, agentId, statusOverride, }) {
|
|
85
|
-
useInjectKeyframes();
|
|
86
102
|
const [status, setStatus] = useState(null);
|
|
103
|
+
const [isOverlayOpen, setIsOverlayOpen] = useState(false);
|
|
87
104
|
const wsRef = useRef(null);
|
|
88
105
|
const reconnectTimerRef = useRef();
|
|
89
106
|
const wsUrl = useMemo(() => getWsUrl(apiBase, authToken, agentId), [apiBase, authToken, agentId]);
|
|
@@ -149,27 +166,45 @@ export function SandboxStatusIndicator({ apiBase, authToken, agentId, statusOver
|
|
|
149
166
|
}, [aggregate, effectiveStatus]);
|
|
150
167
|
// Show a subtle gray dot when sandbox is unavailable.
|
|
151
168
|
// The tooltip tells the user none is configured.
|
|
152
|
-
return (
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
} }) }) })
|
|
169
|
+
return (_jsxs(Box, { as: "span", sx: {
|
|
170
|
+
position: 'relative',
|
|
171
|
+
display: 'inline-flex',
|
|
172
|
+
alignItems: 'center',
|
|
173
|
+
justifyContent: 'center',
|
|
174
|
+
}, onMouseEnter: () => setIsOverlayOpen(true), onMouseLeave: () => setIsOverlayOpen(false), children: [_jsx("button", { type: "button", "aria-label": tooltipText, onClick: aggregate === 'executing' ? sendInterrupt : undefined, onFocus: () => setIsOverlayOpen(true), onBlur: () => setIsOverlayOpen(false), style: {
|
|
175
|
+
display: 'inline-flex',
|
|
176
|
+
alignItems: 'center',
|
|
177
|
+
justifyContent: 'center',
|
|
178
|
+
width: 28,
|
|
179
|
+
height: 28,
|
|
180
|
+
padding: 0,
|
|
181
|
+
border: 'none',
|
|
182
|
+
background: 'none',
|
|
183
|
+
outline: 'none',
|
|
184
|
+
boxShadow: 'none',
|
|
185
|
+
borderRadius: 0,
|
|
186
|
+
WebkitAppearance: 'none',
|
|
187
|
+
appearance: 'none',
|
|
188
|
+
cursor: aggregate === 'executing' ? 'pointer' : 'default',
|
|
189
|
+
lineHeight: 0,
|
|
190
|
+
}, children: _jsx(Box, { as: "span", sx: { display: 'inline-flex', alignItems: 'center', flexShrink: 0 }, children: renderSandboxGlyph(aggregate) }) }), isOverlayOpen && (_jsxs(Box, { role: "tooltip", sx: {
|
|
191
|
+
position: 'absolute',
|
|
192
|
+
right: 0,
|
|
193
|
+
top: 'calc(100% + 6px)',
|
|
194
|
+
minWidth: 220,
|
|
195
|
+
px: 2,
|
|
196
|
+
py: 2,
|
|
197
|
+
borderRadius: 2,
|
|
198
|
+
bg: 'canvas.overlay',
|
|
199
|
+
color: 'fg.default',
|
|
200
|
+
fontSize: 0,
|
|
201
|
+
lineHeight: 1.5,
|
|
202
|
+
boxShadow: 'shadow.medium',
|
|
203
|
+
border: '1px solid',
|
|
204
|
+
borderColor: 'border.default',
|
|
205
|
+
zIndex: 1000,
|
|
206
|
+
fontFamily: 'mono',
|
|
207
|
+
pointerEvents: 'none',
|
|
208
|
+
}, children: [_jsx(Box, { sx: { mb: 1, fontWeight: 600, fontFamily: 'normal' }, children: tooltipText }), effectiveStatus ? (_jsxs(_Fragment, { children: [_jsxs(Box, { children: [_jsxs(Box, { as: "span", sx: { fontWeight: 600 }, children: ["variant:", ' '] }), _jsx(Box, { as: "span", children: effectiveStatus.variant })] }), _jsxs(Box, { children: [_jsxs(Box, { as: "span", sx: { fontWeight: 600 }, children: ["sandbox_running:", ' '] }), _jsx(Box, { as: "span", children: String(effectiveStatus.sandbox_running) })] }), _jsxs(Box, { children: [_jsxs(Box, { as: "span", sx: { fontWeight: 600 }, children: ["is_executing:", ' '] }), _jsx(Box, { as: "span", children: String(effectiveStatus.is_executing) })] }), effectiveStatus.jupyter_url && (_jsxs(Box, { sx: { wordBreak: 'break-all' }, children: [_jsxs(Box, { as: "span", sx: { fontWeight: 600 }, children: ["jupyter_url:", ' '] }), _jsx(Box, { as: "span", children: effectiveStatus.jupyter_url })] })), effectiveStatus.error && (_jsx(Box, { sx: { mt: 1, color: 'danger.fg' }, children: effectiveStatus.error })), aggregate === 'executing' && (_jsx(Box, { sx: { mt: 1, fontFamily: 'normal', color: 'fg.muted' }, children: "Click to interrupt execution" }))] })) : (_jsx(Box, { sx: { fontFamily: 'normal', color: 'fg.muted' }, children: "No sandbox configured for this agent." }))] }))] }));
|
|
174
209
|
}
|
|
175
210
|
export default SandboxStatusIndicator;
|
|
@@ -26,6 +26,50 @@ function normalizeName(name) {
|
|
|
26
26
|
.replace(/[-_]/g, '')
|
|
27
27
|
.toLowerCase();
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Expand a single-line markdown table (rows joined with `||`) into one row
|
|
31
|
+
* per line so a markdown renderer can produce a real HTML table. Idempotent:
|
|
32
|
+
* any pre-existing `|\n|` boundaries are unaffected.
|
|
33
|
+
*/
|
|
34
|
+
function expandCompactMarkdownTableBody(body) {
|
|
35
|
+
if (!body)
|
|
36
|
+
return body;
|
|
37
|
+
if (!/\|\s*[-:| ]{3,}\|/.test(body))
|
|
38
|
+
return body;
|
|
39
|
+
if (!body.includes('||'))
|
|
40
|
+
return body;
|
|
41
|
+
return body.replace(/\|\|/g, '|\n|');
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Pre-process assistant text for Streamdown:
|
|
45
|
+
* - Unwrap ```markdown / ```md fenced blocks so tables render as HTML
|
|
46
|
+
* instead of as a code block.
|
|
47
|
+
* - Inside any fenced block (or top-level body), expand compact one-line
|
|
48
|
+
* markdown tables.
|
|
49
|
+
*/
|
|
50
|
+
function normalizeAssistantMarkdown(text) {
|
|
51
|
+
if (!text)
|
|
52
|
+
return text;
|
|
53
|
+
// Unwrap explicit ```markdown / ```md fences and expand compact tables.
|
|
54
|
+
let out = text.replace(/```(markdown|md)\s*\n([\s\S]*?)```/gi, (_match, _lang, body) => {
|
|
55
|
+
const expanded = expandCompactMarkdownTableBody(body.trim());
|
|
56
|
+
return `\n\n${expanded}\n\n`;
|
|
57
|
+
});
|
|
58
|
+
// Also expand compact tables inside other fenced blocks that happen to
|
|
59
|
+
// carry a markdown table (e.g. bare ``` fences).
|
|
60
|
+
out = out.replace(/```([a-zA-Z0-9_+-]*)\n([\s\S]*?)```/g, (match, lang, body) => {
|
|
61
|
+
const expanded = expandCompactMarkdownTableBody(body);
|
|
62
|
+
if (expanded === body)
|
|
63
|
+
return match;
|
|
64
|
+
// If we expanded an unlabeled fence containing a real markdown table,
|
|
65
|
+
// unwrap it so it renders as a table.
|
|
66
|
+
if (!lang || /^(text|plain)$/i.test(lang)) {
|
|
67
|
+
return `\n\n${expanded.trim()}\n\n`;
|
|
68
|
+
}
|
|
69
|
+
return `\`\`\`${lang}\n${expanded}\`\`\``;
|
|
70
|
+
});
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
29
73
|
// ---------------------------------------------------------------------------
|
|
30
74
|
// DefaultToolCallRenderer — reads approvals from the Zustand store,
|
|
31
75
|
// sends decisions over the shared WebSocket (no REST polling).
|
|
@@ -266,6 +310,7 @@ export function ChatMessageList({ displayItems, isLoading, isStreaming, showLoad
|
|
|
266
310
|
: avatarConfig.assistantAvatar })), _jsx(Box, { sx: {
|
|
267
311
|
maxWidth: '85%',
|
|
268
312
|
p: 2,
|
|
313
|
+
overflowX: 'auto',
|
|
269
314
|
borderRadius: 2,
|
|
270
315
|
backgroundColor: isUser ? 'accent.emphasis' : 'canvas.subtle',
|
|
271
316
|
// Use primary-button text token for better contrast when
|
|
@@ -278,7 +323,7 @@ export function ChatMessageList({ displayItems, isLoading, isStreaming, showLoad
|
|
|
278
323
|
fontSize: 1,
|
|
279
324
|
whiteSpace: 'pre-wrap',
|
|
280
325
|
wordBreak: 'break-word',
|
|
281
|
-
}, children: getMessageText(message) })) : (_jsx(Box, { sx: streamdownMarkdownStyles, children: _jsx(Streamdown, { children: getMessageText(message) || (isStreaming ? '...' : '') }) })) })] }) }, message.id));
|
|
326
|
+
}, children: getMessageText(message) })) : (_jsx(Box, { sx: streamdownMarkdownStyles, children: _jsx(Streamdown, { children: normalizeAssistantMarkdown(getMessageText(message) || (isStreaming ? '...' : '')) }) })) })] }) }, message.id));
|
|
282
327
|
}), showLoadingIndicator && (isLoading || isStreaming) && (_jsx(Box, { sx: {
|
|
283
328
|
display: 'flex',
|
|
284
329
|
alignItems: 'flex-start',
|
|
@@ -11,7 +11,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
11
11
|
*/
|
|
12
12
|
import { useRef, useEffect } from 'react';
|
|
13
13
|
import { Text, RelativeTime } from '@primer/react';
|
|
14
|
-
import { Box } from '@datalayer/primer-addons';
|
|
14
|
+
import { Box, useThemeStore, getColorPalette } from '@datalayer/primer-addons';
|
|
15
15
|
import { PersonIcon, ToolsIcon } from '@primer/octicons-react';
|
|
16
16
|
import { AiAgentIcon } from '@datalayer/icons-react';
|
|
17
17
|
import { useChatMessages, useChatExtensionRegistry, } from '../../stores/chatStore';
|
|
@@ -24,6 +24,10 @@ export function ChatMessages({ messageRenderer, activityRenderer, showTimestamps
|
|
|
24
24
|
const extensionRegistry = extensionRegistryProp ?? storeExtensionRegistry;
|
|
25
25
|
const containerRef = useRef(null);
|
|
26
26
|
const lastMessageRef = useRef(null);
|
|
27
|
+
// Resolve a light color from the current user theme palette so the
|
|
28
|
+
// assistant icon contrasts the saturated `accent.emphasis` avatar bg.
|
|
29
|
+
const { theme, colorMode } = useThemeStore();
|
|
30
|
+
const assistantIconColor = getColorPalette(theme, colorMode).primary;
|
|
27
31
|
// Auto-scroll to bottom on new messages
|
|
28
32
|
useEffect(() => {
|
|
29
33
|
if (autoScroll && lastMessageRef.current) {
|
|
@@ -73,7 +77,7 @@ export function ChatMessages({ messageRenderer, activityRenderer, showTimestamps
|
|
|
73
77
|
color: message.role === 'user'
|
|
74
78
|
? 'fg.default'
|
|
75
79
|
: 'var(--button-primary-fgColor-rest, var(--fgColor-onEmphasis))',
|
|
76
|
-
}, children: message.role === 'user' ? (_jsx(PersonIcon, { size: 16 })) : message.role === 'assistant' ? (_jsx(AiAgentIcon, { size: 16 })) : (_jsx(ToolsIcon, { size: 16 })) }) })), _jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [_jsxs(Box, { sx: {
|
|
80
|
+
}, children: message.role === 'user' ? (_jsx(PersonIcon, { size: 16 })) : message.role === 'assistant' ? (_jsx(AiAgentIcon, { size: 16, color: assistantIconColor })) : (_jsx(ToolsIcon, { size: 16 })) }) })), _jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [_jsxs(Box, { sx: {
|
|
77
81
|
display: 'flex',
|
|
78
82
|
alignItems: 'center',
|
|
79
83
|
gap: 2,
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import type { KernelMessage } from '@jupyterlab/services';
|
|
1
2
|
import type { BuiltinTool, ContextSnapshotData, MCPServerConfig, McpToolsetsStatusResponse, ModelConfig, SkillInfo } from '../../types';
|
|
2
3
|
export interface InputToolbarProps {
|
|
3
4
|
input: string;
|
|
4
5
|
setInput: (value: string) => void;
|
|
5
6
|
isLoading: boolean;
|
|
7
|
+
kernelStatus?: KernelMessage.Status;
|
|
6
8
|
connectionConfirmed: boolean;
|
|
7
9
|
placeholder?: string;
|
|
8
10
|
autoFocus: boolean;
|
|
@@ -53,4 +55,4 @@ export interface InputToolbarProps {
|
|
|
53
55
|
/** Pre-fetched MCP status from WebSocket — bypasses REST polling */
|
|
54
56
|
mcpStatusData?: McpToolsetsStatusResponse | null;
|
|
55
57
|
}
|
|
56
|
-
export declare function InputToolbar({ input, setInput, isLoading, connectionConfirmed, placeholder, autoFocus, focusTrigger, padding, onSend, onStop, disableInputPrompt, showTokenUsage, agentUsage, showModelSelector, showToolsMenu, showSkillsMenu, codemodeEnabled, onToggleCodemode, isA2AProtocol, hasConfigData, hasSkillsData, models, selectedModel, onModelSelect, availableTools, mcpServers, enabledMcpTools, enabledMcpToolCount, onToggleMcpTool, onToggleAllMcpServerTools, approvedMcpTools, onToggleMcpToolApproval, skills, skillsLoading, enabledSkills, onToggleSkill, onToggleAllSkills, approvedSkills, onToggleSkillApproval, apiBase, authToken, mcpStatusData, }: InputToolbarProps): import("react/jsx-runtime").JSX.Element;
|
|
58
|
+
export declare function InputToolbar({ input, setInput, isLoading, kernelStatus, connectionConfirmed, placeholder, autoFocus, focusTrigger, padding, onSend, onStop, disableInputPrompt, showTokenUsage, agentUsage, showModelSelector, showToolsMenu, showSkillsMenu, codemodeEnabled, onToggleCodemode, isA2AProtocol, hasConfigData, hasSkillsData, models, selectedModel, onModelSelect, availableTools, mcpServers, enabledMcpTools, enabledMcpToolCount, onToggleMcpTool, onToggleAllMcpServerTools, approvedMcpTools, onToggleMcpToolApproval, skills, skillsLoading, enabledSkills, onToggleSkill, onToggleAllSkills, approvedSkills, onToggleSkillApproval, apiBase, authToken, mcpStatusData, }: InputToolbarProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -21,20 +21,23 @@ import { SkillsStatusIndicator } from '../indicators/SkillsStatusIndicator';
|
|
|
21
21
|
// ---------------------------------------------------------------------------
|
|
22
22
|
// Component
|
|
23
23
|
// ---------------------------------------------------------------------------
|
|
24
|
-
export function InputToolbar({ input, setInput, isLoading, connectionConfirmed, placeholder, autoFocus, focusTrigger, padding, onSend, onStop, disableInputPrompt = false, showTokenUsage, agentUsage, showModelSelector, showToolsMenu, showSkillsMenu, codemodeEnabled, onToggleCodemode, isA2AProtocol, hasConfigData, hasSkillsData, models, selectedModel, onModelSelect, availableTools, mcpServers, enabledMcpTools, enabledMcpToolCount, onToggleMcpTool, onToggleAllMcpServerTools, approvedMcpTools, onToggleMcpToolApproval, skills, skillsLoading, enabledSkills, onToggleSkill, onToggleAllSkills, approvedSkills, onToggleSkillApproval, apiBase, authToken, mcpStatusData, }) {
|
|
24
|
+
export function InputToolbar({ input, setInput, isLoading, kernelStatus, connectionConfirmed, placeholder, autoFocus, focusTrigger, padding, onSend, onStop, disableInputPrompt = false, showTokenUsage, agentUsage, showModelSelector, showToolsMenu, showSkillsMenu, codemodeEnabled, onToggleCodemode, isA2AProtocol, hasConfigData, hasSkillsData, models, selectedModel, onModelSelect, availableTools, mcpServers, enabledMcpTools, enabledMcpToolCount, onToggleMcpTool, onToggleAllMcpServerTools, approvedMcpTools, onToggleMcpToolApproval, skills, skillsLoading, enabledSkills, onToggleSkill, onToggleAllSkills, approvedSkills, onToggleSkillApproval, apiBase, authToken, mcpStatusData, }) {
|
|
25
|
+
const isKernelBusy = kernelStatus === 'busy';
|
|
26
|
+
const showSelectorsBar = showModelSelector || showToolsMenu || showSkillsMenu;
|
|
27
|
+
const hasSelectorsContent = hasConfigData || hasSkillsData;
|
|
25
28
|
// Show token usage when we have valid context data
|
|
26
29
|
const hasContext = Boolean(agentUsage && !agentUsage.error && agentUsage.totalTokens > 0);
|
|
27
|
-
return (_jsxs(Box, { children: [_jsx(InputPrompt, { placeholder: placeholder || 'Type a message...', isLoading: isLoading, disabled: disableInputPrompt, readOnly: !connectionConfirmed, onSend: onSend, onStop: onStop, autoFocus: autoFocus, focusTrigger: focusTrigger, padding: padding, value: input, onChange: setInput, footerRightContent: _jsxs(_Fragment, { children: [_jsx(McpStatusIndicator, { apiBase: apiBase, authToken: authToken, data: mcpStatusData }), _jsx(SkillsStatusIndicator, { skillsCount: skills.length, enabledCount: enabledSkills.size, loading: skillsLoading })] }) }), showTokenUsage && hasContext && agentUsage && (_jsx(TokenUsageBar, { agentUsage: agentUsage, padding: padding })),
|
|
28
|
-
(hasConfigData || hasSkillsData) && (_jsxs(Box, { sx: {
|
|
30
|
+
return (_jsxs(Box, { children: [_jsx(InputPrompt, { placeholder: placeholder || 'Type a message...', isLoading: isLoading, isKernelBusy: isKernelBusy, disabled: disableInputPrompt, readOnly: !connectionConfirmed, onSend: onSend, onStop: onStop, autoFocus: autoFocus, focusTrigger: focusTrigger, padding: padding, value: input, onChange: setInput, footerRightContent: _jsxs(_Fragment, { children: [_jsx(McpStatusIndicator, { apiBase: apiBase, authToken: authToken, data: mcpStatusData }), _jsx(SkillsStatusIndicator, { skillsCount: skills.length, enabledCount: enabledSkills.size, loading: skillsLoading })] }) }), showTokenUsage && (_jsx(Box, { sx: { minHeight: hasContext && agentUsage ? 28 : 8 }, children: hasContext && agentUsage ? (_jsx(TokenUsageBar, { agentUsage: agentUsage, padding: padding })) : null })), showSelectorsBar && (_jsx(Box, { sx: {
|
|
29
31
|
display: 'flex',
|
|
30
32
|
gap: 2,
|
|
31
33
|
px: padding,
|
|
32
|
-
py:
|
|
34
|
+
py: 0.5,
|
|
35
|
+
minHeight: 36,
|
|
33
36
|
borderTop: '1px solid',
|
|
34
37
|
borderColor: 'border.default',
|
|
35
38
|
alignItems: 'center',
|
|
36
39
|
bg: 'canvas.subtle',
|
|
37
|
-
}, children: [showToolsMenu && (_jsx(ToolsMenu, { codemodeEnabled: codemodeEnabled, onToggleCodemode: onToggleCodemode, mcpServers: mcpServers, enabledMcpTools: enabledMcpTools, enabledMcpToolCount: enabledMcpToolCount, onToggleMcpTool: onToggleMcpTool, onToggleAllMcpServerTools: onToggleAllMcpServerTools, approvedMcpTools: approvedMcpTools, onToggleMcpToolApproval: onToggleMcpToolApproval, availableTools: availableTools })), showSkillsMenu && (_jsx(SkillsMenu, { skills: skills, skillsLoading: skillsLoading, enabledSkills: enabledSkills, onToggleSkill: onToggleSkill, onToggleAllSkills: onToggleAllSkills, approvedSkills: approvedSkills, onToggleSkillApproval: onToggleSkillApproval })), showModelSelector && models.length > 0 && selectedModel && (_jsx(ModelSelector, { models: models, selectedModel: selectedModel, onModelSelect: onModelSelect, isA2AProtocol: isA2AProtocol }))] }))] }));
|
|
40
|
+
}, children: hasSelectorsContent ? (_jsxs(_Fragment, { children: [showToolsMenu && (_jsx(ToolsMenu, { codemodeEnabled: codemodeEnabled, onToggleCodemode: onToggleCodemode, mcpServers: mcpServers, enabledMcpTools: enabledMcpTools, enabledMcpToolCount: enabledMcpToolCount, onToggleMcpTool: onToggleMcpTool, onToggleAllMcpServerTools: onToggleAllMcpServerTools, approvedMcpTools: approvedMcpTools, onToggleMcpToolApproval: onToggleMcpToolApproval, availableTools: availableTools })), showSkillsMenu && (_jsx(SkillsMenu, { skills: skills, skillsLoading: skillsLoading, enabledSkills: enabledSkills, onToggleSkill: onToggleSkill, onToggleAllSkills: onToggleAllSkills, approvedSkills: approvedSkills, onToggleSkillApproval: onToggleSkillApproval })), showModelSelector && models.length > 0 && selectedModel && (_jsx(ModelSelector, { models: models, selectedModel: selectedModel, onModelSelect: onModelSelect, isA2AProtocol: isA2AProtocol }))] })) : (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Loading controls..." })) }))] }));
|
|
38
41
|
}
|
|
39
42
|
// ---------------------------------------------------------------------------
|
|
40
43
|
// ToolsMenu (private sub-component)
|
|
@@ -24,6 +24,8 @@ export interface InputPromptProps {
|
|
|
24
24
|
placeholder?: string;
|
|
25
25
|
/** Whether the agent is loading / streaming */
|
|
26
26
|
isLoading?: boolean;
|
|
27
|
+
/** Whether the connected kernel is currently busy */
|
|
28
|
+
isKernelBusy?: boolean;
|
|
27
29
|
/** Callback when a message is submitted */
|
|
28
30
|
onSend: (message: string) => void;
|
|
29
31
|
/** Callback when the stop button is clicked */
|
|
@@ -58,5 +60,5 @@ export interface InputPromptProps {
|
|
|
58
60
|
/**
|
|
59
61
|
* InputPrompt — Integrated chat input with header, input area, and footer.
|
|
60
62
|
*/
|
|
61
|
-
export declare function InputPrompt({ variant, placeholder, isLoading, onSend, onStop, autoFocus, focusTrigger, showBorderTop, showBackground, padding, disabled, readOnly, sx, value: controlledValue, onChange: controlledOnChange, headerContent, footerContent, footerRightContent, }: InputPromptProps): import("react/jsx-runtime").JSX.Element;
|
|
63
|
+
export declare function InputPrompt({ variant, placeholder, isLoading, isKernelBusy, onSend, onStop, autoFocus, focusTrigger, showBorderTop, showBackground, padding, disabled, readOnly, sx, value: controlledValue, onChange: controlledOnChange, headerContent, footerContent, footerRightContent, }: InputPromptProps): import("react/jsx-runtime").JSX.Element;
|
|
62
64
|
export default InputPrompt;
|
|
@@ -8,7 +8,7 @@ import { InputPromptLexical } from './InputPromptLexical';
|
|
|
8
8
|
/**
|
|
9
9
|
* InputPrompt — Integrated chat input with header, input area, and footer.
|
|
10
10
|
*/
|
|
11
|
-
export function InputPrompt({ variant = 'text', placeholder = 'Ask anything…', isLoading = false, onSend, onStop, autoFocus = false, focusTrigger, showBorderTop = true, showBackground = true, padding = 3, disabled = false, readOnly = false, sx, value: controlledValue, onChange: controlledOnChange, headerContent, footerContent, footerRightContent, }) {
|
|
11
|
+
export function InputPrompt({ variant = 'text', placeholder = 'Ask anything…', isLoading = false, isKernelBusy = false, onSend, onStop, autoFocus = false, focusTrigger, showBorderTop = true, showBackground = true, padding = 3, disabled = false, readOnly = false, sx, value: controlledValue, onChange: controlledOnChange, headerContent, footerContent, footerRightContent, }) {
|
|
12
12
|
// ---- Controlled / uncontrolled state -----------------------------------
|
|
13
13
|
const [internalInput, setInternalInput] = useState('');
|
|
14
14
|
const input = controlledValue !== undefined ? controlledValue : internalInput;
|
|
@@ -78,6 +78,6 @@ export function InputPrompt({ variant = 'text', placeholder = 'Ask anything…',
|
|
|
78
78
|
borderColor: 'accent.fg',
|
|
79
79
|
boxShadow: (t) => `0 0 0 1px ${t?.colors?.accent?.fg ?? '#0969da'}`,
|
|
80
80
|
},
|
|
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, sendDisabled: !input.trim() || disabled || readOnly, onSend: handleSend, onStop: handleStop, rightContent: footerRightContent, children: footerContent })] }) }) }));
|
|
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
|
}
|
|
83
83
|
export default InputPrompt;
|
|
@@ -16,10 +16,12 @@ export interface InputPromptFooterProps {
|
|
|
16
16
|
isLoading?: boolean;
|
|
17
17
|
/** Whether the send button should be disabled */
|
|
18
18
|
sendDisabled?: boolean;
|
|
19
|
+
/** Whether the connected kernel is currently busy */
|
|
20
|
+
isKernelBusy?: boolean;
|
|
19
21
|
/** Callback when the send button is clicked */
|
|
20
22
|
onSend: () => void;
|
|
21
23
|
/** Callback when the stop button is clicked */
|
|
22
24
|
onStop?: () => void;
|
|
23
25
|
}
|
|
24
|
-
export declare function InputPromptFooter({ children, rightContent, isLoading, sendDisabled, onSend, onStop, }: InputPromptFooterProps): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
export declare function InputPromptFooter({ children, rightContent, isLoading, sendDisabled, isKernelBusy, onSend, onStop, }: InputPromptFooterProps): import("react/jsx-runtime").JSX.Element;
|
|
25
27
|
export default InputPromptFooter;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { IconButton } from '@primer/react';
|
|
3
3
|
import { Box } from '@datalayer/primer-addons';
|
|
4
|
-
import { PaperAirplaneIcon, SquareCircleIcon } from '@primer/octicons-react';
|
|
5
|
-
export function InputPromptFooter({ children, rightContent, isLoading = false, sendDisabled = false, onSend, onStop, }) {
|
|
4
|
+
import { PaperAirplaneIcon, SquareCircleIcon, PauseIcon, } from '@primer/octicons-react';
|
|
5
|
+
export function InputPromptFooter({ children, rightContent, isLoading = false, sendDisabled = false, isKernelBusy = false, onSend, onStop, }) {
|
|
6
6
|
return (_jsxs(Box, { sx: {
|
|
7
7
|
display: 'flex',
|
|
8
8
|
alignItems: 'center',
|
|
@@ -10,6 +10,6 @@ export function InputPromptFooter({ children, rightContent, isLoading = false, s
|
|
|
10
10
|
px: 2,
|
|
11
11
|
pt: 1,
|
|
12
12
|
pb: 2,
|
|
13
|
-
}, children: [_jsx(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2, flex: 1 }, children: children }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: [rightContent, isLoading ? (_jsx(IconButton, { icon: SquareCircleIcon, "aria-label": "Stop", onClick: onStop, size: "small", variant: "invisible" })) : (_jsx(IconButton, { icon: PaperAirplaneIcon, "aria-label": "Send", onClick: onSend, disabled: sendDisabled, size: "small", variant: "invisible" }))] })] }));
|
|
13
|
+
}, children: [_jsx(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2, flex: 1 }, children: children }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: [rightContent, isLoading ? (_jsx(IconButton, { icon: SquareCircleIcon, "aria-label": "Stop", onClick: onStop, size: "small", variant: "invisible" })) : isKernelBusy ? (_jsx(IconButton, { icon: PauseIcon, "aria-label": "Pause (kernel busy)", onClick: onStop, size: "small", variant: "invisible", disabled: !onStop })) : (_jsx(IconButton, { icon: PaperAirplaneIcon, "aria-label": "Send", onClick: onSend, disabled: sendDisabled, size: "small", variant: "invisible" }))] })] }));
|
|
14
14
|
}
|
|
15
15
|
export default InputPromptFooter;
|