@datalayer/agent-runtimes 1.0.4 → 1.0.5
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 +34 -0
- package/lib/App.js +1 -1
- package/lib/agents/AgentDetails.d.ts +22 -1
- package/lib/agents/AgentDetails.js +34 -47
- package/lib/api/index.d.ts +0 -1
- package/lib/api/index.js +4 -2
- package/lib/chat/Chat.d.ts +5 -106
- package/lib/chat/Chat.js +4 -4
- package/lib/chat/ChatFloating.d.ts +7 -140
- package/lib/chat/ChatFloating.js +2 -2
- package/lib/chat/ChatPopupStandalone.d.ts +8 -47
- package/lib/chat/ChatPopupStandalone.js +3 -3
- package/lib/chat/ChatSidebar.d.ts +4 -69
- package/lib/chat/ChatSidebar.js +2 -2
- package/lib/chat/ChatStandalone.d.ts +4 -54
- package/lib/chat/ChatStandalone.js +3 -3
- package/lib/chat/base/ChatBase.js +1083 -157
- package/lib/chat/header/ChatHeaderBase.d.ts +11 -6
- package/lib/chat/header/ChatHeaderBase.js +18 -16
- package/lib/chat/indicators/McpStatusIndicator.d.ts +7 -4
- package/lib/chat/indicators/McpStatusIndicator.js +7 -32
- package/lib/chat/indicators/SandboxStatusIndicator.d.ts +4 -1
- package/lib/chat/indicators/SandboxStatusIndicator.js +9 -9
- package/lib/chat/indicators/SkillsStatusIndicator.d.ts +7 -0
- package/lib/chat/indicators/SkillsStatusIndicator.js +88 -0
- package/lib/chat/indicators/index.d.ts +1 -0
- package/lib/chat/indicators/index.js +1 -0
- package/lib/chat/messages/ChatMessageList.d.ts +1 -1
- package/lib/chat/messages/ChatMessageList.js +108 -113
- package/lib/chat/prompt/InputFooter.d.ts +19 -6
- package/lib/chat/prompt/InputFooter.js +71 -18
- package/lib/chat/prompt/InputPrompt.d.ts +3 -1
- package/lib/chat/prompt/InputPrompt.js +4 -4
- package/lib/chat/prompt/InputPromptFooter.js +1 -1
- package/lib/chat/prompt/InputPromptLexical.d.ts +3 -1
- package/lib/chat/prompt/InputPromptLexical.js +12 -5
- package/lib/chat/prompt/InputPromptText.d.ts +3 -1
- package/lib/chat/prompt/InputPromptText.js +2 -2
- package/lib/chat/tools/ToolApprovalBanner.js +1 -1
- package/lib/chat/tools/ToolCallDisplay.d.ts +3 -1
- package/lib/chat/tools/ToolCallDisplay.js +2 -2
- package/lib/chat/usage/TokenUsageBar.js +20 -2
- package/lib/client/AgentRuntimesClientContext.d.ts +53 -0
- package/lib/client/AgentRuntimesClientContext.js +55 -0
- package/lib/client/AgentsMixin.d.ts +0 -18
- package/lib/client/AgentsMixin.js +6 -30
- package/lib/client/IAgentRuntimesClient.d.ts +215 -0
- package/lib/client/IAgentRuntimesClient.js +5 -0
- package/lib/client/SdkAgentRuntimesClient.d.ts +151 -0
- package/lib/client/SdkAgentRuntimesClient.js +134 -0
- package/lib/client/index.d.ts +4 -1
- package/lib/client/index.js +3 -1
- package/lib/components/NotificationEventCard.js +5 -1
- package/lib/config/AgentConfiguration.js +3 -3
- package/lib/context/ContextDistribution.d.ts +3 -1
- package/lib/context/ContextDistribution.js +8 -27
- package/lib/context/ContextInspector.d.ts +3 -1
- package/lib/context/ContextInspector.js +19 -67
- package/lib/context/ContextPanel.d.ts +3 -1
- package/lib/context/ContextPanel.js +104 -64
- package/lib/context/ContextUsage.d.ts +3 -1
- package/lib/context/ContextUsage.js +3 -3
- package/lib/context/CostTracker.d.ts +9 -3
- package/lib/context/CostTracker.js +26 -47
- package/lib/context/CostUsageChart.d.ts +12 -0
- package/lib/context/CostUsageChart.js +378 -0
- package/lib/context/GraphFlowChart.d.ts +16 -0
- package/lib/context/GraphFlowChart.js +182 -0
- package/lib/context/TokenUsageChart.d.ts +8 -1
- package/lib/context/TokenUsageChart.js +349 -211
- package/lib/context/TurnGraphChart.d.ts +39 -0
- package/lib/context/TurnGraphChart.js +538 -0
- package/lib/context/otelWsPool.d.ts +20 -0
- package/lib/context/otelWsPool.js +69 -0
- package/lib/examples/A2UiComponentGalleryExample.d.ts +0 -17
- package/lib/examples/A2UiComponentGalleryExample.js +315 -522
- package/lib/examples/A2UiContactCardExample.d.ts +0 -18
- package/lib/examples/A2UiContactCardExample.js +154 -411
- package/lib/examples/A2UiRestaurantExample.d.ts +0 -30
- package/lib/examples/A2UiRestaurantExample.js +114 -212
- package/lib/examples/A2UiViewerExample.d.ts +0 -18
- package/lib/examples/A2UiViewerExample.js +283 -532
- package/lib/examples/AgUiBackendToolRenderingExample.js +1 -1
- package/lib/examples/AgUiHaikuGenUiExample.d.ts +1 -1
- package/lib/examples/AgUiHaikuGenUiExample.js +1 -1
- package/lib/examples/AgentCheckpointsExample.js +13 -27
- package/lib/examples/AgentCodemodeExample.d.ts +4 -6
- package/lib/examples/AgentCodemodeExample.js +591 -169
- package/lib/examples/AgentEvalsExample.js +12 -16
- package/lib/examples/AgentGuardrailsExample.js +370 -64
- package/lib/examples/AgentHooksExample.d.ts +3 -0
- package/lib/examples/AgentHooksExample.js +104 -0
- package/lib/examples/AgentMCPExample.d.ts +3 -0
- package/lib/examples/AgentMCPExample.js +480 -0
- package/lib/examples/AgentMemoryExample.js +13 -17
- package/lib/examples/AgentMonitoringExample.js +260 -199
- package/lib/examples/AgentNotificationsExample.js +49 -17
- package/lib/examples/AgentOtelExample.js +2 -3
- package/lib/examples/AgentOutputsExample.d.ts +11 -6
- package/lib/examples/AgentOutputsExample.js +382 -81
- package/lib/examples/AgentParametersExample.d.ts +3 -0
- package/lib/examples/AgentParametersExample.js +246 -0
- package/lib/examples/AgentSandboxExample.d.ts +2 -2
- package/lib/examples/AgentSandboxExample.js +68 -40
- package/lib/examples/AgentSkillsExample.js +91 -99
- package/lib/examples/{AgentspecExample.js → AgentSpecsExample.js} +10 -21
- package/lib/examples/AgentSubagentsExample.d.ts +14 -0
- package/lib/examples/AgentSubagentsExample.js +228 -0
- package/lib/examples/AgentToolApprovalsExample.js +29 -557
- package/lib/examples/AgentTriggersExample.js +819 -565
- package/lib/examples/ChatCustomExample.js +11 -24
- package/lib/examples/ChatExample.js +7 -24
- package/lib/examples/CopilotKitLexicalExample.js +2 -1
- package/lib/examples/CopilotKitNotebookExample.js +2 -1
- package/lib/examples/HomeExample.d.ts +15 -0
- package/lib/examples/HomeExample.js +77 -0
- package/lib/examples/Lexical2Example.js +4 -2
- package/lib/examples/{LexicalExample.d.ts → LexicalAgentExample.d.ts} +4 -4
- package/lib/examples/{LexicalExample.js → LexicalAgentExample.js} +65 -16
- package/lib/examples/{LexicalSidebarExample.d.ts → LexicalAgentSidebarExample.d.ts} +5 -5
- package/lib/examples/LexicalAgentSidebarExample.js +261 -0
- package/lib/examples/NotebookAgentExample.d.ts +9 -0
- package/lib/examples/NotebookAgentExample.js +192 -0
- package/lib/examples/{NotebookSidebarExample.d.ts → NotebookAgentSidebarExample.d.ts} +2 -2
- package/lib/examples/NotebookAgentSidebarExample.js +221 -0
- package/lib/examples/{DatalayerNotebookExample.d.ts → NotebookCollaborationExample.d.ts} +4 -4
- package/lib/examples/{DatalayerNotebookExample.js → NotebookCollaborationExample.js} +3 -3
- package/lib/examples/NotebookExample.d.ts +4 -7
- package/lib/examples/NotebookExample.js +14 -146
- package/lib/examples/components/AuthRequiredView.d.ts +6 -0
- package/lib/examples/components/AuthRequiredView.js +33 -0
- package/lib/examples/components/ExampleWrapper.d.ts +7 -0
- package/lib/examples/components/ExampleWrapper.js +25 -6
- package/lib/examples/{ag-ui → components}/haiku/HaikuDisplay.js +1 -1
- package/lib/examples/{ag-ui → components}/haiku/InlineHaikuCard.js +1 -1
- package/lib/examples/{ag-ui → components}/haiku/index.d.ts +1 -1
- package/lib/examples/{ag-ui → components}/haiku/index.js +1 -1
- package/lib/examples/components/index.d.ts +3 -0
- package/lib/examples/components/index.js +4 -0
- package/lib/examples/{ag-ui → components}/weather/index.d.ts +1 -1
- package/lib/examples/{ag-ui → components}/weather/index.js +1 -1
- package/lib/examples/example-selector.d.ts +17 -4
- package/lib/examples/example-selector.js +107 -41
- package/lib/examples/index.d.ts +9 -6
- package/lib/examples/index.js +9 -6
- package/lib/examples/main.js +217 -27
- package/lib/examples/utils/a2ui.d.ts +18 -0
- package/lib/examples/utils/a2ui.js +69 -0
- package/lib/examples/utils/a2uiMarkdownProvider.d.ts +7 -0
- package/lib/examples/utils/a2uiMarkdownProvider.js +9 -0
- package/lib/examples/utils/agentId.d.ts +18 -0
- package/lib/examples/utils/agentId.js +54 -0
- package/lib/examples/utils/agents/earthquake-detector.json +11 -11
- package/lib/examples/utils/agents/sales-forecaster.json +11 -11
- package/lib/examples/utils/agents/social-post-generator.json +11 -11
- package/lib/examples/utils/agents/stock-market.json +11 -11
- package/lib/examples/utils/examplesStore.js +82 -27
- package/lib/hooks/index.d.ts +8 -8
- package/lib/hooks/index.js +7 -7
- package/lib/hooks/useA2A.d.ts +2 -3
- package/lib/hooks/useAIAgentsWebSocket.d.ts +43 -4
- package/lib/hooks/useAIAgentsWebSocket.js +118 -12
- package/lib/hooks/useAcp.d.ts +1 -2
- package/lib/hooks/useAgUi.d.ts +1 -1
- package/lib/hooks/{useAgents.d.ts → useAgentRuntimes.d.ts} +39 -2
- package/lib/hooks/{useAgents.js → useAgentRuntimes.js} +125 -15
- package/lib/hooks/useAgentsCatalog.js +1 -1
- package/lib/hooks/useAgentsService.d.ts +2 -2
- package/lib/hooks/useAgentsService.js +7 -7
- package/lib/hooks/useCheckpoints.js +1 -1
- package/lib/hooks/useConfig.d.ts +4 -1
- package/lib/hooks/useConfig.js +10 -3
- package/lib/hooks/useContextSnapshot.d.ts +9 -4
- package/lib/hooks/useContextSnapshot.js +9 -37
- package/lib/hooks/useMonitoring.js +3 -0
- package/lib/hooks/useSandbox.d.ts +20 -8
- package/lib/hooks/useSandbox.js +105 -40
- package/lib/hooks/useSkills.d.ts +23 -5
- package/lib/hooks/useSkills.js +94 -39
- package/lib/hooks/useToolApprovals.d.ts +60 -36
- package/lib/hooks/useToolApprovals.js +318 -69
- package/lib/hooks/useVercelAI.d.ts +1 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.js +1 -0
- package/lib/inference/index.d.ts +0 -1
- package/lib/middleware/index.d.ts +0 -1
- package/lib/protocols/AGUIAdapter.js +6 -0
- package/lib/protocols/VercelAIAdapter.d.ts +7 -0
- package/lib/protocols/VercelAIAdapter.js +59 -7
- package/lib/specs/agents/agents.d.ts +10 -0
- package/lib/specs/agents/agents.js +2139 -262
- package/lib/specs/agents/index.js +3 -1
- package/lib/specs/envvars.d.ts +1 -0
- package/lib/specs/envvars.js +38 -20
- package/lib/specs/evals.js +6 -6
- package/lib/specs/events.d.ts +3 -10
- package/lib/specs/events.js +127 -84
- package/lib/specs/frontendTools.js +2 -2
- package/lib/specs/guardrails.d.ts +0 -7
- package/lib/specs/guardrails.js +240 -159
- package/lib/specs/index.d.ts +1 -0
- package/lib/specs/index.js +1 -0
- package/lib/specs/mcpServers.js +35 -6
- package/lib/specs/memory.d.ts +0 -2
- package/lib/specs/memory.js +4 -17
- package/lib/specs/models.js +25 -5
- package/lib/specs/notifications.js +102 -18
- package/lib/specs/outputs.js +15 -9
- package/lib/specs/personas.d.ts +41 -0
- package/lib/specs/personas.js +168 -0
- package/lib/specs/skills.d.ts +2 -1
- package/lib/specs/skills.js +41 -23
- package/lib/specs/teams/index.js +3 -1
- package/lib/specs/teams/teams.js +468 -348
- package/lib/specs/tools.js +4 -4
- package/lib/specs/triggers.js +61 -11
- package/lib/stores/agentRuntimeStore.d.ts +204 -0
- package/lib/stores/agentRuntimeStore.js +636 -0
- package/lib/stores/index.d.ts +1 -1
- package/lib/stores/index.js +1 -1
- package/lib/tools/adapters/copilotkit/lexicalHooks.d.ts +1 -2
- package/lib/tools/adapters/copilotkit/lexicalHooks.js +1 -3
- package/lib/tools/adapters/copilotkit/notebookHooks.d.ts +1 -2
- package/lib/tools/adapters/copilotkit/notebookHooks.js +1 -3
- package/lib/tools/index.d.ts +0 -2
- package/lib/tools/index.js +0 -1
- package/lib/types/agentspecs.d.ts +50 -1
- package/lib/types/chat.d.ts +309 -8
- package/lib/types/context.d.ts +27 -0
- package/lib/types/cost.d.ts +2 -2
- package/lib/types/index.d.ts +2 -0
- package/lib/types/index.js +2 -0
- package/lib/types/mcp.d.ts +8 -0
- package/lib/types/models.d.ts +2 -2
- package/lib/types/personas.d.ts +25 -0
- package/lib/types/personas.js +5 -0
- package/lib/types/skills.d.ts +43 -1
- package/lib/types/stream.d.ts +110 -0
- package/lib/types/stream.js +36 -0
- package/lib/utils/utils.d.ts +9 -5
- package/lib/utils/utils.js +9 -5
- package/package.json +13 -9
- package/scripts/codegen/__pycache__/generate_agents.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_events.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/versioning.cpython-313.pyc +0 -0
- package/scripts/codegen/generate_agents.py +102 -6
- package/scripts/codegen/generate_events.py +35 -13
- package/scripts/codegen/generate_personas.py +319 -0
- package/scripts/codegen/generate_skills.py +9 -9
- package/scripts/sync-jupyter.sh +26 -7
- package/lib/api/tool-approvals.d.ts +0 -62
- package/lib/api/tool-approvals.js +0 -145
- package/lib/examples/LexicalSidebarExample.js +0 -163
- package/lib/examples/NotebookSidebarExample.js +0 -119
- package/lib/examples/NotebookSimpleExample.d.ts +0 -6
- package/lib/examples/NotebookSimpleExample.js +0 -22
- package/lib/examples/ag-ui/index.d.ts +0 -10
- package/lib/examples/ag-ui/index.js +0 -16
- package/lib/hooks/useAgentsRegistry.d.ts +0 -10
- package/lib/hooks/useAgentsRegistry.js +0 -20
- package/lib/stores/agentsStore.d.ts +0 -123
- package/lib/stores/agentsStore.js +0 -270
- /package/lib/examples/{AgentspecExample.d.ts → AgentSpecsExample.d.ts} +0 -0
- /package/lib/examples/{ag-ui → components}/haiku/HaikuDisplay.d.ts +0 -0
- /package/lib/examples/{ag-ui → components}/haiku/InlineHaikuCard.d.ts +0 -0
- /package/lib/examples/{ag-ui → components}/weather/InlineWeatherCard.d.ts +0 -0
- /package/lib/examples/{ag-ui → components}/weather/InlineWeatherCard.js +0 -0
|
@@ -9,19 +9,24 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { type ReactNode } from 'react';
|
|
11
11
|
import type { ChatViewMode, HeaderButtonsConfig } from '../../types/chat';
|
|
12
|
-
import type {
|
|
12
|
+
import type { SandboxWsStatus } from '../../types/sandbox';
|
|
13
13
|
export interface ChatBaseHeaderProps {
|
|
14
14
|
title?: string;
|
|
15
|
+
subtitle?: string;
|
|
15
16
|
brandIcon?: ReactNode;
|
|
16
17
|
headerContent?: ReactNode;
|
|
17
18
|
headerActions?: ReactNode;
|
|
18
19
|
showInformation?: boolean;
|
|
19
20
|
onInformationClick?: () => void;
|
|
20
21
|
padding: number;
|
|
21
|
-
/**
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
|
|
22
|
+
/** API base URL passed to SandboxStatusIndicator */
|
|
23
|
+
sandboxApiBase?: string;
|
|
24
|
+
/** Auth token passed to SandboxStatusIndicator */
|
|
25
|
+
sandboxAuthToken?: string;
|
|
26
|
+
/** Agent ID passed to SandboxStatusIndicator for agent-scoped status */
|
|
27
|
+
sandboxAgentId?: string;
|
|
28
|
+
/** Optional sandbox status override for immediate indicator updates */
|
|
29
|
+
sandboxStatusData?: SandboxWsStatus | null;
|
|
25
30
|
/** Header button configuration */
|
|
26
31
|
headerButtons?: HeaderButtonsConfig;
|
|
27
32
|
/** Current count of messages (used to conditionally show clear button) */
|
|
@@ -35,4 +40,4 @@ export interface ChatBaseHeaderProps {
|
|
|
35
40
|
/** Callback when view mode changes */
|
|
36
41
|
onChatViewModeChange?: (mode: ChatViewMode) => void;
|
|
37
42
|
}
|
|
38
|
-
export declare function ChatBaseHeader({ title, brandIcon, headerContent, headerActions, showInformation, onInformationClick, padding,
|
|
43
|
+
export declare function ChatBaseHeader({ title, subtitle, brandIcon, headerContent, headerActions, showInformation, onInformationClick, padding, sandboxApiBase, sandboxAuthToken, sandboxAgentId, sandboxStatusData, headerButtons, messageCount, onNewChat, onClear, chatViewMode, onChatViewModeChange, }: ChatBaseHeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Heading, IconButton, Truncate } from '@primer/react';
|
|
2
|
+
import { Heading, IconButton, Text, Truncate } from '@primer/react';
|
|
3
3
|
import { Box } from '@datalayer/primer-addons';
|
|
4
|
-
import { PlusIcon, TrashIcon, GearIcon,
|
|
4
|
+
import { PlusIcon, TrashIcon, GearIcon, CommentDiscussionIcon, DeviceMobileIcon, SidebarExpandIcon, InfoIcon, } from '@primer/octicons-react';
|
|
5
5
|
import { AiAgentIcon } from '@datalayer/icons-react';
|
|
6
|
+
import { SandboxStatusIndicator } from '../indicators/SandboxStatusIndicator';
|
|
6
7
|
// ---------------------------------------------------------------------------
|
|
7
8
|
// Component
|
|
8
9
|
// ---------------------------------------------------------------------------
|
|
9
|
-
export function ChatBaseHeader({ title, brandIcon, headerContent, headerActions, showInformation, onInformationClick, padding,
|
|
10
|
+
export function ChatBaseHeader({ title, subtitle, brandIcon, headerContent, headerActions, showInformation, onInformationClick, padding, sandboxApiBase, sandboxAuthToken, sandboxAgentId, sandboxStatusData, headerButtons, messageCount, onNewChat, onClear, chatViewMode, onChatViewModeChange, }) {
|
|
10
11
|
return (_jsx(Box, { sx: {
|
|
11
12
|
display: 'flex',
|
|
12
13
|
flexDirection: 'column',
|
|
@@ -23,21 +24,22 @@ export function ChatBaseHeader({ title, brandIcon, headerContent, headerActions,
|
|
|
23
24
|
gap: 2,
|
|
24
25
|
minWidth: 0,
|
|
25
26
|
flex: '1 1 auto',
|
|
26
|
-
}, children: [brandIcon || _jsx(AiAgentIcon, { colored: true, size: 20 }), title && (
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
}, children: [brandIcon || _jsx(AiAgentIcon, { colored: true, size: 20 }), (title || subtitle) && (_jsxs(Box, { sx: {
|
|
28
|
+
display: 'flex',
|
|
29
|
+
flexDirection: 'column',
|
|
29
30
|
minWidth: 0,
|
|
30
31
|
maxWidth: '100%',
|
|
31
|
-
}, children:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
}, children: [title && (_jsx(Heading, { as: "h3", sx: {
|
|
33
|
+
fontSize: 2,
|
|
34
|
+
fontWeight: 'semibold',
|
|
35
|
+
minWidth: 0,
|
|
36
|
+
maxWidth: '100%',
|
|
37
|
+
}, children: _jsx(Truncate, { title: title, maxWidth: "28ch", children: title }) })), subtitle && (_jsx(Text, { sx: {
|
|
38
|
+
fontSize: 0,
|
|
39
|
+
color: 'fg.muted',
|
|
40
|
+
minWidth: 0,
|
|
41
|
+
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(SandboxStatusIndicator, { apiBase: sandboxApiBase, authToken: sandboxAuthToken, agentId: sandboxAgentId, statusOverride: sandboxStatusData }), 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: {
|
|
41
43
|
display: 'inline-flex',
|
|
42
44
|
alignItems: 'center',
|
|
43
45
|
bg: 'neutral.muted',
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import type { McpToolsetsStatusResponse } from '../../types/mcp';
|
|
1
2
|
export interface McpStatusIndicatorProps {
|
|
2
|
-
/**
|
|
3
|
-
*
|
|
3
|
+
/** Pre-fetched MCP status data pushed via WebSocket. When provided the
|
|
4
|
+
* component will NOT poll the REST endpoint. */
|
|
5
|
+
data?: McpToolsetsStatusResponse | null;
|
|
6
|
+
/** @deprecated — Only used when `data` is not provided. */
|
|
4
7
|
apiBase?: string;
|
|
5
|
-
/**
|
|
8
|
+
/** @deprecated — Only used when `data` is not provided. */
|
|
6
9
|
authToken?: string;
|
|
7
10
|
}
|
|
8
|
-
export declare function McpStatusIndicator({ apiBase, authToken, }: McpStatusIndicatorProps): import("react/jsx-runtime").JSX.Element
|
|
11
|
+
export declare function McpStatusIndicator({ data: wsData, apiBase, authToken, }: McpStatusIndicatorProps): import("react/jsx-runtime").JSX.Element;
|
|
9
12
|
export default McpStatusIndicator;
|
|
@@ -21,19 +21,8 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
21
21
|
import { useEffect, useMemo } from 'react';
|
|
22
22
|
import { Tooltip } from '@primer/react';
|
|
23
23
|
import { Box } from '@datalayer/primer-addons';
|
|
24
|
-
import { useQuery } from '@tanstack/react-query';
|
|
25
24
|
import { MCP_STATUS_COLORS, MCP_STATUS_LABELS } from '../../types/mcp';
|
|
26
25
|
/* ── Helpers ───────────────────────────────────────────── */
|
|
27
|
-
function getApiBase(apiBase) {
|
|
28
|
-
if (apiBase)
|
|
29
|
-
return apiBase;
|
|
30
|
-
if (typeof window === 'undefined')
|
|
31
|
-
return '';
|
|
32
|
-
const host = window.location.hostname;
|
|
33
|
-
return host === 'localhost' || host === '127.0.0.1'
|
|
34
|
-
? 'http://127.0.0.1:8765'
|
|
35
|
-
: '';
|
|
36
|
-
}
|
|
37
26
|
function deriveAggregate(servers) {
|
|
38
27
|
if (!servers || servers.length === 0)
|
|
39
28
|
return 'none';
|
|
@@ -47,7 +36,7 @@ function deriveAggregate(servers) {
|
|
|
47
36
|
}
|
|
48
37
|
function buildTooltipText(aggregate, servers) {
|
|
49
38
|
if (aggregate === 'none')
|
|
50
|
-
return
|
|
39
|
+
return 'No MCP Server defined';
|
|
51
40
|
const lines = [MCP_STATUS_LABELS[aggregate]];
|
|
52
41
|
for (const s of servers) {
|
|
53
42
|
let detail = `• ${s.id}: ${s.status}`;
|
|
@@ -79,29 +68,15 @@ function useInjectKeyframes() {
|
|
|
79
68
|
}, []);
|
|
80
69
|
}
|
|
81
70
|
/* ── Component ─────────────────────────────────────────── */
|
|
82
|
-
export function McpStatusIndicator({ apiBase, authToken, }) {
|
|
71
|
+
export function McpStatusIndicator({ data: wsData, apiBase, authToken, }) {
|
|
83
72
|
useInjectKeyframes();
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const base = getApiBase(apiBase);
|
|
88
|
-
const headers = {};
|
|
89
|
-
if (authToken) {
|
|
90
|
-
headers['Authorization'] = `Bearer ${authToken}`;
|
|
91
|
-
}
|
|
92
|
-
const response = await fetch(`${base}/api/v1/configure/mcp-toolsets-status`, { headers });
|
|
93
|
-
if (!response.ok)
|
|
94
|
-
throw new Error('Failed to fetch MCP status');
|
|
95
|
-
return response.json();
|
|
96
|
-
},
|
|
97
|
-
refetchInterval: 5000,
|
|
98
|
-
});
|
|
99
|
-
const servers = data?.servers ?? [];
|
|
73
|
+
// REST polling removed — data comes exclusively via WS `agent.snapshot`.
|
|
74
|
+
const effectiveData = wsData;
|
|
75
|
+
const servers = effectiveData?.servers ?? [];
|
|
100
76
|
const aggregate = useMemo(() => deriveAggregate(servers), [servers]);
|
|
101
77
|
const tooltipText = useMemo(() => buildTooltipText(aggregate, servers), [aggregate, servers]);
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
return null;
|
|
78
|
+
// Show a subtle gray dot when no MCP servers are configured.
|
|
79
|
+
// The tooltip tells the user none are defined.
|
|
105
80
|
return (_jsx(Tooltip, { text: tooltipText, direction: "n", children: _jsx("button", { type: "button", "aria-label": tooltipText, style: {
|
|
106
81
|
display: 'inline-flex',
|
|
107
82
|
alignItems: 'center',
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SandboxWsStatus } from '../../types/sandbox';
|
|
1
2
|
export interface SandboxStatusIndicatorProps {
|
|
2
3
|
/** API base URL (e.g. "http://127.0.0.1:8765"). */
|
|
3
4
|
apiBase?: string;
|
|
@@ -5,6 +6,8 @@ export interface SandboxStatusIndicatorProps {
|
|
|
5
6
|
authToken?: string;
|
|
6
7
|
/** Agent ID to scope sandbox status to a specific agent. */
|
|
7
8
|
agentId?: string;
|
|
9
|
+
/** Optional status override to update indicator immediately from parent UI. */
|
|
10
|
+
statusOverride?: SandboxWsStatus | null;
|
|
8
11
|
}
|
|
9
|
-
export declare function SandboxStatusIndicator({ apiBase, authToken, agentId, }: SandboxStatusIndicatorProps): import("react/jsx-runtime").JSX.Element
|
|
12
|
+
export declare function SandboxStatusIndicator({ apiBase, authToken, agentId, statusOverride, }: SandboxStatusIndicatorProps): import("react/jsx-runtime").JSX.Element;
|
|
10
13
|
export default SandboxStatusIndicator;
|
|
@@ -81,7 +81,7 @@ function useInjectKeyframes() {
|
|
|
81
81
|
}, []);
|
|
82
82
|
}
|
|
83
83
|
/* ── Component ─────────────────────────────────────────── */
|
|
84
|
-
export function SandboxStatusIndicator({ apiBase, authToken, agentId, }) {
|
|
84
|
+
export function SandboxStatusIndicator({ apiBase, authToken, agentId, statusOverride, }) {
|
|
85
85
|
useInjectKeyframes();
|
|
86
86
|
const [status, setStatus] = useState(null);
|
|
87
87
|
const wsRef = useRef(null);
|
|
@@ -138,17 +138,17 @@ export function SandboxStatusIndicator({ apiBase, authToken, agentId, }) {
|
|
|
138
138
|
}
|
|
139
139
|
}, []);
|
|
140
140
|
// ---- Derived display values ----
|
|
141
|
-
const
|
|
141
|
+
const effectiveStatus = statusOverride ?? status;
|
|
142
|
+
const aggregate = useMemo(() => deriveAggregate(effectiveStatus), [effectiveStatus]);
|
|
142
143
|
const tooltipText = useMemo(() => {
|
|
143
|
-
if (!
|
|
144
|
-
return
|
|
144
|
+
if (!effectiveStatus)
|
|
145
|
+
return 'No Sandbox defined';
|
|
145
146
|
const label = SANDBOX_STATUS_LABELS[aggregate];
|
|
146
|
-
const variant =
|
|
147
|
+
const variant = effectiveStatus.variant;
|
|
147
148
|
return `${label} (${variant})`;
|
|
148
|
-
}, [aggregate,
|
|
149
|
-
//
|
|
150
|
-
|
|
151
|
-
return null;
|
|
149
|
+
}, [aggregate, effectiveStatus]);
|
|
150
|
+
// Show a subtle gray dot when sandbox is unavailable.
|
|
151
|
+
// The tooltip tells the user none is configured.
|
|
152
152
|
return (_jsx(Tooltip, { text: tooltipText, direction: "n", children: _jsx("button", { type: "button", "aria-label": tooltipText, onClick: aggregate === 'executing' ? sendInterrupt : undefined, style: {
|
|
153
153
|
display: 'inline-flex',
|
|
154
154
|
alignItems: 'center',
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface SkillsStatusIndicatorProps {
|
|
2
|
+
skillsCount: number;
|
|
3
|
+
enabledCount: number;
|
|
4
|
+
loading?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function SkillsStatusIndicator({ skillsCount, enabledCount, loading, }: SkillsStatusIndicatorProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export default SkillsStatusIndicator;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { jsx as _jsx } 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
|
+
* SkillsStatusIndicator — Round status dot representing skill state.
|
|
8
|
+
*
|
|
9
|
+
* Aggregate logic:
|
|
10
|
+
* - no skills configured -> none (gray)
|
|
11
|
+
* - skills loading -> loading (amber, pulsing)
|
|
12
|
+
* - some enabled -> active (green)
|
|
13
|
+
* - none enabled -> inactive (gray)
|
|
14
|
+
*/
|
|
15
|
+
import { useEffect, useMemo } from 'react';
|
|
16
|
+
import { Tooltip } from '@primer/react';
|
|
17
|
+
import { Box } from '@datalayer/primer-addons';
|
|
18
|
+
const SKILLS_STATUS_COLORS = {
|
|
19
|
+
none: '#8b949e',
|
|
20
|
+
loading: '#d29922',
|
|
21
|
+
inactive: '#8b949e',
|
|
22
|
+
active: '#3fb950',
|
|
23
|
+
};
|
|
24
|
+
const SKILLS_PULSE_KEYFRAMES = `
|
|
25
|
+
@keyframes skills-pulse {
|
|
26
|
+
0%, 100% { opacity: 1; }
|
|
27
|
+
50% { opacity: 0.4; }
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
30
|
+
function useInjectKeyframes() {
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
const id = '__skills-pulse-keyframes__';
|
|
33
|
+
if (document.getElementById(id))
|
|
34
|
+
return;
|
|
35
|
+
const style = document.createElement('style');
|
|
36
|
+
style.id = id;
|
|
37
|
+
style.textContent = SKILLS_PULSE_KEYFRAMES;
|
|
38
|
+
document.head.appendChild(style);
|
|
39
|
+
}, []);
|
|
40
|
+
}
|
|
41
|
+
function deriveAggregate(skillsCount, enabledCount, loading) {
|
|
42
|
+
if (skillsCount <= 0)
|
|
43
|
+
return 'none';
|
|
44
|
+
if (loading)
|
|
45
|
+
return 'loading';
|
|
46
|
+
if (enabledCount > 0)
|
|
47
|
+
return 'active';
|
|
48
|
+
return 'inactive';
|
|
49
|
+
}
|
|
50
|
+
function buildTooltip(aggregate, skillsCount, enabledCount) {
|
|
51
|
+
if (aggregate === 'none')
|
|
52
|
+
return 'No Skills defined';
|
|
53
|
+
if (aggregate === 'loading') {
|
|
54
|
+
return `Skills loading (${enabledCount}/${skillsCount} enabled)`;
|
|
55
|
+
}
|
|
56
|
+
if (aggregate === 'active') {
|
|
57
|
+
return `Skills active (${enabledCount}/${skillsCount} enabled)`;
|
|
58
|
+
}
|
|
59
|
+
return `Skills available (${enabledCount}/${skillsCount} enabled)`;
|
|
60
|
+
}
|
|
61
|
+
export function SkillsStatusIndicator({ skillsCount, enabledCount, loading = false, }) {
|
|
62
|
+
useInjectKeyframes();
|
|
63
|
+
const aggregate = useMemo(() => deriveAggregate(skillsCount, enabledCount, loading), [skillsCount, enabledCount, loading]);
|
|
64
|
+
const tooltipText = useMemo(() => buildTooltip(aggregate, skillsCount, enabledCount), [aggregate, skillsCount, enabledCount]);
|
|
65
|
+
return (_jsx(Tooltip, { text: tooltipText, direction: "n", children: _jsx("button", { type: "button", "aria-label": tooltipText, style: {
|
|
66
|
+
display: 'inline-flex',
|
|
67
|
+
alignItems: 'center',
|
|
68
|
+
justifyContent: 'center',
|
|
69
|
+
width: 28,
|
|
70
|
+
height: 28,
|
|
71
|
+
padding: 0,
|
|
72
|
+
border: 'none',
|
|
73
|
+
background: 'none',
|
|
74
|
+
cursor: 'default',
|
|
75
|
+
lineHeight: 0,
|
|
76
|
+
}, children: _jsx(Box, { as: "span", sx: {
|
|
77
|
+
display: 'inline-block',
|
|
78
|
+
width: 12,
|
|
79
|
+
height: 12,
|
|
80
|
+
borderRadius: '50%',
|
|
81
|
+
bg: SKILLS_STATUS_COLORS[aggregate],
|
|
82
|
+
flexShrink: 0,
|
|
83
|
+
...(aggregate === 'loading' && {
|
|
84
|
+
animation: 'skills-pulse 1.5s ease-in-out infinite',
|
|
85
|
+
}),
|
|
86
|
+
} }) }) }));
|
|
87
|
+
}
|
|
88
|
+
export default SkillsStatusIndicator;
|
|
@@ -15,3 +15,4 @@ export { McpStatusIndicator, type McpStatusIndicatorProps, } from './McpStatusIn
|
|
|
15
15
|
export type { McpServerStatus, McpAggregateStatus } from '../../types/mcp';
|
|
16
16
|
export { SandboxStatusIndicator, type SandboxStatusIndicatorProps, } from './SandboxStatusIndicator';
|
|
17
17
|
export type { SandboxAggregateStatus, SandboxWsStatus, } from '../../types/sandbox';
|
|
18
|
+
export { SkillsStatusIndicator, type SkillsStatusIndicatorProps, } from './SkillsStatusIndicator';
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { type ReactNode, type RefObject } from 'react';
|
|
8
8
|
import type { DisplayItem, AvatarConfig, RenderToolResult } from '../../types/chat';
|
|
9
9
|
export interface ToolApprovalConfig {
|
|
10
|
-
/** Base API URL for the agent runtime (e.g., http://localhost:8765/api/v1) */
|
|
10
|
+
/** Base API URL for the agent runtime (e.g., `http://localhost:8765/api/v1`). */
|
|
11
11
|
apiBaseUrl: string;
|
|
12
12
|
/** Auth token for API calls */
|
|
13
13
|
authToken?: string;
|
|
@@ -9,138 +9,133 @@ 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,
|
|
12
|
+
import { useState, useCallback, useMemo, } from 'react';
|
|
13
13
|
import { Text } from '@primer/react';
|
|
14
14
|
import { Box } from '@datalayer/primer-addons';
|
|
15
15
|
import { Streamdown } from 'streamdown';
|
|
16
16
|
import { streamdownMarkdownStyles, streamdownCodeBlockStyles, } from '../styles/streamdownStyles';
|
|
17
17
|
import { ToolCallDisplay } from '../tools/ToolCallDisplay';
|
|
18
18
|
import { isToolCallMessage, getMessageText } from '../../utils';
|
|
19
|
+
import { useAgentRuntimeStore } from '../../stores/agentRuntimeStore';
|
|
19
20
|
// ---------------------------------------------------------------------------
|
|
20
|
-
//
|
|
21
|
+
// Normalize helper
|
|
21
22
|
// ---------------------------------------------------------------------------
|
|
22
|
-
function
|
|
23
|
+
function normalizeName(name) {
|
|
24
|
+
return name
|
|
25
|
+
.replace(/:[0-9]+\.[0-9]+\.[0-9]+.*$/, '')
|
|
26
|
+
.replace(/[-_]/g, '')
|
|
27
|
+
.toLowerCase();
|
|
28
|
+
}
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// DefaultToolCallRenderer — reads approvals from the Zustand store,
|
|
31
|
+
// sends decisions over the shared WebSocket (no REST polling).
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
function DefaultToolCallRenderer({ item, onRespond, }) {
|
|
23
34
|
const resultObject = item.result && typeof item.result === 'object'
|
|
24
35
|
? item.result
|
|
25
36
|
: undefined;
|
|
26
37
|
// Detect pending approval from tool result (pydantic-deferred mode)
|
|
27
38
|
const isPendingFromResult = item.status === 'inProgress' && resultObject?.pending_approval === true;
|
|
39
|
+
// Extract the approval_id carried in the tool result by the adapter.
|
|
40
|
+
// This is always available when the SSE stream contained a
|
|
41
|
+
// `tool-approval-request` event, even when the Zustand store has not been
|
|
42
|
+
// populated (e.g. no monitoring WS open).
|
|
43
|
+
const resultApprovalId = typeof resultObject?.approval_id === 'string'
|
|
44
|
+
? resultObject.approval_id
|
|
45
|
+
: undefined;
|
|
28
46
|
// In ai-agents-wrapper mode, the server blocks waiting for approval and
|
|
29
47
|
// never sends a tool-result with pending_approval. The tool stays in
|
|
30
|
-
// 'executing' status.
|
|
31
|
-
// a pending approval record exists for this tool.
|
|
48
|
+
// 'executing' status.
|
|
32
49
|
const isExecuting = item.status === 'executing';
|
|
33
|
-
const [matchedApprovalId, setMatchedApprovalId] = useState(null);
|
|
34
50
|
const [decision, setDecision] = useState();
|
|
35
|
-
const
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
//
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (!
|
|
51
|
+
const normalizedToolName = useMemo(() => normalizeName(item.toolName), [item.toolName]);
|
|
52
|
+
// Skill tools (`run_skill_script`, `load_skill`, `read_skill_resource`) are
|
|
53
|
+
// gated by the server under a synthetic approval ``tool_name`` of the form
|
|
54
|
+
// ``skill:<skill_id>`` (see ``SkillsGuardrailCapability._request_skill_approval``).
|
|
55
|
+
// Capture the skill id carried in the tool-call arguments so we can match
|
|
56
|
+
// the inline approval button against that synthetic approval entry.
|
|
57
|
+
const skillApprovalKey = useMemo(() => {
|
|
58
|
+
const SKILL_TOOLS = new Set([
|
|
59
|
+
'run_skill_script',
|
|
60
|
+
'load_skill',
|
|
61
|
+
'read_skill_resource',
|
|
62
|
+
]);
|
|
63
|
+
if (!SKILL_TOOLS.has(item.toolName))
|
|
64
|
+
return null;
|
|
65
|
+
const a = (item.args ?? {});
|
|
66
|
+
const raw = a.skill_name ?? a.skill ?? a.name;
|
|
67
|
+
if (typeof raw !== 'string' || raw.length === 0)
|
|
68
|
+
return null;
|
|
69
|
+
// Strip optional ``<name>:<version>`` suffix to the base skill id.
|
|
70
|
+
const base = raw.split(':', 1)[0] || raw;
|
|
71
|
+
return `skill:${base}`.toLowerCase();
|
|
72
|
+
}, [item.toolName, item.args]);
|
|
73
|
+
// Read pending approvals from the Zustand store (fed by the agent-runtime WS).
|
|
74
|
+
const approvals = useAgentRuntimeStore(s => s.approvals);
|
|
75
|
+
// Prefer an exact match by approval id (when present in SSE tool result).
|
|
76
|
+
const matchedByResultId = useMemo(() => {
|
|
77
|
+
if (!resultApprovalId)
|
|
78
|
+
return null;
|
|
79
|
+
return approvals.find(a => a.id === resultApprovalId) ?? null;
|
|
80
|
+
}, [approvals, resultApprovalId]);
|
|
81
|
+
// Match the synthetic ``skill:<id>`` approval for skill tool calls. This
|
|
82
|
+
// runs whenever the tool call is a skill tool so the inline Approve/Deny
|
|
83
|
+
// buttons surface even when the server is blocked in
|
|
84
|
+
// ``ToolApprovalManager.request_and_wait`` (status stays ``executing`` and
|
|
85
|
+
// no ``pending_approval=True`` result is ever emitted).
|
|
86
|
+
const matchedBySkill = useMemo(() => {
|
|
87
|
+
if (!skillApprovalKey)
|
|
88
|
+
return null;
|
|
89
|
+
return (approvals.find(a => a.tool_name?.toLowerCase() === skillApprovalKey) ??
|
|
90
|
+
null);
|
|
91
|
+
}, [approvals, skillApprovalKey]);
|
|
92
|
+
// Fallback to matching by tool name for inline approval flows that don't
|
|
93
|
+
// always provide approval_id in the tool result payload.
|
|
94
|
+
const matchedByName = useMemo(() => {
|
|
95
|
+
if (!isPendingFromResult && !isExecuting)
|
|
96
|
+
return null;
|
|
97
|
+
return (approvals.find(a => normalizeName(a.tool_name) === normalizedToolName) ??
|
|
98
|
+
null);
|
|
99
|
+
}, [approvals, normalizedToolName, isPendingFromResult, isExecuting]);
|
|
100
|
+
const matchedApproval = matchedByResultId ?? matchedBySkill ?? matchedByName;
|
|
101
|
+
// Prefer the store-matched approval id, fall back to the id from the tool
|
|
102
|
+
// result (SSE path). This ensures the approve/deny buttons work even when
|
|
103
|
+
// the monitoring WS is not connected.
|
|
104
|
+
const effectiveApprovalId = matchedApproval?.id ?? resultApprovalId ?? null;
|
|
105
|
+
// Reflect approval decisions that happened outside this card (e.g. sidebar).
|
|
106
|
+
const externalDecision = matchedApproval?.status === 'approved'
|
|
107
|
+
? 'approved'
|
|
108
|
+
: matchedApproval?.status === 'rejected'
|
|
109
|
+
? 'denied'
|
|
110
|
+
: undefined;
|
|
111
|
+
const makeDecision = useCallback((action) => {
|
|
112
|
+
if (!effectiveApprovalId)
|
|
48
113
|
return;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return;
|
|
61
|
-
const data = await res.json();
|
|
62
|
-
const approvals = Array.isArray(data)
|
|
63
|
-
? data
|
|
64
|
-
: (data.approvals ??
|
|
65
|
-
data.requests ??
|
|
66
|
-
[]);
|
|
67
|
-
// Match by normalized tool name
|
|
68
|
-
const match = approvals.find(a => {
|
|
69
|
-
const aNormalized = String(a.tool_name || '')
|
|
70
|
-
.replace(/:[0-9]+\.[0-9]+\.[0-9]+.*$/, '')
|
|
71
|
-
.replace(/[-_]/g, '')
|
|
72
|
-
.toLowerCase();
|
|
73
|
-
return aNormalized === normalizedToolName && a.status === 'pending';
|
|
74
|
-
});
|
|
75
|
-
if (match && !cancelled) {
|
|
76
|
-
setMatchedApprovalId(match.id);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
catch {
|
|
80
|
-
// Retry on next interval
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
poll();
|
|
84
|
-
const interval = setInterval(poll, 3000);
|
|
85
|
-
return () => {
|
|
86
|
-
cancelled = true;
|
|
87
|
-
clearInterval(interval);
|
|
88
|
-
};
|
|
89
|
-
}, [
|
|
90
|
-
shouldPoll,
|
|
91
|
-
approvalConfig?.apiBaseUrl,
|
|
92
|
-
approvalConfig?.authToken,
|
|
93
|
-
normalizedToolName,
|
|
94
|
-
]);
|
|
95
|
-
const makeDecision = useCallback(async (action) => {
|
|
96
|
-
if (!matchedApprovalId || !approvalConfig?.apiBaseUrl)
|
|
97
|
-
return;
|
|
98
|
-
setLoading(true);
|
|
99
|
-
try {
|
|
100
|
-
const headers = {
|
|
101
|
-
'Content-Type': 'application/json',
|
|
102
|
-
};
|
|
103
|
-
if (approvalConfig.authToken) {
|
|
104
|
-
headers['Authorization'] = `Bearer ${approvalConfig.authToken}`;
|
|
105
|
-
}
|
|
106
|
-
const res = await fetch(`${approvalConfig.apiBaseUrl}/tool-approvals/${matchedApprovalId}/${action}`, {
|
|
107
|
-
method: 'POST',
|
|
108
|
-
headers,
|
|
109
|
-
body: JSON.stringify({}),
|
|
110
|
-
});
|
|
111
|
-
if (res.ok) {
|
|
112
|
-
setDecision(action === 'approve' ? 'approved' : 'denied');
|
|
113
|
-
onRespond({
|
|
114
|
-
type: 'tool-approval-decision',
|
|
115
|
-
approved: action === 'approve',
|
|
116
|
-
approvalId: matchedApprovalId,
|
|
117
|
-
toolName: item.toolName,
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
catch {
|
|
122
|
-
// Allow retry
|
|
123
|
-
}
|
|
124
|
-
finally {
|
|
125
|
-
setLoading(false);
|
|
126
|
-
}
|
|
127
|
-
}, [
|
|
128
|
-
matchedApprovalId,
|
|
129
|
-
approvalConfig?.apiBaseUrl,
|
|
130
|
-
approvalConfig?.authToken,
|
|
131
|
-
onRespond,
|
|
132
|
-
item.toolCallId,
|
|
133
|
-
item.toolName,
|
|
134
|
-
]);
|
|
114
|
+
const approved = action === 'approve';
|
|
115
|
+
// Use ONLY the adapter continuation path (SSE/POST). Sending both WS and
|
|
116
|
+
// continuation decisions can trigger duplicate approval cycles.
|
|
117
|
+
setDecision(approved ? 'approved' : 'denied');
|
|
118
|
+
onRespond({
|
|
119
|
+
type: 'tool-approval-decision',
|
|
120
|
+
approved,
|
|
121
|
+
approvalId: effectiveApprovalId,
|
|
122
|
+
toolName: item.toolName,
|
|
123
|
+
});
|
|
124
|
+
}, [effectiveApprovalId, onRespond, item.toolName]);
|
|
135
125
|
// Show approval UI when we have a confirmed pending approval (from result
|
|
136
|
-
// or discovered via
|
|
137
|
-
const hasApproval = isPendingFromResult || !!
|
|
138
|
-
const approvalState = decision || (hasApproval ? 'pending' : undefined);
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
?
|
|
143
|
-
: undefined
|
|
126
|
+
// or discovered via the store) or after a decision has been made.
|
|
127
|
+
const hasApproval = isPendingFromResult || !!effectiveApprovalId || !!externalDecision;
|
|
128
|
+
const approvalState = decision || externalDecision || (hasApproval ? 'pending' : undefined);
|
|
129
|
+
const approvalDecisionSource = decision
|
|
130
|
+
? 'inline'
|
|
131
|
+
: externalDecision
|
|
132
|
+
? 'external'
|
|
133
|
+
: undefined;
|
|
134
|
+
return (_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
|
|
135
|
+
? () => makeDecision('approve')
|
|
136
|
+
: undefined, onDeny: effectiveApprovalId && !decision && !externalDecision
|
|
137
|
+
? () => makeDecision('reject')
|
|
138
|
+
: undefined, approvalLoading: false }));
|
|
144
139
|
}
|
|
145
140
|
// ---------------------------------------------------------------------------
|
|
146
141
|
// Component
|