@datalayer/agent-runtimes 1.0.4 → 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 +182 -1
- package/lib/AgentNode.d.ts +3 -0
- package/lib/AgentNode.js +676 -0
- package/lib/App.js +1 -1
- 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/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 +20 -14
- package/lib/chat/ChatFloating.d.ts +7 -140
- package/lib/chat/ChatFloating.js +3 -3
- 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 +83 -51
- package/lib/chat/ChatStandalone.d.ts +4 -54
- package/lib/chat/ChatStandalone.js +3 -3
- package/lib/chat/base/ChatBase.js +1414 -174
- 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 +30 -5
- package/lib/chat/header/ChatHeaderBase.js +41 -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 +91 -56
- 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 +154 -114
- package/lib/chat/messages/ChatMessages.js +6 -2
- package/lib/chat/prompt/InputFooter.d.ts +21 -6
- package/lib/chat/prompt/InputFooter.js +76 -20
- package/lib/chat/prompt/InputPrompt.d.ts +5 -1
- package/lib/chat/prompt/InputPrompt.js +4 -4
- package/lib/chat/prompt/InputPromptFooter.d.ts +3 -1
- package/lib/chat/prompt/InputPromptFooter.js +3 -3
- 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 +20 -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.d.ts +22 -0
- package/lib/config/AgentConfiguration.js +319 -64
- 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/AgUiSharedStateExample.js +2 -1
- package/lib/examples/AgentCheckpointsExample.js +14 -28
- package/lib/examples/AgentCodemodeExample.d.ts +4 -6
- package/lib/examples/AgentCodemodeExample.js +603 -169
- package/lib/examples/AgentEvalsExample.js +339 -53
- package/lib/examples/AgentGuardrailsExample.js +383 -66
- package/lib/examples/AgentHooksExample.d.ts +3 -0
- package/lib/examples/AgentHooksExample.js +122 -0
- package/lib/examples/AgentInferenceProviderExample.d.ts +3 -0
- package/lib/examples/AgentInferenceProviderExample.js +329 -0
- package/lib/examples/AgentMCPExample.d.ts +3 -0
- package/lib/examples/AgentMCPExample.js +481 -0
- package/lib/examples/AgentMemoryExample.d.ts +1 -2
- package/lib/examples/AgentMemoryExample.js +78 -33
- package/lib/examples/AgentMonitoringExample.js +261 -200
- package/lib/examples/AgentNotificationsExample.d.ts +1 -2
- package/lib/examples/AgentNotificationsExample.js +114 -33
- package/lib/examples/AgentOtelExample.js +32 -42
- package/lib/examples/AgentOutputsExample.d.ts +11 -6
- package/lib/examples/AgentOutputsExample.js +433 -81
- package/lib/examples/AgentParametersExample.d.ts +3 -0
- package/lib/examples/AgentParametersExample.js +248 -0
- package/lib/examples/AgentSandboxExample.d.ts +3 -3
- package/lib/examples/AgentSandboxExample.js +74 -45
- package/lib/examples/AgentSkillsExample.js +95 -103
- package/lib/examples/AgentSubagentsExample.d.ts +14 -0
- package/lib/examples/AgentSubagentsExample.js +228 -0
- package/lib/examples/AgentToolApprovalsExample.js +49 -561
- package/lib/examples/AgentTriggersExample.js +823 -569
- package/lib/examples/{AgentspecExample.d.ts → AgentspecsExample.d.ts} +2 -2
- package/lib/examples/AgentspecsExample.js +1096 -0
- package/lib/examples/ChatCustomExample.js +16 -28
- package/lib/examples/ChatExample.js +13 -29
- 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} +66 -17
- 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 +9 -3
- package/lib/examples/components/ExampleWrapper.js +45 -9
- 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 +108 -41
- package/lib/examples/index.d.ts +10 -6
- package/lib/examples/index.js +10 -6
- package/lib/examples/lexical/initial-content.json +6 -6
- package/lib/examples/main.js +257 -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/examples/utils/useExampleAgentRuntimesUrl.d.ts +5 -0
- package/lib/examples/utils/useExampleAgentRuntimesUrl.js +19 -0
- 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 +153 -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} +70 -4
- package/lib/hooks/{useAgents.js → useAgentRuntimes.js} +237 -32
- 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 +21 -4
- package/lib/specs/agents/agents.js +2879 -316
- package/lib/specs/agents/index.js +3 -1
- package/lib/specs/benchmarks.d.ts +20 -0
- package/lib/specs/benchmarks.js +205 -0
- package/lib/specs/envvars.js +27 -20
- package/lib/specs/evals.d.ts +10 -9
- package/lib/specs/evals.js +128 -88
- 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/mcpServers.js +35 -6
- package/lib/specs/memory.d.ts +0 -2
- package/lib/specs/memory.js +4 -17
- package/lib/specs/models.d.ts +0 -2
- package/lib/specs/models.js +20 -15
- 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 +1 -1
- package/lib/specs/skills.js +23 -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 +208 -0
- package/lib/stores/agentRuntimeStore.js +650 -0
- package/lib/stores/conversationStore.js +2 -2
- 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/agents-lifecycle.d.ts +18 -0
- package/lib/types/agents.d.ts +6 -0
- package/lib/types/agentspecs.d.ts +54 -1
- package/lib/types/benchmarks.d.ts +43 -0
- package/lib/types/benchmarks.js +5 -0
- package/lib/types/chat.d.ts +325 -8
- package/lib/types/context.d.ts +27 -0
- package/lib/types/cost.d.ts +2 -2
- package/lib/types/evals.d.ts +26 -17
- package/lib/types/index.d.ts +3 -0
- package/lib/types/index.js +3 -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 +19 -11
- 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/__pycache__/generate_events.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/versioning.cpython-313.pyc +0 -0
- package/scripts/codegen/generate_agents.py +187 -45
- package/scripts/codegen/generate_benchmarks.py +441 -0
- package/scripts/codegen/generate_evals.py +94 -16
- package/scripts/codegen/generate_events.py +35 -14
- 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/AgentspecExample.js +0 -705
- 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/{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
|
@@ -7,60 +7,18 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
7
7
|
import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
|
|
8
8
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
9
9
|
import { Box } from '@datalayer/primer-addons';
|
|
10
|
-
import { ErrorView } from './components';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { useCoreStore } from '@datalayer/core';
|
|
14
|
-
import { DEFAULT_SERVICE_URLS } from '@datalayer/core/lib/api/constants';
|
|
10
|
+
import { AuthRequiredView, ErrorView } from './components';
|
|
11
|
+
import { Spinner, Text } from '@primer/react';
|
|
12
|
+
import { CheckCircleIcon } from '@primer/octicons-react';
|
|
15
13
|
import { useSimpleAuthStore } from '@datalayer/core/lib/views/otel';
|
|
16
|
-
import { SignInSimple } from '@datalayer/core/lib/views/iam';
|
|
17
|
-
import { UserBadge } from '@datalayer/core/lib/views/profile';
|
|
18
14
|
import { ThemedProvider } from './utils/themedProvider';
|
|
15
|
+
import { uniqueAgentId } from './utils/agentId';
|
|
19
16
|
import { Chat } from '../chat';
|
|
20
|
-
import {
|
|
21
|
-
const normalizeToolName = (value) => value.replace(/[-_]/g, '').toLowerCase();
|
|
22
|
-
const AI_AGENTS_API_PREFIX = '/api/ai-agents/v1';
|
|
23
|
-
const stableStringify = (value) => {
|
|
24
|
-
if (value === null || typeof value !== 'object') {
|
|
25
|
-
return JSON.stringify(value);
|
|
26
|
-
}
|
|
27
|
-
if (Array.isArray(value)) {
|
|
28
|
-
return `[${value.map(item => stableStringify(item)).join(',')}]`;
|
|
29
|
-
}
|
|
30
|
-
const entries = Object.entries(value)
|
|
31
|
-
.sort(([a], [b]) => a.localeCompare(b))
|
|
32
|
-
.map(([key, itemValue]) => `${JSON.stringify(key)}:${stableStringify(itemValue)}`);
|
|
33
|
-
return `{${entries.join(',')}}`;
|
|
34
|
-
};
|
|
35
|
-
const toWsUrl = (baseUrl, path, token) => {
|
|
36
|
-
try {
|
|
37
|
-
const url = new URL(baseUrl);
|
|
38
|
-
url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
39
|
-
url.pathname = path;
|
|
40
|
-
if (token) {
|
|
41
|
-
url.searchParams.set('token', token);
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
url.search = '';
|
|
45
|
-
}
|
|
46
|
-
return url.toString();
|
|
47
|
-
}
|
|
48
|
-
catch {
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
const normalizeAiAgentsBaseUrl = (rawBaseUrl) => {
|
|
53
|
-
const trimmed = rawBaseUrl.replace(/\/$/, '');
|
|
54
|
-
if (trimmed.endsWith(AI_AGENTS_API_PREFIX)) {
|
|
55
|
-
return trimmed.slice(0, -AI_AGENTS_API_PREFIX.length);
|
|
56
|
-
}
|
|
57
|
-
return trimmed;
|
|
58
|
-
};
|
|
17
|
+
import { useAgentRuntimeApprovals } from '../stores/agentRuntimeStore';
|
|
59
18
|
const queryClient = new QueryClient();
|
|
60
|
-
const AGENT_NAME_PREFIX = 'tool-approval-
|
|
61
|
-
const DEFAULT_AGENT_SPEC_ID = '
|
|
19
|
+
const AGENT_NAME_PREFIX = 'tool-approval-example-agent';
|
|
20
|
+
const DEFAULT_AGENT_SPEC_ID = 'example-tool-approvals';
|
|
62
21
|
const DEFAULT_LOCAL_BASE_URL = import.meta.env.VITE_BASE_URL || 'http://localhost:8765';
|
|
63
|
-
const FALLBACK_AI_AGENTS_BASE_URL = import.meta.env.VITE_AI_AGENTS_URL || DEFAULT_SERVICE_URLS.AI_AGENTS;
|
|
64
22
|
const getSelectedAgentSpecIdFromUi = () => {
|
|
65
23
|
const params = new URLSearchParams(window.location.search);
|
|
66
24
|
const directKeys = [
|
|
@@ -91,36 +49,25 @@ const buildAgentNameForSpec = (specId) => {
|
|
|
91
49
|
.replace(/[^a-z0-9]+/g, '-')
|
|
92
50
|
.replace(/^-+|-+$/g, '')
|
|
93
51
|
.slice(0, 48);
|
|
94
|
-
|
|
52
|
+
const base = slug ? `${AGENT_NAME_PREFIX}-${slug}` : AGENT_NAME_PREFIX;
|
|
53
|
+
return uniqueAgentId(base);
|
|
95
54
|
};
|
|
96
|
-
const approvalSignature = (toolName, args) => `${normalizeToolName(toolName)}::${stableStringify(args ?? {})}`;
|
|
97
55
|
const AgentToolApprovalsInner = ({ onLogout, }) => {
|
|
98
56
|
const { token } = useSimpleAuthStore();
|
|
99
57
|
const [selectedSpecId] = useState(() => getSelectedAgentSpecIdFromUi());
|
|
100
58
|
const agentName = useMemo(() => buildAgentNameForSpec(selectedSpecId), [selectedSpecId]);
|
|
101
|
-
const [mode, setMode] = useState('local');
|
|
102
59
|
const [runtimeStatus, setRuntimeStatus] = useState('launching');
|
|
103
60
|
const [isReady, setIsReady] = useState(false);
|
|
104
61
|
const [hookError, setHookError] = useState(null);
|
|
105
62
|
const [agentId, setAgentId] = useState(agentName);
|
|
106
63
|
const [isReconnectedAgent, setIsReconnectedAgent] = useState(false);
|
|
107
|
-
const [approvals, setApprovals] = useState([]);
|
|
108
|
-
const [localApprovals, setLocalApprovals] = useState([]);
|
|
109
|
-
const [activeApproval, setActiveApproval] = useState(null);
|
|
110
|
-
const [approvalLoading, setApprovalLoading] = useState(null);
|
|
111
|
-
const [approvalError, setApprovalError] = useState(null);
|
|
112
|
-
const [toolApprovalState, setToolApprovalState] = useState({});
|
|
113
|
-
const [wsState, setWsState] = useState('closed');
|
|
114
|
-
const createdApprovalSignatures = useRef(new Set());
|
|
115
|
-
const toolRespondersRef = useRef(new Map());
|
|
116
|
-
const respondedToolCallsRef = useRef(new Set());
|
|
117
|
-
const resolveLocalRef = useRef(async () => null);
|
|
118
|
-
const authFetchRef = useRef((url, opts) => fetch(url, opts));
|
|
119
64
|
const chatAuthToken = token === null ? undefined : token;
|
|
120
|
-
const configuredAiAgentsBaseUrl = useCoreStore((s) => s.configuration?.aiagentsRunUrl);
|
|
121
65
|
const agentBaseUrl = DEFAULT_LOCAL_BASE_URL;
|
|
122
|
-
const aiAgentsBaseUrl = normalizeAiAgentsBaseUrl(configuredAiAgentsBaseUrl || FALLBACK_AI_AGENTS_BASE_URL);
|
|
123
66
|
const podName = 'localhost';
|
|
67
|
+
const approvals = useAgentRuntimeApprovals();
|
|
68
|
+
const pendingApprovalCount = useMemo(() => approvals.filter(approval => approval.status === 'pending' &&
|
|
69
|
+
(!agentId || approval.agent_id === agentId)).length, [approvals, agentId]);
|
|
70
|
+
const createAttemptedRef = useRef(false);
|
|
124
71
|
const authFetch = useCallback((url, opts = {}) => fetch(url, {
|
|
125
72
|
...opts,
|
|
126
73
|
headers: {
|
|
@@ -129,30 +76,11 @@ const AgentToolApprovalsInner = ({ onLogout, }) => {
|
|
|
129
76
|
...(opts.headers ?? {}),
|
|
130
77
|
},
|
|
131
78
|
}), [token]);
|
|
132
|
-
const emitServerToolDecision = useCallback((toolName, toolArgs, approved, approvalId, message) => {
|
|
133
|
-
const signature = approvalSignature(toolName, toolArgs);
|
|
134
|
-
const responder = toolRespondersRef.current.get(signature);
|
|
135
|
-
if (!responder?.respond) {
|
|
136
|
-
return false;
|
|
137
|
-
}
|
|
138
|
-
if (respondedToolCallsRef.current.has(responder.toolCallId)) {
|
|
139
|
-
return false;
|
|
140
|
-
}
|
|
141
|
-
setToolApprovalState(prev => ({
|
|
142
|
-
...prev,
|
|
143
|
-
[responder.toolCallId]: approved ? 'approved' : 'denied',
|
|
144
|
-
}));
|
|
145
|
-
respondedToolCallsRef.current.add(responder.toolCallId);
|
|
146
|
-
responder.respond({
|
|
147
|
-
type: 'tool-approval-decision',
|
|
148
|
-
approved,
|
|
149
|
-
approvalId,
|
|
150
|
-
toolName: responder.toolName || toolName,
|
|
151
|
-
...(message ? { message } : {}),
|
|
152
|
-
});
|
|
153
|
-
return true;
|
|
154
|
-
}, []);
|
|
155
79
|
useEffect(() => {
|
|
80
|
+
if (createAttemptedRef.current) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
createAttemptedRef.current = true;
|
|
156
84
|
let isCancelled = false;
|
|
157
85
|
const createLocalAgent = async () => {
|
|
158
86
|
setRuntimeStatus('launching');
|
|
@@ -218,448 +146,22 @@ const AgentToolApprovalsInner = ({ onLogout, }) => {
|
|
|
218
146
|
isCancelled = true;
|
|
219
147
|
};
|
|
220
148
|
}, [agentBaseUrl, authFetch, agentName, selectedSpecId]);
|
|
221
|
-
const pollApprovals = useCallback(async () => {
|
|
222
|
-
if (!isReady) {
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
const fetchPendingApprovals = async (baseUrl, apiPrefix) => {
|
|
226
|
-
const res = await authFetch(`${baseUrl}${apiPrefix}`);
|
|
227
|
-
if (!res.ok) {
|
|
228
|
-
return [];
|
|
229
|
-
}
|
|
230
|
-
const data = await res.json();
|
|
231
|
-
const allApprovals = Array.isArray(data)
|
|
232
|
-
? data
|
|
233
|
-
: (data.approvals ?? data.requests ?? []);
|
|
234
|
-
return allApprovals
|
|
235
|
-
.filter((approval) => approval.status === 'pending' &&
|
|
236
|
-
(approval.agent_id === agentId || !approval.agent_id))
|
|
237
|
-
.map((approval) => ({
|
|
238
|
-
...approval,
|
|
239
|
-
backendBaseUrl: baseUrl,
|
|
240
|
-
apiPrefix,
|
|
241
|
-
}));
|
|
242
|
-
};
|
|
243
|
-
const source = mode === 'server'
|
|
244
|
-
? {
|
|
245
|
-
baseUrl: aiAgentsBaseUrl,
|
|
246
|
-
apiPrefix: `${AI_AGENTS_API_PREFIX}/tool-approvals`,
|
|
247
|
-
}
|
|
248
|
-
: { baseUrl: agentBaseUrl, apiPrefix: '/api/v1/tool-approvals' };
|
|
249
|
-
try {
|
|
250
|
-
const localPending = await fetchPendingApprovals(agentBaseUrl, '/api/v1/tool-approvals');
|
|
251
|
-
setLocalApprovals(localPending);
|
|
252
|
-
const pendingForAgent = await fetchPendingApprovals(source.baseUrl, source.apiPrefix);
|
|
253
|
-
setApprovals(pendingForAgent);
|
|
254
|
-
}
|
|
255
|
-
catch (error) {
|
|
256
|
-
if (mode === 'server') {
|
|
257
|
-
setApprovalError(error instanceof Error
|
|
258
|
-
? error.message
|
|
259
|
-
: 'Failed to fetch server approvals');
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}, [isReady, mode, aiAgentsBaseUrl, agentBaseUrl, agentId, authFetch]);
|
|
263
|
-
const resolveLocalApprovalForTool = useCallback(async (toolName, toolArgs) => {
|
|
264
|
-
const signature = approvalSignature(toolName, toolArgs);
|
|
265
|
-
const matchFromState = localApprovals.find(approval => approvalSignature(approval.tool_name, approval.tool_args ?? {}) ===
|
|
266
|
-
signature);
|
|
267
|
-
if (matchFromState) {
|
|
268
|
-
return matchFromState;
|
|
269
|
-
}
|
|
270
|
-
const res = await authFetch(`${agentBaseUrl}/api/v1/tool-approvals`);
|
|
271
|
-
if (!res.ok) {
|
|
272
|
-
return null;
|
|
273
|
-
}
|
|
274
|
-
const data = await res.json();
|
|
275
|
-
const allApprovals = Array.isArray(data)
|
|
276
|
-
? data
|
|
277
|
-
: (data.approvals ?? data.requests ?? []);
|
|
278
|
-
const pendingLocal = allApprovals
|
|
279
|
-
.filter((approval) => approval.status === 'pending' &&
|
|
280
|
-
(approval.agent_id === agentId || !approval.agent_id))
|
|
281
|
-
.map((approval) => ({
|
|
282
|
-
...approval,
|
|
283
|
-
backendBaseUrl: agentBaseUrl,
|
|
284
|
-
apiPrefix: '/api/v1/tool-approvals',
|
|
285
|
-
}));
|
|
286
|
-
setLocalApprovals(pendingLocal);
|
|
287
|
-
return (pendingLocal.find(approval => approvalSignature(approval.tool_name, approval.tool_args ?? {}) ===
|
|
288
|
-
signature) || null);
|
|
289
|
-
}, [localApprovals, authFetch, agentBaseUrl, agentId]);
|
|
290
|
-
useEffect(() => {
|
|
291
|
-
void pollApprovals();
|
|
292
|
-
const interval = setInterval(pollApprovals, 5000);
|
|
293
|
-
return () => clearInterval(interval);
|
|
294
|
-
}, [pollApprovals]);
|
|
295
|
-
useEffect(() => {
|
|
296
|
-
if (!isReady || mode !== 'server') {
|
|
297
|
-
setWsState('closed');
|
|
298
|
-
return;
|
|
299
|
-
}
|
|
300
|
-
const wsUrl = toWsUrl(aiAgentsBaseUrl, `${AI_AGENTS_API_PREFIX}/ws`, chatAuthToken);
|
|
301
|
-
if (!wsUrl) {
|
|
302
|
-
setWsState('closed');
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
let closedByCleanup = false;
|
|
306
|
-
setWsState('connecting');
|
|
307
|
-
const ws = new WebSocket(wsUrl);
|
|
308
|
-
ws.onopen = () => {
|
|
309
|
-
setWsState('connected');
|
|
310
|
-
void pollApprovals();
|
|
311
|
-
};
|
|
312
|
-
ws.onmessage = event => {
|
|
313
|
-
try {
|
|
314
|
-
const payload = JSON.parse(String(event.data));
|
|
315
|
-
const wsEvent = payload?.event;
|
|
316
|
-
const approval = payload?.data;
|
|
317
|
-
if (!wsEvent || !approval) {
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
if (wsEvent === 'tool_approval_created') {
|
|
321
|
-
if (approval.status !== 'pending' ||
|
|
322
|
-
(approval.agent_id && approval.agent_id !== agentId)) {
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
|
-
setApprovals(prev => {
|
|
326
|
-
const next = prev.filter(item => item.id !== approval.id);
|
|
327
|
-
next.unshift({
|
|
328
|
-
...approval,
|
|
329
|
-
backendBaseUrl: aiAgentsBaseUrl,
|
|
330
|
-
apiPrefix: `${AI_AGENTS_API_PREFIX}/tool-approvals`,
|
|
331
|
-
});
|
|
332
|
-
return next;
|
|
333
|
-
});
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
if (wsEvent === 'tool_approval_approved' ||
|
|
337
|
-
wsEvent === 'tool_approval_rejected') {
|
|
338
|
-
// Remove from the server approval queue.
|
|
339
|
-
setApprovals(prev => prev.filter(item => item.id !== approval.id));
|
|
340
|
-
// Bridge the decision to the local agent-runtimes approval.
|
|
341
|
-
const action = wsEvent === 'tool_approval_approved' ? 'approve' : 'reject';
|
|
342
|
-
void (async () => {
|
|
343
|
-
try {
|
|
344
|
-
const approved = wsEvent === 'tool_approval_approved';
|
|
345
|
-
emitServerToolDecision(approval.tool_name, approval.tool_args ?? {}, approved, approval.id, approval.note);
|
|
346
|
-
const localMatch = await resolveLocalRef.current(approval.tool_name, approval.tool_args ?? {});
|
|
347
|
-
if (localMatch) {
|
|
348
|
-
const localBaseUrl = `${agentBaseUrl}/api/v1/tool-approvals/${localMatch.id}/${action}`;
|
|
349
|
-
await authFetchRef.current(localBaseUrl, {
|
|
350
|
-
method: 'POST',
|
|
351
|
-
body: JSON.stringify(approval.note ? { note: approval.note } : {}),
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
catch {
|
|
356
|
-
// Local bridge errors are non-fatal; poll will reconcile.
|
|
357
|
-
}
|
|
358
|
-
})();
|
|
359
|
-
return;
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
catch {
|
|
363
|
-
// Ignore malformed WS payloads.
|
|
364
|
-
}
|
|
365
|
-
};
|
|
366
|
-
ws.onclose = () => {
|
|
367
|
-
if (!closedByCleanup) {
|
|
368
|
-
setWsState('closed');
|
|
369
|
-
}
|
|
370
|
-
};
|
|
371
|
-
ws.onerror = () => {
|
|
372
|
-
setWsState('closed');
|
|
373
|
-
};
|
|
374
|
-
return () => {
|
|
375
|
-
closedByCleanup = true;
|
|
376
|
-
ws.close();
|
|
377
|
-
setWsState('closed');
|
|
378
|
-
};
|
|
379
|
-
}, [
|
|
380
|
-
isReady,
|
|
381
|
-
mode,
|
|
382
|
-
aiAgentsBaseUrl,
|
|
383
|
-
agentBaseUrl,
|
|
384
|
-
chatAuthToken,
|
|
385
|
-
agentId,
|
|
386
|
-
pollApprovals,
|
|
387
|
-
emitServerToolDecision,
|
|
388
|
-
]);
|
|
389
|
-
// Keep refs in sync so the WS handler always has the latest functions.
|
|
390
|
-
useEffect(() => {
|
|
391
|
-
resolveLocalRef.current = resolveLocalApprovalForTool;
|
|
392
|
-
}, [resolveLocalApprovalForTool]);
|
|
393
|
-
useEffect(() => {
|
|
394
|
-
authFetchRef.current = authFetch;
|
|
395
|
-
}, [authFetch]);
|
|
396
|
-
const approve = useCallback(async (requestId, note) => {
|
|
397
|
-
setApprovalLoading(requestId);
|
|
398
|
-
setApprovalError(null);
|
|
399
|
-
try {
|
|
400
|
-
const approval = approvals.find(item => item.id === requestId);
|
|
401
|
-
const baseUrl = approval?.backendBaseUrl ||
|
|
402
|
-
(mode === 'server' ? aiAgentsBaseUrl : agentBaseUrl);
|
|
403
|
-
const apiPrefix = approval?.apiPrefix ||
|
|
404
|
-
(mode === 'server'
|
|
405
|
-
? `${AI_AGENTS_API_PREFIX}/tool-approvals`
|
|
406
|
-
: '/api/v1/tool-approvals');
|
|
407
|
-
const response = await authFetch(`${baseUrl}${apiPrefix}/${requestId}/approve`, {
|
|
408
|
-
method: 'POST',
|
|
409
|
-
body: JSON.stringify(note ? { note } : {}),
|
|
410
|
-
});
|
|
411
|
-
if (!response.ok) {
|
|
412
|
-
const errorText = await response.text().catch(() => '');
|
|
413
|
-
throw new Error(errorText ||
|
|
414
|
-
`Failed to approve request (${response.status} ${response.statusText})`);
|
|
415
|
-
}
|
|
416
|
-
// In server mode, UI/continuation updates are websocket-driven only.
|
|
417
|
-
// The click only sends approve to the server; local chat reacts when
|
|
418
|
-
// tool_approval_approved is received.
|
|
419
|
-
if (mode !== 'server') {
|
|
420
|
-
setApprovals(prev => prev.filter(a => a.id !== requestId));
|
|
421
|
-
void pollApprovals();
|
|
422
|
-
}
|
|
423
|
-
return true;
|
|
424
|
-
}
|
|
425
|
-
catch (error) {
|
|
426
|
-
const message = error instanceof Error
|
|
427
|
-
? error.message
|
|
428
|
-
: 'Failed to approve tool request';
|
|
429
|
-
setApprovalError(message);
|
|
430
|
-
return false;
|
|
431
|
-
}
|
|
432
|
-
finally {
|
|
433
|
-
setApprovalLoading(null);
|
|
434
|
-
}
|
|
435
|
-
}, [
|
|
436
|
-
approvals,
|
|
437
|
-
mode,
|
|
438
|
-
aiAgentsBaseUrl,
|
|
439
|
-
agentBaseUrl,
|
|
440
|
-
authFetch,
|
|
441
|
-
pollApprovals,
|
|
442
|
-
]);
|
|
443
|
-
const reject = useCallback(async (requestId, note) => {
|
|
444
|
-
setApprovalLoading(requestId);
|
|
445
|
-
setApprovalError(null);
|
|
446
|
-
try {
|
|
447
|
-
const approval = approvals.find(item => item.id === requestId);
|
|
448
|
-
const baseUrl = approval?.backendBaseUrl ||
|
|
449
|
-
(mode === 'server' ? aiAgentsBaseUrl : agentBaseUrl);
|
|
450
|
-
const apiPrefix = approval?.apiPrefix ||
|
|
451
|
-
(mode === 'server'
|
|
452
|
-
? `${AI_AGENTS_API_PREFIX}/tool-approvals`
|
|
453
|
-
: '/api/v1/tool-approvals');
|
|
454
|
-
const response = await authFetch(`${baseUrl}${apiPrefix}/${requestId}/reject`, {
|
|
455
|
-
method: 'POST',
|
|
456
|
-
body: JSON.stringify(note ? { note } : {}),
|
|
457
|
-
});
|
|
458
|
-
if (!response.ok) {
|
|
459
|
-
const errorText = await response.text().catch(() => '');
|
|
460
|
-
throw new Error(errorText ||
|
|
461
|
-
`Failed to reject request (${response.status} ${response.statusText})`);
|
|
462
|
-
}
|
|
463
|
-
// In server mode, UI/continuation updates are websocket-driven only.
|
|
464
|
-
// The click only sends reject to the server; local chat reacts when
|
|
465
|
-
// tool_approval_rejected is received.
|
|
466
|
-
if (mode !== 'server') {
|
|
467
|
-
setApprovals(prev => prev.filter(a => a.id !== requestId));
|
|
468
|
-
void pollApprovals();
|
|
469
|
-
}
|
|
470
|
-
return true;
|
|
471
|
-
}
|
|
472
|
-
catch (error) {
|
|
473
|
-
const message = error instanceof Error
|
|
474
|
-
? error.message
|
|
475
|
-
: 'Failed to reject tool request';
|
|
476
|
-
setApprovalError(message);
|
|
477
|
-
return false;
|
|
478
|
-
}
|
|
479
|
-
finally {
|
|
480
|
-
setApprovalLoading(null);
|
|
481
|
-
}
|
|
482
|
-
}, [
|
|
483
|
-
approvals,
|
|
484
|
-
mode,
|
|
485
|
-
aiAgentsBaseUrl,
|
|
486
|
-
agentBaseUrl,
|
|
487
|
-
authFetch,
|
|
488
|
-
pollApprovals,
|
|
489
|
-
]);
|
|
490
|
-
const ensureServerApproval = useCallback(async (toolName, args) => {
|
|
491
|
-
if (mode !== 'server' || !aiAgentsBaseUrl || !isReady) {
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
|
-
const signature = approvalSignature(toolName, args);
|
|
495
|
-
if (createdApprovalSignatures.current.has(signature)) {
|
|
496
|
-
return;
|
|
497
|
-
}
|
|
498
|
-
const alreadyTracked = approvals.some(approval => approvalSignature(approval.tool_name, approval.tool_args ?? {}) ===
|
|
499
|
-
signature);
|
|
500
|
-
if (alreadyTracked) {
|
|
501
|
-
createdApprovalSignatures.current.add(signature);
|
|
502
|
-
return;
|
|
503
|
-
}
|
|
504
|
-
createdApprovalSignatures.current.add(signature);
|
|
505
|
-
try {
|
|
506
|
-
const response = await authFetch(`${aiAgentsBaseUrl}${AI_AGENTS_API_PREFIX}/tool-approvals`, {
|
|
507
|
-
method: 'POST',
|
|
508
|
-
body: JSON.stringify({
|
|
509
|
-
agent_id: agentId,
|
|
510
|
-
pod_name: podName,
|
|
511
|
-
tool_name: toolName,
|
|
512
|
-
tool_args: args,
|
|
513
|
-
}),
|
|
514
|
-
});
|
|
515
|
-
if (!response.ok) {
|
|
516
|
-
const text = await response.text().catch(() => '');
|
|
517
|
-
throw new Error(text ||
|
|
518
|
-
`Failed creating server approval (${response.status} ${response.statusText})`);
|
|
519
|
-
}
|
|
520
|
-
const created = (await response.json());
|
|
521
|
-
setApprovals(prev => {
|
|
522
|
-
const next = prev.filter(item => item.id !== created.id);
|
|
523
|
-
next.unshift({
|
|
524
|
-
...created,
|
|
525
|
-
backendBaseUrl: aiAgentsBaseUrl,
|
|
526
|
-
apiPrefix: `${AI_AGENTS_API_PREFIX}/tool-approvals`,
|
|
527
|
-
});
|
|
528
|
-
return next;
|
|
529
|
-
});
|
|
530
|
-
}
|
|
531
|
-
catch (error) {
|
|
532
|
-
createdApprovalSignatures.current.delete(signature);
|
|
533
|
-
setApprovalError(error instanceof Error
|
|
534
|
-
? error.message
|
|
535
|
-
: 'Failed to create server approval request');
|
|
536
|
-
}
|
|
537
|
-
}, [mode, aiAgentsBaseUrl, isReady, approvals, authFetch, agentId, podName]);
|
|
538
|
-
const pendingApprovals = useMemo(() => approvals.map(req => ({
|
|
539
|
-
id: req.id,
|
|
540
|
-
toolName: req.tool_name,
|
|
541
|
-
toolDescription: req.note,
|
|
542
|
-
args: req.tool_args ?? {},
|
|
543
|
-
agentId,
|
|
544
|
-
requestedAt: req.created_at ?? new Date().toISOString(),
|
|
545
|
-
})), [approvals, agentId]);
|
|
546
|
-
const findMatchingApproval = useCallback((toolName, args) => {
|
|
547
|
-
const normalizedToolName = normalizeToolName(toolName);
|
|
548
|
-
const argsSig = stableStringify(args ?? {});
|
|
549
|
-
return (approvals.find(approval => {
|
|
550
|
-
if (normalizeToolName(approval.tool_name) !== normalizedToolName) {
|
|
551
|
-
return false;
|
|
552
|
-
}
|
|
553
|
-
return stableStringify(approval.tool_args ?? {}) === argsSig;
|
|
554
|
-
}) || null);
|
|
555
|
-
}, [approvals]);
|
|
556
|
-
const handleToolLevelApprove = useCallback(async (toolCallId, requestId, toolName, respond) => {
|
|
557
|
-
const ok = await approve(requestId, 'Approved from tool message card');
|
|
558
|
-
if (ok) {
|
|
559
|
-
if (mode !== 'server') {
|
|
560
|
-
setToolApprovalState(prev => ({ ...prev, [toolCallId]: 'approved' }));
|
|
561
|
-
respond?.({
|
|
562
|
-
type: 'tool-approval-decision',
|
|
563
|
-
approved: true,
|
|
564
|
-
approvalId: requestId,
|
|
565
|
-
toolName,
|
|
566
|
-
});
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
}, [approve, mode]);
|
|
570
|
-
const handleToolLevelDeny = useCallback(async (toolCallId, requestId, toolName, respond) => {
|
|
571
|
-
const ok = await reject(requestId, 'Rejected from tool message card');
|
|
572
|
-
if (ok) {
|
|
573
|
-
if (mode !== 'server') {
|
|
574
|
-
setToolApprovalState(prev => ({ ...prev, [toolCallId]: 'denied' }));
|
|
575
|
-
respond?.({
|
|
576
|
-
type: 'tool-approval-decision',
|
|
577
|
-
approved: false,
|
|
578
|
-
approvalId: requestId,
|
|
579
|
-
toolName,
|
|
580
|
-
});
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
}, [reject, mode]);
|
|
584
|
-
const renderToolResult = useCallback(({ toolCallId, toolName, args, result, status, error, respond }) => {
|
|
585
|
-
const signature = approvalSignature(toolName, args);
|
|
586
|
-
if (respond && status === 'inProgress') {
|
|
587
|
-
toolRespondersRef.current.set(signature, {
|
|
588
|
-
toolCallId,
|
|
589
|
-
toolName,
|
|
590
|
-
respond,
|
|
591
|
-
});
|
|
592
|
-
}
|
|
593
|
-
else if (status === 'complete' || status === 'error') {
|
|
594
|
-
toolRespondersRef.current.delete(signature);
|
|
595
|
-
}
|
|
596
|
-
const matchedApproval = findMatchingApproval(toolName, args);
|
|
597
|
-
const resultObject = result && typeof result === 'object'
|
|
598
|
-
? result
|
|
599
|
-
: undefined;
|
|
600
|
-
const pendingByResult = status === 'inProgress' && resultObject?.pending_approval === true;
|
|
601
|
-
const requiresServerApproval = mode === 'server' &&
|
|
602
|
-
normalizeToolName(toolName) ===
|
|
603
|
-
normalizeToolName('runtime_sensitive_echo');
|
|
604
|
-
if (requiresServerApproval && !matchedApproval) {
|
|
605
|
-
void ensureServerApproval(toolName, args);
|
|
606
|
-
}
|
|
607
|
-
const toolDecision = toolApprovalState[toolCallId];
|
|
608
|
-
const loadingThisApproval = !!matchedApproval && approvalLoading === matchedApproval.id;
|
|
609
|
-
const approvalState = toolDecision ||
|
|
610
|
-
(pendingByResult || requiresServerApproval || !!matchedApproval
|
|
611
|
-
? 'pending'
|
|
612
|
-
: undefined);
|
|
613
|
-
return (_jsx(ToolCallDisplay, { toolCallId: toolCallId, toolName: toolName, args: args, result: result, status: status, error: error, approvalRequired: !!approvalState, approvalState: approvalState, approvalLoading: loadingThisApproval, onApprove: matchedApproval
|
|
614
|
-
? () => void handleToolLevelApprove(toolCallId, matchedApproval.id, toolName, respond)
|
|
615
|
-
: undefined, onDeny: matchedApproval
|
|
616
|
-
? () => void handleToolLevelDeny(toolCallId, matchedApproval.id, toolName, respond)
|
|
617
|
-
: undefined }));
|
|
618
|
-
}, [
|
|
619
|
-
mode,
|
|
620
|
-
findMatchingApproval,
|
|
621
|
-
ensureServerApproval,
|
|
622
|
-
toolApprovalState,
|
|
623
|
-
approvalLoading,
|
|
624
|
-
handleToolLevelApprove,
|
|
625
|
-
handleToolLevelDeny,
|
|
626
|
-
]);
|
|
627
149
|
if (!isReady && runtimeStatus !== 'error') {
|
|
628
150
|
return (_jsxs(Box, { sx: {
|
|
629
151
|
display: 'flex',
|
|
630
152
|
flexDirection: 'column',
|
|
631
153
|
alignItems: 'center',
|
|
632
154
|
justifyContent: 'center',
|
|
633
|
-
height: '
|
|
155
|
+
height: '100%',
|
|
634
156
|
gap: 3,
|
|
635
|
-
}, children: [_jsx(Spinner, { size: "large" }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Launching tool
|
|
157
|
+
}, children: [_jsx(Spinner, { size: "large" }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Launching tool approvals example agent..." })] }));
|
|
636
158
|
}
|
|
637
159
|
if (runtimeStatus === 'error' || hookError) {
|
|
638
160
|
return _jsx(ErrorView, { error: hookError, onLogout: onLogout });
|
|
639
161
|
}
|
|
640
|
-
const serverPanel = mode === 'server' ? (_jsxs(Box, { sx: {
|
|
641
|
-
width: 320,
|
|
642
|
-
minWidth: 280,
|
|
643
|
-
borderLeft: '1px solid',
|
|
644
|
-
borderColor: 'border.default',
|
|
645
|
-
display: 'flex',
|
|
646
|
-
flexDirection: 'column',
|
|
647
|
-
minHeight: 0,
|
|
648
|
-
bg: 'canvas.subtle',
|
|
649
|
-
}, children: [_jsxs(Box, { sx: {
|
|
650
|
-
p: 2,
|
|
651
|
-
borderBottom: '1px solid',
|
|
652
|
-
borderColor: 'border.default',
|
|
653
|
-
}, children: [_jsx(Heading, { as: "h4", sx: { fontSize: 1, mb: 1 }, children: "Server Approval Queue" }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["WebSocket: ", wsState, " \u2022 Pending: ", approvals.length] })] }), _jsx(Box, { sx: { p: 2, overflow: 'auto', flex: 1 }, children: approvals.length === 0 ? (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "No pending approvals." })) : (approvals.map(approval => (_jsxs(Box, { sx: {
|
|
654
|
-
border: '1px solid',
|
|
655
|
-
borderColor: 'border.default',
|
|
656
|
-
borderRadius: 2,
|
|
657
|
-
p: 2,
|
|
658
|
-
mb: 2,
|
|
659
|
-
bg: 'canvas.default',
|
|
660
|
-
}, children: [_jsx(Text, { sx: { fontWeight: 600, fontSize: 1 }, children: approval.tool_name }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', mb: 2 }, children: approval.id }), _jsxs(Box, { sx: { display: 'flex', gap: 1 }, children: [_jsx(Button, { size: "small", variant: "primary", onClick: () => void approve(approval.id), children: "Approve" }), _jsx(Button, { size: "small", variant: "danger", onClick: () => void reject(approval.id, 'Rejected from queue'), children: "Reject" })] })] }, approval.id)))) })] })) : null;
|
|
661
162
|
return (_jsxs(Box, { sx: {
|
|
662
|
-
height: '
|
|
163
|
+
height: '100%',
|
|
164
|
+
minHeight: 0,
|
|
663
165
|
display: 'flex',
|
|
664
166
|
flexDirection: 'column',
|
|
665
167
|
}, children: [isReconnectedAgent && (_jsx(Box, { sx: {
|
|
@@ -667,41 +169,32 @@ const AgentToolApprovalsInner = ({ onLogout, }) => {
|
|
|
667
169
|
py: 1,
|
|
668
170
|
borderBottom: '1px solid',
|
|
669
171
|
borderColor: 'border.default',
|
|
670
|
-
}, children: _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: "Agent already running - reconnected." }) })), _jsx(
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
{
|
|
697
|
-
title: 'Run tool with approval',
|
|
698
|
-
message: "Call the runtime_sensitive_echo tool with text 'hello' and reason 'audit', then reply with the tool result.",
|
|
699
|
-
},
|
|
700
|
-
{
|
|
701
|
-
title: 'Run tool without approval',
|
|
702
|
-
message: "Call the runtime_echo tool with text 'hello world', then reply with the tool result.",
|
|
703
|
-
},
|
|
704
|
-
], renderToolResult: renderToolResult, submitOnSuggestionClick: true }) }), serverPanel] })] }));
|
|
172
|
+
}, children: _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: "Agent already running - reconnected." }) })), _jsx(Box, { sx: { flex: 1, minHeight: 0, display: 'flex' }, children: _jsx(Box, { sx: { flex: 1, minWidth: 0 }, children: _jsx(Chat, { protocol: "vercel-ai", baseUrl: agentBaseUrl, agentId: agentId, authToken: chatAuthToken, title: `Tool Approval Agent - ${podName}`, brandIcon: _jsx(CheckCircleIcon, { size: 16 }), placeholder: "Ask for actions that require approval...", showHeader: true, showNewChatButton: true, showClearButton: false, showTokenUsage: true, autoFocus: true, height: "100%", runtimeId: agentId, historyEndpoint: `${agentBaseUrl}/api/v1/history`, headerActions: _jsx(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: _jsxs(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: ["Pending: ", pendingApprovalCount] }) }), suggestions: [
|
|
173
|
+
{
|
|
174
|
+
title: 'List your tools',
|
|
175
|
+
message: 'list your tools',
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
title: 'Sensitive tool with delegated allow',
|
|
179
|
+
message: "Call the runtime_sensitive_echo tool with text 'hello' and reason 'audit', then explain the before_tool_execute decision and reply with the tool result.",
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
title: 'Sensitive tool denied by Python hook',
|
|
183
|
+
message: "Call the runtime_sensitive_echo tool with text 'danger' and reason 'delete project', then explain why it was denied.",
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
title: 'Non-sensitive tool baseline',
|
|
187
|
+
message: "Call the runtime_echo tool with text 'hello world', then reply with the tool result.",
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
title: 'Inspect audit entries',
|
|
191
|
+
message: 'Use execute_code to print the latest entries from /tmp/agent_runtimes_tool_approvals_audit.jsonl and summarize decision + execution status.',
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
title: 'Explain deferred approvals hook',
|
|
195
|
+
message: 'Explain how deferred_tool_calls resolves approval-required tool requests inline when a decision is already available.',
|
|
196
|
+
},
|
|
197
|
+
], submitOnSuggestionClick: true }) }) })] }));
|
|
705
198
|
};
|
|
706
199
|
const syncTokenToIamStore = (token) => {
|
|
707
200
|
import('@datalayer/core/lib/state').then(({ iamStore }) => {
|
|
@@ -709,7 +202,7 @@ const syncTokenToIamStore = (token) => {
|
|
|
709
202
|
});
|
|
710
203
|
};
|
|
711
204
|
const AgentToolApprovalsExample = () => {
|
|
712
|
-
const { token,
|
|
205
|
+
const { token, clearAuth } = useSimpleAuthStore();
|
|
713
206
|
const hasSynced = useRef(false);
|
|
714
207
|
useEffect(() => {
|
|
715
208
|
if (token && !hasSynced.current) {
|
|
@@ -717,11 +210,6 @@ const AgentToolApprovalsExample = () => {
|
|
|
717
210
|
syncTokenToIamStore(token);
|
|
718
211
|
}
|
|
719
212
|
}, [token]);
|
|
720
|
-
const handleSignIn = useCallback((newToken, handle) => {
|
|
721
|
-
setAuth(newToken, handle);
|
|
722
|
-
hasSynced.current = true;
|
|
723
|
-
syncTokenToIamStore(newToken);
|
|
724
|
-
}, [setAuth]);
|
|
725
213
|
const handleLogout = useCallback(() => {
|
|
726
214
|
clearAuth();
|
|
727
215
|
hasSynced.current = false;
|
|
@@ -730,7 +218,7 @@ const AgentToolApprovalsExample = () => {
|
|
|
730
218
|
});
|
|
731
219
|
}, [clearAuth]);
|
|
732
220
|
if (!token) {
|
|
733
|
-
return (_jsx(ThemedProvider, { children: _jsx(
|
|
221
|
+
return (_jsx(ThemedProvider, { children: _jsx(AuthRequiredView, {}) }));
|
|
734
222
|
}
|
|
735
223
|
return (_jsx(QueryClientProvider, { client: queryClient, children: _jsx(ThemedProvider, { children: _jsx(AgentToolApprovalsInner, { onLogout: handleLogout }) }) }));
|
|
736
224
|
};
|