@datalayer/agent-runtimes 1.0.3 → 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 +35 -119
- 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 -104
- 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 +1118 -141
- 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 +110 -102
- 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 +48 -19
- package/lib/client/AgentsMixin.js +115 -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 +55 -26
- package/lib/components/OutputCard.js +21 -7
- package/lib/components/ToolApprovalCard.js +20 -2
- 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 +14 -34
- package/lib/examples/AgentCodemodeExample.d.ts +4 -6
- package/lib/examples/AgentCodemodeExample.js +591 -175
- package/lib/examples/AgentEvalsExample.js +13 -23
- package/lib/examples/AgentGuardrailsExample.js +371 -71
- 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 +14 -24
- package/lib/examples/AgentMonitoringExample.js +261 -206
- package/lib/examples/AgentNotificationsExample.js +50 -24
- package/lib/examples/AgentOtelExample.js +2 -3
- package/lib/examples/AgentOutputsExample.d.ts +11 -6
- package/lib/examples/AgentOutputsExample.js +383 -88
- 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 +69 -47
- package/lib/examples/AgentSkillsExample.js +92 -106
- 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 +30 -493
- package/lib/examples/AgentTriggersExample.js +1067 -246
- package/lib/examples/ChatCustomExample.js +11 -24
- package/lib/examples/ChatExample.js +9 -34
- 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/ErrorView.d.ts +14 -0
- package/lib/examples/components/ErrorView.js +20 -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 +5 -0
- package/lib/examples/components/index.js +5 -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.d.ts +1 -0
- package/lib/examples/main.js +218 -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 +9 -0
- package/lib/protocols/VercelAIAdapter.js +144 -26
- package/lib/shims/json5.d.ts +4 -0
- package/lib/shims/json5.js +8 -0
- package/lib/specs/agents/agents.d.ts +10 -0
- package/lib/specs/agents/agents.js +752 -24
- package/lib/specs/envvars.d.ts +1 -0
- package/lib/specs/envvars.js +11 -0
- package/lib/specs/events.d.ts +1 -0
- package/lib/specs/events.js +1 -0
- package/lib/specs/index.d.ts +1 -0
- package/lib/specs/index.js +1 -0
- 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 +23 -5
- package/lib/specs/tools.js +3 -0
- 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/types/tools.d.ts +2 -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 +106 -7
- package/scripts/codegen/generate_events.py +47 -17
- package/scripts/codegen/generate_personas.py +319 -0
- package/scripts/codegen/generate_skills.py +9 -9
- package/scripts/codegen/generate_tools.py +20 -0
- package/scripts/sync-jupyter.sh +26 -7
- package/style/primer-primitives.css +1 -6
- 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/scripts/codegen/__pycache__/generate_envvars.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_evals.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_guardrails.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_mcp_servers.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_memory.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_models.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_notifications.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_outputs.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_skills.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_teams.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_tools.cpython-313.pyc +0 -0
- package/scripts/codegen/__pycache__/generate_triggers.cpython-313.pyc +0 -0
- /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
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
4
|
+
* Distributed under the terms of the Modified BSD License.
|
|
5
|
+
*/
|
|
6
|
+
import { useEffect, useState } from 'react';
|
|
7
|
+
import { Text, Spinner } from '@primer/react';
|
|
8
|
+
import { Box, setupPrimerPortals } from '@datalayer/primer-addons';
|
|
9
|
+
import { ThemedProvider } from './utils/themedProvider';
|
|
10
|
+
import { uniqueAgentId } from './utils/agentId';
|
|
11
|
+
import { ErrorView } from './components';
|
|
12
|
+
import { Chat } from '../chat';
|
|
13
|
+
setupPrimerPortals();
|
|
14
|
+
const BASE_URL = 'http://localhost:8765';
|
|
15
|
+
const AGENT_SPEC_ID = 'demo-hooks';
|
|
16
|
+
const AGENT_NAME = 'hooks-demo';
|
|
17
|
+
const AgentHooksExample = () => {
|
|
18
|
+
const [agentId, setAgentId] = useState(null);
|
|
19
|
+
const [error, setError] = useState(null);
|
|
20
|
+
const [isCreating, setIsCreating] = useState(true);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
let cancelled = false;
|
|
23
|
+
const name = uniqueAgentId(AGENT_NAME);
|
|
24
|
+
const createAgent = async () => {
|
|
25
|
+
try {
|
|
26
|
+
const response = await fetch(`${BASE_URL}/api/v1/agents`, {
|
|
27
|
+
method: 'POST',
|
|
28
|
+
headers: { 'Content-Type': 'application/json' },
|
|
29
|
+
body: JSON.stringify({
|
|
30
|
+
name,
|
|
31
|
+
agent_spec_id: AGENT_SPEC_ID,
|
|
32
|
+
transport: 'vercel-ai',
|
|
33
|
+
}),
|
|
34
|
+
});
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
const data = await response
|
|
37
|
+
.json()
|
|
38
|
+
.catch(() => ({ detail: 'Unknown error' }));
|
|
39
|
+
throw new Error(data.detail || `Failed to create agent: ${response.status}`);
|
|
40
|
+
}
|
|
41
|
+
const data = await response.json();
|
|
42
|
+
if (!cancelled) {
|
|
43
|
+
setAgentId(data.id);
|
|
44
|
+
setIsCreating(false);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
if (!cancelled) {
|
|
49
|
+
setError(err instanceof Error ? err.message : 'Failed to create agent');
|
|
50
|
+
setIsCreating(false);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
createAgent();
|
|
55
|
+
return () => {
|
|
56
|
+
cancelled = true;
|
|
57
|
+
};
|
|
58
|
+
}, []);
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
return () => {
|
|
61
|
+
if (!agentId) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
void fetch(`${BASE_URL}/api/v1/agents/${encodeURIComponent(agentId)}`, {
|
|
65
|
+
method: 'DELETE',
|
|
66
|
+
}).catch(() => {
|
|
67
|
+
// Ignore teardown failures in example mode.
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
}, [agentId]);
|
|
71
|
+
if (isCreating) {
|
|
72
|
+
return (_jsx(ThemedProvider, { children: _jsxs(Box, { sx: {
|
|
73
|
+
display: 'flex',
|
|
74
|
+
flexDirection: 'column',
|
|
75
|
+
alignItems: 'center',
|
|
76
|
+
justifyContent: 'center',
|
|
77
|
+
height: '100vh',
|
|
78
|
+
gap: 3,
|
|
79
|
+
bg: 'canvas.default',
|
|
80
|
+
}, children: [_jsx(Spinner, { size: "large" }), _jsxs(Text, { sx: { color: 'fg.muted' }, children: ["Creating agent from ", AGENT_SPEC_ID, "..."] })] }) }));
|
|
81
|
+
}
|
|
82
|
+
if (error || !agentId) {
|
|
83
|
+
return (_jsx(ThemedProvider, { children: _jsx(ErrorView, { error: "Failed to start hooks agent", detail: error || 'No agent ID returned' }) }));
|
|
84
|
+
}
|
|
85
|
+
return (_jsx(Chat, { protocol: "vercel-ai", baseUrl: BASE_URL, agentId: agentId, title: "Hooks Agent", placeholder: "Ask about lifecycle hooks...", description: "Pre-hook installed 'rich', wrote a marker file, and set hook_name / hook_ran_at / hook_env variables in the sandbox", showHeader: true, showModelSelector: true, showToolsMenu: true, showSkillsMenu: true, showTokenUsage: true, showInformation: true, autoFocus: true, height: "100vh", runtimeId: agentId, historyEndpoint: `${BASE_URL}/api/v1/history`, suggestions: [
|
|
86
|
+
{
|
|
87
|
+
title: 'Read the pre-hook marker file',
|
|
88
|
+
message: 'Use execute_code to read /tmp/agent_runtimes_pre_hook_demo.txt and show its contents.',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
title: 'Verify hook variables',
|
|
92
|
+
message: 'Use execute_code to run this verification:\n```python\nassert isinstance(hook_name, str) and hook_name == "demo-hooks:pre", f"❌ hook_name wrong: {hook_name!r}"\nassert isinstance(hook_ran_at, str) and len(hook_ran_at) > 0, f"❌ hook_ran_at wrong: {hook_ran_at!r}"\nassert isinstance(hook_env, dict) and len(hook_env) > 0, f"❌ hook_env wrong: {hook_env!r}"\nprint("✅ hook_name =", hook_name)\nprint("✅ hook_ran_at =", hook_ran_at)\nprint("✅ hook_env =", hook_env)\n```\nThrow an exception with a ❌ message if any variable is missing or has the wrong type, print ✅ lines if all pass.',
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
title: "Verify 'rich' was installed",
|
|
96
|
+
message: 'Use execute_code to import rich and print its version — the pre-hook installed it via pip.',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
title: 'Explain the hook lifecycle',
|
|
100
|
+
message: 'What pre-hooks and post-hooks are configured for this agent, and when does each run?',
|
|
101
|
+
},
|
|
102
|
+
], submitOnSuggestionClick: true }));
|
|
103
|
+
};
|
|
104
|
+
export default AgentHooksExample;
|
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
4
|
+
* Distributed under the terms of the Modified BSD License.
|
|
5
|
+
*/
|
|
6
|
+
/// <reference types="vite/client" />
|
|
7
|
+
import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
|
|
8
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
9
|
+
import { Box } from '@datalayer/primer-addons';
|
|
10
|
+
import { AuthRequiredView, ErrorView } from './components';
|
|
11
|
+
import { Heading, Label, Spinner, Text, Token as PrimerToken, } from '@primer/react';
|
|
12
|
+
import { GlobeIcon, CheckCircleIcon, ServerIcon, XCircleIcon, ToolsIcon, } from '@primer/octicons-react';
|
|
13
|
+
import { useSimpleAuthStore } from '@datalayer/core/lib/views/otel';
|
|
14
|
+
import { ThemedProvider } from './utils/themedProvider';
|
|
15
|
+
import { uniqueAgentId } from './utils/agentId';
|
|
16
|
+
import { Chat } from '../chat';
|
|
17
|
+
import { useAIAgentsWebSocket } from '../hooks';
|
|
18
|
+
import { parseAgentStreamMessage } from '../types/stream';
|
|
19
|
+
import { MCP_STATUS_COLORS, MCP_STATUS_LABELS } from '../types/mcp';
|
|
20
|
+
import { MCP_SERVER_LIBRARY } from '../specs/mcpServers';
|
|
21
|
+
const queryClient = new QueryClient();
|
|
22
|
+
const AGENT_NAME = 'mcp-demo-agent';
|
|
23
|
+
const AGENT_SPEC_ID = 'demo-mcp';
|
|
24
|
+
const DEFAULT_LOCAL_BASE_URL = import.meta.env.VITE_BASE_URL || 'http://localhost:8765';
|
|
25
|
+
/* ── Aggregate MCP status helpers ─────────────────────── */
|
|
26
|
+
function deriveAggregate(servers) {
|
|
27
|
+
if (!servers || servers.length === 0)
|
|
28
|
+
return 'none';
|
|
29
|
+
if (servers.some(s => s.status === 'starting'))
|
|
30
|
+
return 'starting';
|
|
31
|
+
if (servers.some(s => s.status === 'failed'))
|
|
32
|
+
return 'failed';
|
|
33
|
+
if (servers.every(s => s.status === 'started'))
|
|
34
|
+
return 'started';
|
|
35
|
+
return 'not_started';
|
|
36
|
+
}
|
|
37
|
+
/* ── Tool card ────────────────────────────────────────── */
|
|
38
|
+
const McpToolCard = ({ tool }) => {
|
|
39
|
+
const schemaProps = tool.inputSchema
|
|
40
|
+
?.properties;
|
|
41
|
+
const paramNames = schemaProps ? Object.keys(schemaProps) : [];
|
|
42
|
+
return (_jsxs(Box, { sx: {
|
|
43
|
+
border: '1px solid',
|
|
44
|
+
borderColor: 'border.default',
|
|
45
|
+
borderRadius: 2,
|
|
46
|
+
p: 2,
|
|
47
|
+
mb: 2,
|
|
48
|
+
bg: 'canvas.default',
|
|
49
|
+
}, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1, mb: 1 }, children: [_jsx(ToolsIcon, { size: 14 }), _jsx(Text, { sx: { fontWeight: 600, fontSize: 1 }, children: tool.name })] }), tool.description && (_jsx(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', mb: 1, mt: 0 }, children: tool.description })), _jsxs(Text, { sx: {
|
|
50
|
+
fontSize: 0,
|
|
51
|
+
fontFamily: 'mono',
|
|
52
|
+
color: 'fg.muted',
|
|
53
|
+
display: 'block',
|
|
54
|
+
}, children: ["server: ", tool.serverName] }), paramNames.length > 0 && (_jsx(Box, { sx: { mt: 1, display: 'flex', gap: 1, flexWrap: 'wrap' }, children: paramNames.map(p => (_jsx(PrimerToken, { text: p, size: "small" }, p))) }))] }));
|
|
55
|
+
};
|
|
56
|
+
/* ── Server status card ───────────────────────────────── */
|
|
57
|
+
const McpServerCard = ({ server }) => (_jsxs(Box, { sx: {
|
|
58
|
+
p: 2,
|
|
59
|
+
mb: 2,
|
|
60
|
+
border: '1px solid',
|
|
61
|
+
borderColor: 'border.default',
|
|
62
|
+
borderRadius: 2,
|
|
63
|
+
bg: 'canvas.default',
|
|
64
|
+
}, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1, mb: 1 }, children: [server.emoji && _jsx(Text, { sx: { fontSize: 2 }, children: server.emoji }), _jsx(Text, { sx: { fontWeight: 600, fontSize: 1 }, children: server.name }), _jsx(Label, { size: "small", variant: server.status === 'started' ? 'success' : 'secondary', children: server.status })] }), server.description && (_jsx(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', mt: 0, mb: 1 }, children: server.description })), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: [server.toolsCount, " tool", server.toolsCount !== 1 ? 's' : '', " available"] })] }));
|
|
65
|
+
const McpStatusPanel = ({ data }) => {
|
|
66
|
+
const servers = data?.servers ?? [];
|
|
67
|
+
const aggregate = deriveAggregate(servers);
|
|
68
|
+
if (!data) {
|
|
69
|
+
return (_jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "Waiting for websocket snapshot..." }));
|
|
70
|
+
}
|
|
71
|
+
if (aggregate === 'none') {
|
|
72
|
+
return (_jsxs(Box, { sx: {
|
|
73
|
+
p: 2,
|
|
74
|
+
border: '1px solid',
|
|
75
|
+
borderColor: 'border.default',
|
|
76
|
+
borderRadius: 2,
|
|
77
|
+
display: 'flex',
|
|
78
|
+
alignItems: 'center',
|
|
79
|
+
gap: 2,
|
|
80
|
+
}, children: [_jsx(ServerIcon, { size: 16 }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "No MCP server is defined for this agent." })] }));
|
|
81
|
+
}
|
|
82
|
+
return (_jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: [_jsxs(Box, { sx: {
|
|
83
|
+
display: 'flex',
|
|
84
|
+
alignItems: 'center',
|
|
85
|
+
gap: 2,
|
|
86
|
+
}, children: [_jsx(Box, { as: "span", sx: {
|
|
87
|
+
display: 'inline-block',
|
|
88
|
+
width: 10,
|
|
89
|
+
height: 10,
|
|
90
|
+
borderRadius: '50%',
|
|
91
|
+
bg: MCP_STATUS_COLORS[aggregate],
|
|
92
|
+
flexShrink: 0,
|
|
93
|
+
} }), _jsx(Text, { sx: { fontSize: 1 }, children: MCP_STATUS_LABELS[aggregate] })] }), servers.map(s => (_jsx(Box, { sx: {
|
|
94
|
+
p: 2,
|
|
95
|
+
border: '1px solid',
|
|
96
|
+
borderColor: 'border.default',
|
|
97
|
+
borderRadius: 2,
|
|
98
|
+
display: 'flex',
|
|
99
|
+
alignItems: 'center',
|
|
100
|
+
gap: 2,
|
|
101
|
+
}, children: _jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: [s.status === 'started' ? (_jsx(CheckCircleIcon, { size: 14, fill: "success.fg" })) : s.status === 'failed' ? (_jsx(XCircleIcon, { size: 14, fill: "danger.fg" })) : (_jsx(Box, { as: "span", sx: {
|
|
102
|
+
width: 8,
|
|
103
|
+
height: 8,
|
|
104
|
+
borderRadius: '50%',
|
|
105
|
+
bg: MCP_STATUS_COLORS[s.status] ??
|
|
106
|
+
MCP_STATUS_COLORS.not_started,
|
|
107
|
+
display: 'inline-block',
|
|
108
|
+
} })), _jsx(Text, { sx: { fontSize: 1, fontWeight: 'bold' }, children: s.id })] }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: [s.status, s.status === 'started' && s.tools_count !== undefined
|
|
109
|
+
? ` · ${s.tools_count} tool${s.tools_count !== 1 ? 's' : ''}`
|
|
110
|
+
: '', s.status === 'failed' && s.error ? ` — ${s.error}` : ''] })] }) }, s.id)))] }));
|
|
111
|
+
};
|
|
112
|
+
/* ── Main inner component ─────────────────────────────── */
|
|
113
|
+
const AgentMCPInner = ({ onLogout }) => {
|
|
114
|
+
const { token } = useSimpleAuthStore();
|
|
115
|
+
const agentName = useRef(uniqueAgentId(AGENT_NAME)).current;
|
|
116
|
+
const [runtimeStatus, setRuntimeStatus] = useState('launching');
|
|
117
|
+
const [isReady, setIsReady] = useState(false);
|
|
118
|
+
const [hookError, setHookError] = useState(null);
|
|
119
|
+
const [agentId, setAgentId] = useState(agentName);
|
|
120
|
+
const [isReconnectedAgent, setIsReconnectedAgent] = useState(false);
|
|
121
|
+
// MCP server IDs from the agent creation spec (e.g. ["tavily"])
|
|
122
|
+
const [selectedServerIds, setSelectedServerIds] = useState([]);
|
|
123
|
+
// Per-agent tool definitions from WS fullContext.tools
|
|
124
|
+
const [agentTools, setAgentTools] = useState([]);
|
|
125
|
+
// WS-provided mcpStatus (global – used as fallback for indicator)
|
|
126
|
+
const [liveMcpStatus, setLiveMcpStatus] = useState(undefined);
|
|
127
|
+
// Pending approvals are now managed internally by ChatBase via the Zustand
|
|
128
|
+
// agent-runtime store — no local state needed.
|
|
129
|
+
const agentBaseUrl = DEFAULT_LOCAL_BASE_URL;
|
|
130
|
+
const chatAuthToken = token === null ? undefined : token;
|
|
131
|
+
const authFetch = useCallback((url, opts = {}) => fetch(url, {
|
|
132
|
+
...opts,
|
|
133
|
+
headers: {
|
|
134
|
+
'Content-Type': 'application/json',
|
|
135
|
+
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
|
136
|
+
...(opts.headers ?? {}),
|
|
137
|
+
},
|
|
138
|
+
}), [token]);
|
|
139
|
+
// ── Create agent ──────────────────────────────────────
|
|
140
|
+
useEffect(() => {
|
|
141
|
+
let isCancelled = false;
|
|
142
|
+
const createAgent = async () => {
|
|
143
|
+
setRuntimeStatus('launching');
|
|
144
|
+
setIsReady(false);
|
|
145
|
+
setHookError(null);
|
|
146
|
+
setIsReconnectedAgent(false);
|
|
147
|
+
try {
|
|
148
|
+
const response = await authFetch(`${agentBaseUrl}/api/v1/agents`, {
|
|
149
|
+
method: 'POST',
|
|
150
|
+
body: JSON.stringify({
|
|
151
|
+
name: agentName,
|
|
152
|
+
description: 'MCP demo agent – web crawling and research via Tavily',
|
|
153
|
+
agent_library: 'pydantic-ai',
|
|
154
|
+
transport: 'vercel-ai',
|
|
155
|
+
agent_spec_id: AGENT_SPEC_ID,
|
|
156
|
+
enable_codemode: false,
|
|
157
|
+
enable_skills: true,
|
|
158
|
+
tools: [],
|
|
159
|
+
}),
|
|
160
|
+
});
|
|
161
|
+
let resolvedAgentId = agentName;
|
|
162
|
+
let isAlreadyRunning = false;
|
|
163
|
+
if (response.ok) {
|
|
164
|
+
const data = await response.json();
|
|
165
|
+
resolvedAgentId = data?.id || agentName;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
const contentType = response.headers.get('content-type') || '';
|
|
169
|
+
let detail = '';
|
|
170
|
+
if (contentType.includes('application/json')) {
|
|
171
|
+
const data = await response.json().catch(() => null);
|
|
172
|
+
detail =
|
|
173
|
+
(typeof data?.detail === 'string' && data.detail) ||
|
|
174
|
+
(typeof data?.message === 'string' && data.message) ||
|
|
175
|
+
'';
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
detail = await response.text();
|
|
179
|
+
}
|
|
180
|
+
if (response.status === 409 || /already exists/i.test(detail || '')) {
|
|
181
|
+
isAlreadyRunning = true;
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
throw new Error(detail || `Failed to create agent: ${response.status}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (!isCancelled) {
|
|
188
|
+
setAgentId(resolvedAgentId);
|
|
189
|
+
setIsReconnectedAgent(isAlreadyRunning);
|
|
190
|
+
setIsReady(true);
|
|
191
|
+
setRuntimeStatus('ready');
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
if (!isCancelled) {
|
|
196
|
+
setHookError(error instanceof Error ? error.message : 'Agent failed to start');
|
|
197
|
+
setRuntimeStatus('error');
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
void createAgent();
|
|
202
|
+
return () => {
|
|
203
|
+
isCancelled = true;
|
|
204
|
+
};
|
|
205
|
+
}, [agentBaseUrl, agentName, authFetch]);
|
|
206
|
+
// ── WebSocket: receive MCP status + fullContext tools from agent.snapshot ─
|
|
207
|
+
const handleSnapshotMessage = useCallback((message) => {
|
|
208
|
+
try {
|
|
209
|
+
const stream = parseAgentStreamMessage(message?.raw ?? message);
|
|
210
|
+
if (!stream || stream.type !== 'agent.snapshot')
|
|
211
|
+
return;
|
|
212
|
+
const payload = stream.payload;
|
|
213
|
+
if (payload.mcpStatus !== undefined) {
|
|
214
|
+
setLiveMcpStatus(payload.mcpStatus ?? undefined);
|
|
215
|
+
}
|
|
216
|
+
// Extract per-agent tool definitions from fullContext.tools
|
|
217
|
+
const fc = payload.fullContext;
|
|
218
|
+
if (fc && Array.isArray(fc.tools) && fc.tools.length > 0) {
|
|
219
|
+
setAgentTools(fc.tools);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
// Ignore malformed payloads.
|
|
224
|
+
}
|
|
225
|
+
}, []);
|
|
226
|
+
useAIAgentsWebSocket({
|
|
227
|
+
enabled: isReady && Boolean(agentBaseUrl),
|
|
228
|
+
baseUrl: agentBaseUrl,
|
|
229
|
+
path: '/api/v1/tool-approvals/ws',
|
|
230
|
+
queryParams: { agent_id: agentId },
|
|
231
|
+
onMessage: handleSnapshotMessage,
|
|
232
|
+
reconnectDelayMs: attempt => Math.min(1000 * 2 ** Math.max(0, attempt - 1), 10000),
|
|
233
|
+
});
|
|
234
|
+
// ── Fetch creation spec to get selected MCP server IDs ──
|
|
235
|
+
useEffect(() => {
|
|
236
|
+
if (!isReady)
|
|
237
|
+
return;
|
|
238
|
+
const fetchSpec = async () => {
|
|
239
|
+
try {
|
|
240
|
+
const res = await authFetch(`${agentBaseUrl}/api/v1/configure/agents/${agentId}/spec`);
|
|
241
|
+
if (!res.ok)
|
|
242
|
+
return;
|
|
243
|
+
const spec = await res.json();
|
|
244
|
+
const servers = (spec?.selected_mcp_servers ?? []);
|
|
245
|
+
setSelectedServerIds(servers.map(s => s.id));
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
// Non-fatal: sidebar info is informational
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
void fetchSpec();
|
|
252
|
+
}, [isReady, agentId, agentBaseUrl, authFetch]);
|
|
253
|
+
// ── Build McpServerInfo[] from selected servers + catalog + WS tools ──
|
|
254
|
+
const mcpServers = useMemo(() => {
|
|
255
|
+
if (selectedServerIds.length === 0)
|
|
256
|
+
return [];
|
|
257
|
+
return selectedServerIds.map(serverId => {
|
|
258
|
+
const catalogServer = MCP_SERVER_LIBRARY[serverId];
|
|
259
|
+
const serverName = catalogServer?.name ?? serverId;
|
|
260
|
+
// Live status from WS snapshot — the most reliable source of truth.
|
|
261
|
+
const liveServer = liveMcpStatus?.servers?.find(s => s.id === serverId);
|
|
262
|
+
// Match tools by prefix convention: "tavily__tavily_search" → server "tavily"
|
|
263
|
+
// (fallback when live data is not yet available)
|
|
264
|
+
const serverTools = agentTools
|
|
265
|
+
.filter(t => t.name.startsWith(`${serverId}__`))
|
|
266
|
+
.map(t => ({
|
|
267
|
+
name: t.name,
|
|
268
|
+
description: t.description,
|
|
269
|
+
serverId,
|
|
270
|
+
serverName,
|
|
271
|
+
inputSchema: t.parametersSchema,
|
|
272
|
+
}));
|
|
273
|
+
// Prefer live tools from WS snapshot; fall back to agentTools-derived tools.
|
|
274
|
+
const toolsFromLive = (liveServer?.tools ?? []).map(t => ({
|
|
275
|
+
name: t.name,
|
|
276
|
+
description: t.description ?? '',
|
|
277
|
+
serverId,
|
|
278
|
+
serverName,
|
|
279
|
+
inputSchema: undefined,
|
|
280
|
+
}));
|
|
281
|
+
const tools = toolsFromLive.length > 0 ? toolsFromLive : serverTools;
|
|
282
|
+
const status = liveServer?.status ?? (serverTools.length > 0 ? 'started' : 'starting');
|
|
283
|
+
const toolsCount = liveServer?.tools_count ?? tools.length;
|
|
284
|
+
return {
|
|
285
|
+
id: serverId,
|
|
286
|
+
name: serverName,
|
|
287
|
+
description: catalogServer?.description,
|
|
288
|
+
status,
|
|
289
|
+
toolsCount,
|
|
290
|
+
tools,
|
|
291
|
+
emoji: catalogServer?.emoji,
|
|
292
|
+
icon: catalogServer?.icon,
|
|
293
|
+
};
|
|
294
|
+
});
|
|
295
|
+
}, [selectedServerIds, agentTools, liveMcpStatus]);
|
|
296
|
+
// ── Build synthetic McpToolsetsStatusResponse for the Chat MCP indicator ──
|
|
297
|
+
const mcpStatusData = useMemo(() => {
|
|
298
|
+
const derivedEnabledToolsByServer = {};
|
|
299
|
+
const derivedApprovedToolsByServer = {};
|
|
300
|
+
for (const s of mcpServers) {
|
|
301
|
+
if (s.tools.length > 0) {
|
|
302
|
+
derivedEnabledToolsByServer[s.id] = s.tools.map(t => t.name);
|
|
303
|
+
// Keep approval default explicit: enabled but not approved.
|
|
304
|
+
derivedApprovedToolsByServer[s.id] = [];
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
// If the WS-provided global mcpStatus has actual servers, prefer it
|
|
308
|
+
if (liveMcpStatus && liveMcpStatus.servers.length > 0) {
|
|
309
|
+
const live = liveMcpStatus;
|
|
310
|
+
const enabledToolsByServer = {
|
|
311
|
+
...(live.enabled_tools_by_server ?? {}),
|
|
312
|
+
};
|
|
313
|
+
for (const [serverId, tools] of Object.entries(derivedEnabledToolsByServer)) {
|
|
314
|
+
if (!enabledToolsByServer[serverId] ||
|
|
315
|
+
enabledToolsByServer[serverId].length === 0) {
|
|
316
|
+
enabledToolsByServer[serverId] = tools;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
const approvedToolsByServer = {
|
|
320
|
+
...(live.approved_tools_by_server ?? {}),
|
|
321
|
+
};
|
|
322
|
+
for (const [serverId, tools] of Object.entries(derivedApprovedToolsByServer)) {
|
|
323
|
+
if (!approvedToolsByServer[serverId]) {
|
|
324
|
+
approvedToolsByServer[serverId] = tools;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return {
|
|
328
|
+
...live,
|
|
329
|
+
enabled_tools_by_server: enabledToolsByServer,
|
|
330
|
+
approved_tools_by_server: approvedToolsByServer,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
// Otherwise build from our per-agent derived info
|
|
334
|
+
if (mcpServers.length === 0)
|
|
335
|
+
return undefined;
|
|
336
|
+
const servers = mcpServers.map(s => ({
|
|
337
|
+
id: s.id,
|
|
338
|
+
status: s.status,
|
|
339
|
+
tools_count: s.toolsCount,
|
|
340
|
+
tools: s.tools.map(t => ({
|
|
341
|
+
name: t.name,
|
|
342
|
+
description: t.description,
|
|
343
|
+
enabled: true,
|
|
344
|
+
})),
|
|
345
|
+
}));
|
|
346
|
+
const readyServers = servers
|
|
347
|
+
.filter(s => s.status === 'started')
|
|
348
|
+
.map(s => s.id);
|
|
349
|
+
// Build enabled_tools_by_server so the InputFooter ToolsMenu can render
|
|
350
|
+
// tools as enabled immediately on first open.
|
|
351
|
+
const enabledToolsByServer = derivedEnabledToolsByServer;
|
|
352
|
+
const approvedToolsByServer = derivedApprovedToolsByServer;
|
|
353
|
+
return {
|
|
354
|
+
initialized: true,
|
|
355
|
+
ready_count: readyServers.length,
|
|
356
|
+
failed_count: servers.filter(s => s.status === 'failed').length,
|
|
357
|
+
ready_servers: readyServers,
|
|
358
|
+
failed_servers: {},
|
|
359
|
+
servers,
|
|
360
|
+
enabled_tools_by_server: enabledToolsByServer,
|
|
361
|
+
approved_tools_by_server: approvedToolsByServer,
|
|
362
|
+
enabled_tools_count: mcpServers.reduce((sum, s) => sum + s.tools.length, 0),
|
|
363
|
+
};
|
|
364
|
+
}, [liveMcpStatus, mcpServers]);
|
|
365
|
+
const totalTools = mcpServers.reduce((sum, s) => sum + s.toolsCount, 0);
|
|
366
|
+
const aggregate = deriveAggregate(mcpStatusData?.servers ?? []);
|
|
367
|
+
if (!isReady && runtimeStatus !== 'error') {
|
|
368
|
+
return (_jsxs(Box, { sx: {
|
|
369
|
+
display: 'flex',
|
|
370
|
+
flexDirection: 'column',
|
|
371
|
+
alignItems: 'center',
|
|
372
|
+
justifyContent: 'center',
|
|
373
|
+
height: '100vh',
|
|
374
|
+
gap: 3,
|
|
375
|
+
}, children: [_jsx(Spinner, { size: "large" }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Launching MCP demo agent..." })] }));
|
|
376
|
+
}
|
|
377
|
+
if (runtimeStatus === 'error' || hookError) {
|
|
378
|
+
return _jsx(ErrorView, { error: hookError, onLogout: onLogout });
|
|
379
|
+
}
|
|
380
|
+
return (_jsxs(Box, { sx: {
|
|
381
|
+
height: 'calc(100vh - 60px)',
|
|
382
|
+
display: 'flex',
|
|
383
|
+
flexDirection: 'column',
|
|
384
|
+
}, children: [isReconnectedAgent && (_jsx(Box, { sx: {
|
|
385
|
+
px: 3,
|
|
386
|
+
py: 1,
|
|
387
|
+
borderBottom: '1px solid',
|
|
388
|
+
borderColor: 'border.default',
|
|
389
|
+
}, children: _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: "Agent already running - reconnected." }) })), _jsxs(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: "MCP Demo Agent", placeholder: "Ask the agent to search the web or explore GitHub...", showHeader: true, showNewChatButton: true, showClearButton: false, showToolsMenu: true, showSkillsMenu: true, showTokenUsage: true, autoFocus: true, height: "100%", runtimeId: agentId, historyEndpoint: `${agentBaseUrl}/api/v1/history`, mcpStatusData: mcpStatusData, showToolApprovalBanner: true, headerActions: _jsx(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: _jsxs(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: ["MCP Tools: ", totalTools] }) }), suggestions: [
|
|
390
|
+
{
|
|
391
|
+
title: '🔍 Search the web',
|
|
392
|
+
message: 'Search the web for recent news about AI agents.',
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
title: '🐙 GitHub repos',
|
|
396
|
+
message: 'Find trending open-source Python projects on GitHub.',
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
title: '📚 Research topic',
|
|
400
|
+
message: 'Research best practices for building RAG applications.',
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
title: '⚡ Compare frameworks',
|
|
404
|
+
message: 'Compare popular JavaScript frameworks in 2024.',
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
title: '😄 Tell me a joke',
|
|
408
|
+
message: 'Use your jokes skill to tell me a random joke.',
|
|
409
|
+
},
|
|
410
|
+
], submitOnSuggestionClick: true }) }), _jsxs(Box, { sx: {
|
|
411
|
+
width: 320,
|
|
412
|
+
minWidth: 260,
|
|
413
|
+
borderLeft: '1px solid',
|
|
414
|
+
borderColor: 'border.default',
|
|
415
|
+
display: 'flex',
|
|
416
|
+
flexDirection: 'column',
|
|
417
|
+
minHeight: 0,
|
|
418
|
+
bg: 'canvas.subtle',
|
|
419
|
+
}, children: [_jsxs(Box, { sx: {
|
|
420
|
+
p: 2,
|
|
421
|
+
borderBottom: '1px solid',
|
|
422
|
+
borderColor: 'border.default',
|
|
423
|
+
}, children: [_jsx(Heading, { as: "h4", sx: { fontSize: 1, mb: 1 }, children: _jsxs(Box, { sx: { display: 'inline-flex', alignItems: 'center', gap: 1 }, children: [_jsx(ServerIcon, { size: 16 }), "MCP Servers & Tools"] }) }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Box, { as: "span", sx: {
|
|
424
|
+
display: 'inline-block',
|
|
425
|
+
width: 8,
|
|
426
|
+
height: 8,
|
|
427
|
+
borderRadius: '50%',
|
|
428
|
+
bg: MCP_STATUS_COLORS[aggregate],
|
|
429
|
+
flexShrink: 0,
|
|
430
|
+
} }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: [MCP_STATUS_LABELS[aggregate], " \u00B7 ", totalTools, " tool", totalTools !== 1 ? 's' : ''] })] })] }), _jsxs(Box, { sx: { p: 2, overflow: 'auto', flex: 1 }, children: [_jsxs(Box, { sx: { mb: 3 }, children: [_jsx(Heading, { as: "h5", sx: { fontSize: 1, mb: 2 }, children: "MCP Servers" }), _jsx(McpStatusPanel, { data: mcpStatusData })] }), mcpServers.length === 0 ? (_jsxs(Box, { sx: {
|
|
431
|
+
display: 'flex',
|
|
432
|
+
flexDirection: 'column',
|
|
433
|
+
alignItems: 'center',
|
|
434
|
+
gap: 2,
|
|
435
|
+
py: 4,
|
|
436
|
+
}, children: [_jsx(Spinner, { size: "medium" }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Waiting for MCP servers to start..." })] })) : (_jsxs(_Fragment, { children: [mcpServers.map(server => (_jsxs(Box, { children: [_jsx(McpServerCard, { server: server }), server.tools.length > 0 && (_jsx(Box, { sx: { pl: 2 }, children: server.tools.map(tool => (_jsx(McpToolCard, { tool: tool }, `${tool.serverId}-${tool.name}`))) }))] }, server.id))), _jsxs(Box, { sx: {
|
|
437
|
+
mt: 3,
|
|
438
|
+
p: 2,
|
|
439
|
+
borderRadius: 2,
|
|
440
|
+
bg: 'canvas.inset',
|
|
441
|
+
border: '1px solid',
|
|
442
|
+
borderColor: 'border.muted',
|
|
443
|
+
}, children: [_jsx(Heading, { as: "h5", sx: { fontSize: 0, mb: 1 }, children: "MCP (Model Context Protocol)" }), _jsxs(Box, { sx: { fontSize: 0, color: 'fg.muted' }, children: [_jsxs(Box, { sx: {
|
|
444
|
+
display: 'flex',
|
|
445
|
+
alignItems: 'center',
|
|
446
|
+
gap: 1,
|
|
447
|
+
mb: 1,
|
|
448
|
+
}, children: [_jsx(GlobeIcon, { size: 12 }), _jsxs(Text, { children: [_jsx("strong", { children: "Servers:" }), " Discover and start MCP servers that expose tools to the agent"] })] }), _jsxs(Box, { sx: {
|
|
449
|
+
display: 'flex',
|
|
450
|
+
alignItems: 'center',
|
|
451
|
+
gap: 1,
|
|
452
|
+
}, children: [_jsx(ToolsIcon, { size: 12 }), _jsxs(Text, { children: [_jsx("strong", { children: "Tools:" }), " Individual capabilities exposed by each server (search, fetch, etc.)"] })] })] })] })] }))] })] })] })] }));
|
|
453
|
+
};
|
|
454
|
+
const syncTokenToIamStore = (token) => {
|
|
455
|
+
import('@datalayer/core/lib/state').then(({ iamStore }) => {
|
|
456
|
+
iamStore.setState({ token });
|
|
457
|
+
});
|
|
458
|
+
};
|
|
459
|
+
const AgentMCPExample = () => {
|
|
460
|
+
const { token, clearAuth } = useSimpleAuthStore();
|
|
461
|
+
const hasSynced = useRef(false);
|
|
462
|
+
useEffect(() => {
|
|
463
|
+
if (token && !hasSynced.current) {
|
|
464
|
+
hasSynced.current = true;
|
|
465
|
+
syncTokenToIamStore(token);
|
|
466
|
+
}
|
|
467
|
+
}, [token]);
|
|
468
|
+
const handleLogout = useCallback(() => {
|
|
469
|
+
clearAuth();
|
|
470
|
+
hasSynced.current = false;
|
|
471
|
+
import('@datalayer/core/lib/state').then(({ iamStore }) => {
|
|
472
|
+
iamStore.setState({ token: undefined });
|
|
473
|
+
});
|
|
474
|
+
}, [clearAuth]);
|
|
475
|
+
if (!token) {
|
|
476
|
+
return (_jsx(ThemedProvider, { children: _jsx(AuthRequiredView, {}) }));
|
|
477
|
+
}
|
|
478
|
+
return (_jsx(QueryClientProvider, { client: queryClient, children: _jsx(ThemedProvider, { children: _jsx(AgentMCPInner, { onLogout: handleLogout }) }) }));
|
|
479
|
+
};
|
|
480
|
+
export default AgentMCPExample;
|