@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
|
@@ -15,38 +15,49 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
15
15
|
* - Lists recent trigger history and next scheduled run
|
|
16
16
|
*/
|
|
17
17
|
/// <reference types="vite/client" />
|
|
18
|
-
import { useEffect, useState, useCallback, useRef } from 'react';
|
|
18
|
+
import React, { useEffect, useState, useCallback, useRef } from 'react';
|
|
19
19
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
20
20
|
import { Text, Button, Spinner, Heading, Label, TextInput, Flash, Timeline, Truncate, Tooltip, } from '@primer/react';
|
|
21
|
-
import { ClockIcon, SyncIcon, CheckCircleIcon, XCircleIcon, PlayIcon,
|
|
21
|
+
import { ClockIcon, SyncIcon, CheckCircleIcon, XCircleIcon, PlayIcon, GlobeIcon, ZapIcon, EyeIcon, EyeClosedIcon, TrashIcon, CopyIcon, } from '@primer/octicons-react';
|
|
22
22
|
import { Box } from '@datalayer/primer-addons';
|
|
23
|
-
import { ErrorView } from './components';
|
|
23
|
+
import { AuthRequiredView, ErrorView } from './components';
|
|
24
24
|
import { ThemedProvider } from './utils/themedProvider';
|
|
25
|
+
import { uniqueAgentId } from './utils/agentId';
|
|
25
26
|
import { useSimpleAuthStore } from '@datalayer/core/lib/views/otel';
|
|
26
|
-
import { SignInSimple } from '@datalayer/core/lib/views/iam';
|
|
27
|
-
import { UserBadge } from '@datalayer/core/lib/views/profile';
|
|
28
27
|
import { Chat } from '../chat';
|
|
28
|
+
import { useConnectedIdentities } from '../identity';
|
|
29
29
|
import { ToolApprovalBanner, ToolApprovalDialog, } from '../chat/tools';
|
|
30
30
|
import { useAgentEvents, useDeleteAgentEvent, useMarkEventRead, useMarkEventUnread, useAIAgentsWebSocket, } from '../hooks';
|
|
31
31
|
import { VercelAIAdapter } from '../protocols';
|
|
32
32
|
import { createUserMessage } from '../types/messages';
|
|
33
33
|
const queryClient = new QueryClient();
|
|
34
34
|
// ─── Constants ─────────────────────────────────────────────────────────────
|
|
35
|
-
const AGENT_NAME = 'trigger-
|
|
36
|
-
const AGENT_SPEC_ID = '
|
|
37
|
-
const APPROVAL_AGENT_NAME = 'trigger-approval-
|
|
38
|
-
const APPROVAL_AGENT_SPEC_ID = '
|
|
39
|
-
const ONCE_TRIGGER_PROMPT = "List the user's top 3 public and top 3 private GitHub repositories, ranked by recent activity, and provide a brief summary of each.";
|
|
40
|
-
const ONCE_TRIGGER_APPROVAL_PROMPT =
|
|
35
|
+
const AGENT_NAME = 'trigger-example-agent';
|
|
36
|
+
const AGENT_SPEC_ID = 'example-one-trigger';
|
|
37
|
+
const APPROVAL_AGENT_NAME = 'trigger-approval-example-agent';
|
|
38
|
+
const APPROVAL_AGENT_SPEC_ID = 'example-one-trigger-approval';
|
|
39
|
+
const ONCE_TRIGGER_PROMPT = "List the user's top 3 public and top 3 private GitHub repositories, ranked by recent activity, and provide a brief summary of each. Execute exactly two tool calls: run_skill_script(skill_name='github', script_name='list_repos', kwargs={visibility:'public', sort:'updated', limit:3, format:'json'}) and run_skill_script(skill_name='github', script_name='list_repos', kwargs={visibility:'private', sort:'updated', limit:3, format:'json'}). Do not call list_skills/load_skill/read_skill_resource. Do not retry. If a tool call fails, report failure_reason/error/stderr exactly as returned.";
|
|
40
|
+
const ONCE_TRIGGER_APPROVAL_PROMPT = 'Use the runtime_sensitive_echo tool once.';
|
|
41
41
|
const DEFAULT_LOCAL_BASE_URL = import.meta.env.VITE_BASE_URL || 'http://localhost:8765';
|
|
42
42
|
const DEFAULT_CRON = '0 8 * * *'; // daily at 08:00 UTC
|
|
43
43
|
// ─── Inner component (rendered after auth) ─────────────────────────────────
|
|
44
44
|
const AgentTriggerInner = ({ onLogout, }) => {
|
|
45
45
|
const { token } = useSimpleAuthStore();
|
|
46
|
-
const
|
|
46
|
+
const connectedIdentities = useConnectedIdentities();
|
|
47
|
+
const agentName = useRef(uniqueAgentId(AGENT_NAME)).current;
|
|
48
|
+
const approvalAgentName = useRef(uniqueAgentId(APPROVAL_AGENT_NAME)).current;
|
|
49
|
+
const identitiesForRuns = React.useMemo(() => {
|
|
50
|
+
return connectedIdentities
|
|
51
|
+
.filter(identity => identity.token?.accessToken)
|
|
52
|
+
.map(identity => ({
|
|
53
|
+
provider: identity.provider,
|
|
54
|
+
accessToken: identity.token.accessToken,
|
|
55
|
+
}));
|
|
56
|
+
}, [connectedIdentities]);
|
|
57
|
+
const [runtimeStatus, setRuntimeStatus] = useState('idle');
|
|
47
58
|
const [isReady, setIsReady] = useState(false);
|
|
48
59
|
const [hookError, setHookError] = useState(null);
|
|
49
|
-
const [agentId, setAgentId] = useState(
|
|
60
|
+
const [agentId, setAgentId] = useState(agentName);
|
|
50
61
|
const agentBaseUrl = DEFAULT_LOCAL_BASE_URL;
|
|
51
62
|
const chatAuthToken = token === null ? undefined : token;
|
|
52
63
|
// Cron state
|
|
@@ -78,11 +89,21 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
78
89
|
const [eventFilter, setEventFilter] = useState('');
|
|
79
90
|
const [eventSubscribed, setEventSubscribed] = useState(false);
|
|
80
91
|
// Approval agent state
|
|
81
|
-
const [approvalAgentId, setApprovalAgentId] = useState(
|
|
92
|
+
const [approvalAgentId, setApprovalAgentId] = useState(approvalAgentName);
|
|
82
93
|
const [approvalAgentReady, setApprovalAgentReady] = useState(false);
|
|
83
94
|
const [isLaunchingApproval, setIsLaunchingApproval] = useState(false);
|
|
84
|
-
const [approvalFlash, setApprovalFlash] = useState(null);
|
|
85
95
|
const [hasTriggeredApproval, setHasTriggeredApproval] = useState(false);
|
|
96
|
+
const [approvalFlash, setApprovalFlash] = useState(null);
|
|
97
|
+
const toApprovalRequest = useCallback((payload) => ({
|
|
98
|
+
id: payload.id,
|
|
99
|
+
tool_name: payload.tool_name,
|
|
100
|
+
tool_args: payload.tool_args,
|
|
101
|
+
tool_call_id: payload.tool_call_id ?? undefined,
|
|
102
|
+
note: payload.note ?? undefined,
|
|
103
|
+
created_at: payload.created_at,
|
|
104
|
+
status: payload.status,
|
|
105
|
+
agent_id: payload.agent_id,
|
|
106
|
+
}), []);
|
|
86
107
|
const [approvals, setApprovals] = useState([]);
|
|
87
108
|
const [approvalLoading, setApprovalLoading] = useState(null);
|
|
88
109
|
const [approvalError, setApprovalError] = useState(null);
|
|
@@ -96,10 +117,99 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
96
117
|
const markReadMutation = useMarkEventRead(agentId);
|
|
97
118
|
const markUnreadMutation = useMarkEventUnread(agentId);
|
|
98
119
|
const agentEvents = eventsQuery.data?.events ?? [];
|
|
99
|
-
// WebSocket
|
|
100
|
-
|
|
120
|
+
// ── WebSocket to datalayer-ai-agents: agent events + tool approvals ─────
|
|
121
|
+
// Single connection handles both agent:{agentId} channel events and the
|
|
122
|
+
// user's own channel (auto-subscribed) for tool_approval_* events.
|
|
123
|
+
// This mirrors the approval flow used by the /agents/tool-approvals UI page
|
|
124
|
+
// so that approving from either surface produces identical behaviour.
|
|
125
|
+
const handleAIAgentsMessage = useCallback((msg) => {
|
|
126
|
+
const event = msg.event;
|
|
127
|
+
const data = msg.data;
|
|
128
|
+
// Handle tool-approvals-history response (seeds initial pending list).
|
|
129
|
+
if (msg.type === 'tool-approvals-history') {
|
|
130
|
+
const rawList = Array.isArray(data?.approvals) ? data.approvals : [];
|
|
131
|
+
const pending = rawList.filter(a => (!a.agent_id || a.agent_id === approvalAgentId) &&
|
|
132
|
+
a.status === 'pending');
|
|
133
|
+
setApprovals(pending.map(toApprovalRequest));
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (!event)
|
|
137
|
+
return;
|
|
138
|
+
if (event === 'tool_approval_created') {
|
|
139
|
+
const approval = toApprovalRequest(data);
|
|
140
|
+
if (approval.agent_id && approval.agent_id !== approvalAgentId)
|
|
141
|
+
return;
|
|
142
|
+
setApprovals(prev => [
|
|
143
|
+
approval,
|
|
144
|
+
...prev.filter(a => a.id !== approval.id),
|
|
145
|
+
]);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (event === 'tool_approval_approved' ||
|
|
149
|
+
event === 'tool_approval_rejected') {
|
|
150
|
+
const record = data;
|
|
151
|
+
if (record?.agent_id && record.agent_id !== approvalAgentId)
|
|
152
|
+
return;
|
|
153
|
+
setApprovals(prev => prev.filter(a => a.id !== record?.id));
|
|
154
|
+
// When approved and we have a live deferred-tool stream, send the
|
|
155
|
+
// continuation so the agent can execute the tool and produce output.
|
|
156
|
+
// This path fires when the user approves from a DIFFERENT UI surface
|
|
157
|
+
// (e.g. /agents/tool-approvals page) rather than this panel.
|
|
158
|
+
// handleApprove() clears approvalStreamRef before reaching here, so
|
|
159
|
+
// this block only runs for external approvals (no double-send).
|
|
160
|
+
if (event === 'tool_approval_approved') {
|
|
161
|
+
const stream = approvalStreamRef.current;
|
|
162
|
+
if (stream) {
|
|
163
|
+
approvalStreamRef.current = null;
|
|
164
|
+
const toolCallId = stream.adapter.getDeferredToolCallId(record?.tool_name ?? '');
|
|
165
|
+
if (toolCallId) {
|
|
166
|
+
void stream.adapter
|
|
167
|
+
.sendToolResult(toolCallId, {
|
|
168
|
+
toolCallId,
|
|
169
|
+
success: true,
|
|
170
|
+
result: {
|
|
171
|
+
approved: true,
|
|
172
|
+
approvalId: record?.id,
|
|
173
|
+
message: 'Approved from external UI',
|
|
174
|
+
},
|
|
175
|
+
})
|
|
176
|
+
.finally(() => {
|
|
177
|
+
stream.unsubscribe();
|
|
178
|
+
stream.adapter.disconnect();
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
stream.unsubscribe();
|
|
183
|
+
stream.adapter.disconnect();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}, [approvalAgentId, toApprovalRequest]);
|
|
189
|
+
const { send: sendToAIAgents, connectionState: aiAgentsConnectionState } = useAIAgentsWebSocket({
|
|
101
190
|
channels: agentId ? [`agent:${agentId}`] : [],
|
|
191
|
+
onMessage: handleAIAgentsMessage,
|
|
102
192
|
});
|
|
193
|
+
// Request pending approvals for the active approval agent once connected.
|
|
194
|
+
const approvalHistoryAskedRef = useRef(false);
|
|
195
|
+
useEffect(() => {
|
|
196
|
+
if (aiAgentsConnectionState !== 'connected' ||
|
|
197
|
+
!hasTriggeredApproval ||
|
|
198
|
+
!approvalAgentId) {
|
|
199
|
+
approvalHistoryAskedRef.current = false;
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (approvalHistoryAskedRef.current)
|
|
203
|
+
return;
|
|
204
|
+
approvalHistoryAskedRef.current = sendToAIAgents({
|
|
205
|
+
type: 'tool-approvals-history',
|
|
206
|
+
});
|
|
207
|
+
}, [
|
|
208
|
+
aiAgentsConnectionState,
|
|
209
|
+
hasTriggeredApproval,
|
|
210
|
+
approvalAgentId,
|
|
211
|
+
sendToAIAgents,
|
|
212
|
+
]);
|
|
103
213
|
// Authenticated fetch helper (for sidecar endpoints)
|
|
104
214
|
const authFetch = useCallback((url, opts = {}) => fetch(url, {
|
|
105
215
|
...opts,
|
|
@@ -109,68 +219,64 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
109
219
|
...(opts.headers ?? {}),
|
|
110
220
|
},
|
|
111
221
|
}), [token]);
|
|
112
|
-
// ── Create agent on
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
222
|
+
// ── Create agent on demand (no startup on initial page load) ────────────
|
|
223
|
+
const createAgent = useCallback(async () => {
|
|
224
|
+
setRuntimeStatus('launching');
|
|
225
|
+
setIsReady(false);
|
|
226
|
+
setHookError(null);
|
|
227
|
+
try {
|
|
228
|
+
const response = await authFetch(`${agentBaseUrl}/api/v1/agents`, {
|
|
229
|
+
method: 'POST',
|
|
230
|
+
body: JSON.stringify({
|
|
231
|
+
name: agentName,
|
|
232
|
+
description: 'Agent with cron, webhook, event, and manual triggers',
|
|
233
|
+
agent_library: 'pydantic-ai',
|
|
234
|
+
transport: 'vercel-ai',
|
|
235
|
+
agent_spec_id: AGENT_SPEC_ID,
|
|
236
|
+
}),
|
|
237
|
+
});
|
|
238
|
+
let resolvedAgentId = agentName;
|
|
239
|
+
if (response.ok) {
|
|
240
|
+
const data = await response.json();
|
|
241
|
+
resolvedAgentId = data?.id || agentName;
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
const contentType = response.headers.get('content-type') || '';
|
|
245
|
+
let detail = '';
|
|
246
|
+
if (contentType.includes('application/json')) {
|
|
247
|
+
const data = await response.json().catch(() => null);
|
|
248
|
+
detail =
|
|
249
|
+
(typeof data?.detail === 'string' && data.detail) ||
|
|
250
|
+
(typeof data?.message === 'string' && data.message) ||
|
|
251
|
+
'';
|
|
135
252
|
}
|
|
136
253
|
else {
|
|
137
|
-
|
|
138
|
-
let detail = '';
|
|
139
|
-
if (contentType.includes('application/json')) {
|
|
140
|
-
const data = await response.json().catch(() => null);
|
|
141
|
-
detail =
|
|
142
|
-
(typeof data?.detail === 'string' && data.detail) ||
|
|
143
|
-
(typeof data?.message === 'string' && data.message) ||
|
|
144
|
-
'';
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
detail = await response.text();
|
|
148
|
-
}
|
|
149
|
-
if (response.status === 409 || /already exists/i.test(detail || '')) {
|
|
150
|
-
// Agent already running — reuse it
|
|
151
|
-
}
|
|
152
|
-
else {
|
|
153
|
-
throw new Error(detail || `Failed to create agent: ${response.status}`);
|
|
154
|
-
}
|
|
254
|
+
detail = await response.text();
|
|
155
255
|
}
|
|
156
|
-
if (
|
|
157
|
-
|
|
158
|
-
setIsReady(true);
|
|
159
|
-
setRuntimeStatus('ready');
|
|
256
|
+
if (response.status === 409 || /already exists/i.test(detail || '')) {
|
|
257
|
+
// Agent already running — reuse it
|
|
160
258
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if (!isCancelled) {
|
|
164
|
-
setHookError(error instanceof Error ? error.message : 'Agent failed to start');
|
|
165
|
-
setRuntimeStatus('error');
|
|
259
|
+
else {
|
|
260
|
+
throw new Error(detail || `Failed to create agent: ${response.status}`);
|
|
166
261
|
}
|
|
167
262
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
|
|
263
|
+
setAgentId(resolvedAgentId);
|
|
264
|
+
setIsReady(true);
|
|
265
|
+
setRuntimeStatus('ready');
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
setHookError(error instanceof Error ? error.message : 'Agent failed to start');
|
|
270
|
+
setRuntimeStatus('error');
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
}, [agentBaseUrl, authFetch, agentName]);
|
|
274
|
+
const ensureRuntimeReady = useCallback(async () => {
|
|
275
|
+
if (isReady) {
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
return createAgent();
|
|
279
|
+
}, [isReady, createAgent]);
|
|
174
280
|
// ── Poll trigger metadata ─────────────────────────────────────────────
|
|
175
281
|
// TODO: enable once the ai-agents service exposes /trigger and
|
|
176
282
|
// /trigger/history endpoints on the platform API.
|
|
@@ -247,7 +353,9 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
247
353
|
try {
|
|
248
354
|
setError(null);
|
|
249
355
|
await adapter.connect();
|
|
250
|
-
await adapter.sendMessage(createUserMessage(prompt)
|
|
356
|
+
await adapter.sendMessage(createUserMessage(prompt), {
|
|
357
|
+
identities: identitiesForRuns,
|
|
358
|
+
});
|
|
251
359
|
return { finalAssistantText: latestAssistantText, pendingApproval };
|
|
252
360
|
}
|
|
253
361
|
catch (error) {
|
|
@@ -255,7 +363,7 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
255
363
|
throw error;
|
|
256
364
|
}
|
|
257
365
|
finally {
|
|
258
|
-
if (options?.keepAliveForApproval
|
|
366
|
+
if (options?.keepAliveForApproval) {
|
|
259
367
|
approvalStreamRef.current = { adapter, unsubscribe };
|
|
260
368
|
}
|
|
261
369
|
else {
|
|
@@ -263,7 +371,7 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
263
371
|
adapter.disconnect();
|
|
264
372
|
}
|
|
265
373
|
}
|
|
266
|
-
}, [agentBaseUrl, token, upsertSidebarMessage]);
|
|
374
|
+
}, [agentBaseUrl, token, identitiesForRuns, upsertSidebarMessage]);
|
|
267
375
|
useEffect(() => {
|
|
268
376
|
return () => {
|
|
269
377
|
if (approvalStreamRef.current) {
|
|
@@ -273,83 +381,62 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
273
381
|
}
|
|
274
382
|
};
|
|
275
383
|
}, []);
|
|
276
|
-
// ── Create approval agent on
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
384
|
+
// ── Create approval agent on demand ─────────────────────────────────────
|
|
385
|
+
const createApprovalAgent = useCallback(async () => {
|
|
386
|
+
const createRequest = () => authFetch(`${agentBaseUrl}/api/v1/agents`, {
|
|
387
|
+
method: 'POST',
|
|
388
|
+
body: JSON.stringify({
|
|
389
|
+
name: approvalAgentName,
|
|
390
|
+
description: 'Agent with once trigger and tool approval',
|
|
391
|
+
agent_library: 'pydantic-ai',
|
|
392
|
+
transport: 'vercel-ai',
|
|
393
|
+
agent_spec_id: APPROVAL_AGENT_SPEC_ID,
|
|
394
|
+
enable_codemode: false,
|
|
395
|
+
}),
|
|
396
|
+
});
|
|
397
|
+
try {
|
|
398
|
+
let response = await createRequest();
|
|
399
|
+
let resolvedId = approvalAgentName;
|
|
400
|
+
if (response.ok) {
|
|
401
|
+
const data = await response.json();
|
|
402
|
+
resolvedId = data?.id || approvalAgentName;
|
|
403
|
+
}
|
|
404
|
+
else {
|
|
405
|
+
const contentType = response.headers.get('content-type') || '';
|
|
406
|
+
let detail = '';
|
|
407
|
+
if (contentType.includes('application/json')) {
|
|
408
|
+
const data = await response.json().catch(() => null);
|
|
409
|
+
detail = data?.detail || data?.message || '';
|
|
298
410
|
}
|
|
299
411
|
else {
|
|
300
|
-
|
|
301
|
-
let detail = '';
|
|
302
|
-
if (contentType.includes('application/json')) {
|
|
303
|
-
const data = await response.json().catch(() => null);
|
|
304
|
-
detail = data?.detail || data?.message || '';
|
|
305
|
-
}
|
|
306
|
-
else {
|
|
307
|
-
detail = await response.text();
|
|
308
|
-
}
|
|
309
|
-
if (response.status === 409 || /already exists/i.test(detail || '')) {
|
|
310
|
-
// Already running — reuse
|
|
311
|
-
}
|
|
312
|
-
else {
|
|
313
|
-
console.warn('Failed to create approval agent:', detail);
|
|
314
|
-
return;
|
|
315
|
-
}
|
|
412
|
+
detail = await response.text();
|
|
316
413
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
414
|
+
const alreadyExists = response.status === 409 || /already exists/i.test(detail || '');
|
|
415
|
+
if (!alreadyExists) {
|
|
416
|
+
console.warn('Failed to create approval agent:', detail);
|
|
417
|
+
return false;
|
|
320
418
|
}
|
|
419
|
+
// Ensure latest spec/config is applied instead of reusing stale agent state.
|
|
420
|
+
await authFetch(`${agentBaseUrl}/api/v1/agents/${encodeURIComponent(approvalAgentName)}`, {
|
|
421
|
+
method: 'DELETE',
|
|
422
|
+
}).catch(() => undefined);
|
|
423
|
+
response = await createRequest();
|
|
424
|
+
if (!response.ok) {
|
|
425
|
+
console.warn('Failed to recreate approval agent after conflict');
|
|
426
|
+
return false;
|
|
427
|
+
}
|
|
428
|
+
const recreated = await response.json().catch(() => null);
|
|
429
|
+
resolvedId = recreated?.id || approvalAgentName;
|
|
321
430
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
};
|
|
326
|
-
void createApprovalAgent();
|
|
327
|
-
return () => { isCancelled = true; };
|
|
328
|
-
}, [isReady, agentBaseUrl, authFetch]);
|
|
329
|
-
// ── Poll tool approvals ─────────────────────────────────────────────────
|
|
330
|
-
const pollApprovals = useCallback(async () => {
|
|
331
|
-
if (!approvalAgentReady)
|
|
332
|
-
return;
|
|
333
|
-
try {
|
|
334
|
-
const res = await authFetch(`${agentBaseUrl}/api/v1/tool-approvals`);
|
|
335
|
-
if (!res.ok)
|
|
336
|
-
return;
|
|
337
|
-
const data = await res.json();
|
|
338
|
-
const all = Array.isArray(data) ? data : (data.approvals ?? data.requests ?? []);
|
|
339
|
-
const pending = all.filter((a) => a.status === 'pending' && (!a.agent_id || a.agent_id === approvalAgentId));
|
|
340
|
-
setApprovals(pending);
|
|
431
|
+
setApprovalAgentId(resolvedId);
|
|
432
|
+
setApprovalAgentReady(true);
|
|
433
|
+
return true;
|
|
341
434
|
}
|
|
342
|
-
catch {
|
|
343
|
-
|
|
435
|
+
catch (error) {
|
|
436
|
+
console.warn('Approval agent creation failed:', error);
|
|
437
|
+
return false;
|
|
344
438
|
}
|
|
345
|
-
}, [
|
|
346
|
-
useEffect(() => {
|
|
347
|
-
if (!hasTriggeredApproval)
|
|
348
|
-
return;
|
|
349
|
-
void pollApprovals();
|
|
350
|
-
const interval = setInterval(pollApprovals, 2000);
|
|
351
|
-
return () => clearInterval(interval);
|
|
352
|
-
}, [hasTriggeredApproval, pollApprovals]);
|
|
439
|
+
}, [agentBaseUrl, authFetch, approvalAgentName]);
|
|
353
440
|
// Approval sidebar messages are populated from live Vercel stream events.
|
|
354
441
|
// ── Approve / Reject handlers ───────────────────────────────────────────
|
|
355
442
|
const handleApprove = useCallback(async (requestId) => {
|
|
@@ -357,76 +444,116 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
357
444
|
setApprovalError(null);
|
|
358
445
|
try {
|
|
359
446
|
const approval = approvals.find(a => a.id === requestId);
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
|
|
447
|
+
const stream = approvalStreamRef.current;
|
|
448
|
+
// Send the decision via the datalayer-ai-agents WS (same path as the
|
|
449
|
+
// /agents/tool-approvals UI page).
|
|
450
|
+
const sentDecision = sendToAIAgents({
|
|
451
|
+
type: 'tool_approval_decision',
|
|
452
|
+
approvalId: requestId,
|
|
453
|
+
approved: true,
|
|
454
|
+
});
|
|
455
|
+
if (!sentDecision && !stream) {
|
|
456
|
+
throw new Error('AI Agents WebSocket is not connected and no local approval stream is active');
|
|
363
457
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
result: {
|
|
369
|
-
approved: true,
|
|
370
|
-
approvalId: requestId,
|
|
371
|
-
message: 'Approved from trigger panel',
|
|
372
|
-
},
|
|
373
|
-
});
|
|
374
|
-
approvalStreamRef.current.unsubscribe();
|
|
375
|
-
approvalStreamRef.current.adapter.disconnect();
|
|
458
|
+
// Send the Vercel AI continuation so the agent can execute the tool.
|
|
459
|
+
// Clear approvalStreamRef FIRST so the incoming tool_approval_approved
|
|
460
|
+
// WS event doesn't trigger a duplicate sendToolResult.
|
|
461
|
+
if (stream) {
|
|
376
462
|
approvalStreamRef.current = null;
|
|
463
|
+
const toolCallId = approval?.tool_call_id ??
|
|
464
|
+
stream.adapter.getDeferredToolCallId(approval?.tool_name ?? '');
|
|
465
|
+
if (toolCallId) {
|
|
466
|
+
await stream.adapter.sendToolResult(toolCallId, {
|
|
467
|
+
toolCallId,
|
|
468
|
+
success: true,
|
|
469
|
+
result: {
|
|
470
|
+
approved: true,
|
|
471
|
+
approvalId: requestId,
|
|
472
|
+
message: 'Approved from trigger panel',
|
|
473
|
+
},
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
stream.unsubscribe();
|
|
477
|
+
stream.adapter.disconnect();
|
|
377
478
|
}
|
|
378
479
|
setApprovals(prev => prev.filter(a => a.id !== requestId));
|
|
379
|
-
|
|
480
|
+
return true;
|
|
380
481
|
}
|
|
381
482
|
catch (error) {
|
|
382
483
|
setApprovalError(error instanceof Error ? error.message : 'Failed to approve');
|
|
484
|
+
return false;
|
|
383
485
|
}
|
|
384
486
|
finally {
|
|
385
487
|
setApprovalLoading(null);
|
|
386
488
|
}
|
|
387
|
-
}, [
|
|
489
|
+
}, [approvals, sendToAIAgents]);
|
|
388
490
|
const handleReject = useCallback(async (requestId, note) => {
|
|
389
491
|
setApprovalLoading(requestId);
|
|
390
492
|
setApprovalError(null);
|
|
391
493
|
try {
|
|
392
494
|
const approval = approvals.find(a => a.id === requestId);
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
|
|
495
|
+
const stream = approvalStreamRef.current;
|
|
496
|
+
// Send the decision via the datalayer-ai-agents WS (same path as the
|
|
497
|
+
// /agents/tool-approvals UI page).
|
|
498
|
+
const sentDecision = sendToAIAgents({
|
|
499
|
+
type: 'tool_approval_decision',
|
|
500
|
+
approvalId: requestId,
|
|
501
|
+
approved: false,
|
|
502
|
+
...(note ? { note } : {}),
|
|
503
|
+
});
|
|
504
|
+
if (!sentDecision && !stream) {
|
|
505
|
+
throw new Error('AI Agents WebSocket is not connected and no local approval stream is active');
|
|
396
506
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
result: {
|
|
402
|
-
approved: false,
|
|
403
|
-
approvalId: requestId,
|
|
404
|
-
message: note || 'Rejected from trigger panel',
|
|
405
|
-
},
|
|
406
|
-
});
|
|
407
|
-
approvalStreamRef.current.unsubscribe();
|
|
408
|
-
approvalStreamRef.current.adapter.disconnect();
|
|
507
|
+
// Send the Vercel AI continuation so the agent can record the rejection.
|
|
508
|
+
// Clear approvalStreamRef FIRST to prevent a duplicate call from the
|
|
509
|
+
// incoming tool_approval_rejected WS event.
|
|
510
|
+
if (stream) {
|
|
409
511
|
approvalStreamRef.current = null;
|
|
512
|
+
const toolCallId = approval?.tool_call_id ??
|
|
513
|
+
stream.adapter.getDeferredToolCallId(approval?.tool_name ?? '');
|
|
514
|
+
if (toolCallId) {
|
|
515
|
+
await stream.adapter.sendToolResult(toolCallId, {
|
|
516
|
+
toolCallId,
|
|
517
|
+
success: true,
|
|
518
|
+
result: {
|
|
519
|
+
approved: false,
|
|
520
|
+
approvalId: requestId,
|
|
521
|
+
message: note || 'Rejected from trigger panel',
|
|
522
|
+
},
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
stream.unsubscribe();
|
|
526
|
+
stream.adapter.disconnect();
|
|
410
527
|
}
|
|
411
528
|
setApprovals(prev => prev.filter(a => a.id !== requestId));
|
|
412
|
-
|
|
529
|
+
return true;
|
|
413
530
|
}
|
|
414
531
|
catch (error) {
|
|
415
532
|
setApprovalError(error instanceof Error ? error.message : 'Failed to reject');
|
|
533
|
+
return false;
|
|
416
534
|
}
|
|
417
535
|
finally {
|
|
418
536
|
setApprovalLoading(null);
|
|
419
537
|
}
|
|
420
|
-
}, [
|
|
538
|
+
}, [approvals, sendToAIAgents]);
|
|
421
539
|
// ── Launch once trigger with approval ────────────────────────────────────
|
|
422
540
|
const handleLaunchOnceApproval = useCallback(async () => {
|
|
423
|
-
if (!agentBaseUrl
|
|
541
|
+
if (!agentBaseUrl)
|
|
542
|
+
return;
|
|
543
|
+
const ready = await ensureRuntimeReady();
|
|
544
|
+
if (!ready)
|
|
545
|
+
return;
|
|
546
|
+
let approvalReady = approvalAgentReady;
|
|
547
|
+
if (!approvalReady) {
|
|
548
|
+
approvalReady = await createApprovalAgent();
|
|
549
|
+
}
|
|
550
|
+
if (!approvalReady)
|
|
424
551
|
return;
|
|
425
552
|
setIsLaunchingApproval(true);
|
|
553
|
+
setHasTriggeredApproval(true);
|
|
426
554
|
setApprovalFlash(null);
|
|
427
555
|
setApprovalSidebarMessages([]);
|
|
428
556
|
setSidebarMessagesError(null);
|
|
429
|
-
setHasTriggeredApproval(true);
|
|
430
557
|
const runId = `once-approval-${Date.now()}`;
|
|
431
558
|
const startTime = new Date().toISOString();
|
|
432
559
|
setTriggerHistory(prev => [
|
|
@@ -439,13 +566,8 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
439
566
|
...prev,
|
|
440
567
|
]);
|
|
441
568
|
try {
|
|
442
|
-
setApprovalFlash('Once trigger
|
|
443
|
-
|
|
444
|
-
if (!streamResult.pendingApproval && approvalStreamRef.current) {
|
|
445
|
-
approvalStreamRef.current.unsubscribe();
|
|
446
|
-
approvalStreamRef.current.adapter.disconnect();
|
|
447
|
-
approvalStreamRef.current = null;
|
|
448
|
-
}
|
|
569
|
+
setApprovalFlash('Once trigger launched.');
|
|
570
|
+
await streamRunMessages(approvalAgentId, ONCE_TRIGGER_APPROVAL_PROMPT, setApprovalSidebarMessages, setSidebarMessagesError, { keepAliveForApproval: true });
|
|
449
571
|
setTriggerHistory(prev => prev.map(r => r.id === runId
|
|
450
572
|
? {
|
|
451
573
|
...r,
|
|
@@ -464,8 +586,10 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
464
586
|
}, [
|
|
465
587
|
agentBaseUrl,
|
|
466
588
|
approvalAgentReady,
|
|
589
|
+
createApprovalAgent,
|
|
467
590
|
approvalAgentId,
|
|
468
591
|
streamRunMessages,
|
|
592
|
+
ensureRuntimeReady,
|
|
469
593
|
]);
|
|
470
594
|
// ── Pending approvals for banner/dialog ──────────────────────────────────
|
|
471
595
|
const pendingApprovals = approvals.map(req => ({
|
|
@@ -480,6 +604,9 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
480
604
|
const handleUpdateCron = useCallback(async () => {
|
|
481
605
|
if (!agentBaseUrl || !editCron.trim())
|
|
482
606
|
return;
|
|
607
|
+
const ready = await ensureRuntimeReady();
|
|
608
|
+
if (!ready)
|
|
609
|
+
return;
|
|
483
610
|
setIsUpdating(true);
|
|
484
611
|
try {
|
|
485
612
|
const res = await authFetch(`${agentBaseUrl}/api/v1/agents/${agentId}/trigger`, {
|
|
@@ -499,11 +626,14 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
499
626
|
finally {
|
|
500
627
|
setIsUpdating(false);
|
|
501
628
|
}
|
|
502
|
-
}, [agentBaseUrl, agentId, editCron, authFetch]);
|
|
629
|
+
}, [agentBaseUrl, agentId, editCron, authFetch, ensureRuntimeReady]);
|
|
503
630
|
// ── Manual trigger ───────────────────────────────────────────────────────
|
|
504
631
|
const handleTriggerNow = useCallback(async () => {
|
|
505
632
|
if (!agentBaseUrl)
|
|
506
633
|
return;
|
|
634
|
+
const ready = await ensureRuntimeReady();
|
|
635
|
+
if (!ready)
|
|
636
|
+
return;
|
|
507
637
|
setIsTriggeringNow(true);
|
|
508
638
|
setTriggerFlash(null);
|
|
509
639
|
const runId = `manual-${Date.now()}`;
|
|
@@ -518,7 +648,13 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
518
648
|
...prev,
|
|
519
649
|
]);
|
|
520
650
|
try {
|
|
521
|
-
const res = await authFetch(`${agentBaseUrl}/api/v1/agents/${agentId}/trigger/run`, {
|
|
651
|
+
const res = await authFetch(`${agentBaseUrl}/api/v1/agents/${agentId}/trigger/run`, {
|
|
652
|
+
method: 'POST',
|
|
653
|
+
body: JSON.stringify({
|
|
654
|
+
source: 'manual',
|
|
655
|
+
identities: identitiesForRuns,
|
|
656
|
+
}),
|
|
657
|
+
});
|
|
522
658
|
if (res.ok) {
|
|
523
659
|
setTriggerFlash('Trigger fired successfully');
|
|
524
660
|
setTriggerHistory(prev => prev.map(r => r.id === runId
|
|
@@ -541,11 +677,14 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
541
677
|
finally {
|
|
542
678
|
setIsTriggeringNow(false);
|
|
543
679
|
}
|
|
544
|
-
}, [agentBaseUrl, agentId, authFetch]);
|
|
680
|
+
}, [agentBaseUrl, agentId, authFetch, identitiesForRuns, ensureRuntimeReady]);
|
|
545
681
|
// ── Webhook management ─────────────────────────────────────────────────
|
|
546
682
|
const handleGenerateWebhook = useCallback(async () => {
|
|
547
683
|
if (!agentBaseUrl)
|
|
548
684
|
return;
|
|
685
|
+
const ready = await ensureRuntimeReady();
|
|
686
|
+
if (!ready)
|
|
687
|
+
return;
|
|
549
688
|
setIsUpdating(true);
|
|
550
689
|
try {
|
|
551
690
|
const res = await authFetch(`${agentBaseUrl}/api/v1/agents/${agentId}/trigger/webhook`, { method: 'POST' });
|
|
@@ -562,10 +701,13 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
562
701
|
finally {
|
|
563
702
|
setIsUpdating(false);
|
|
564
703
|
}
|
|
565
|
-
}, [agentBaseUrl, agentId, authFetch]);
|
|
704
|
+
}, [agentBaseUrl, agentId, authFetch, ensureRuntimeReady]);
|
|
566
705
|
const handleToggleWebhook = useCallback(async () => {
|
|
567
706
|
if (!agentBaseUrl)
|
|
568
707
|
return;
|
|
708
|
+
const ready = await ensureRuntimeReady();
|
|
709
|
+
if (!ready)
|
|
710
|
+
return;
|
|
569
711
|
try {
|
|
570
712
|
await authFetch(`${agentBaseUrl}/api/v1/agents/${agentId}/trigger/webhook`, {
|
|
571
713
|
method: 'PATCH',
|
|
@@ -576,11 +718,14 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
576
718
|
catch {
|
|
577
719
|
/* ok */
|
|
578
720
|
}
|
|
579
|
-
}, [agentBaseUrl, agentId, webhookEnabled, authFetch]);
|
|
721
|
+
}, [agentBaseUrl, agentId, webhookEnabled, authFetch, ensureRuntimeReady]);
|
|
580
722
|
// ── Event subscription ─────────────────────────────────────────────────
|
|
581
723
|
const handleSubscribeEvent = useCallback(async () => {
|
|
582
724
|
if (!agentBaseUrl || !eventTopic.trim())
|
|
583
725
|
return;
|
|
726
|
+
const ready = await ensureRuntimeReady();
|
|
727
|
+
if (!ready)
|
|
728
|
+
return;
|
|
584
729
|
setIsUpdating(true);
|
|
585
730
|
try {
|
|
586
731
|
const res = await authFetch(`${agentBaseUrl}/api/v1/agents/${agentId}/trigger/event`, {
|
|
@@ -600,11 +745,21 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
600
745
|
finally {
|
|
601
746
|
setIsUpdating(false);
|
|
602
747
|
}
|
|
603
|
-
}, [
|
|
748
|
+
}, [
|
|
749
|
+
agentBaseUrl,
|
|
750
|
+
agentId,
|
|
751
|
+
eventTopic,
|
|
752
|
+
eventFilter,
|
|
753
|
+
authFetch,
|
|
754
|
+
ensureRuntimeReady,
|
|
755
|
+
]);
|
|
604
756
|
// ── Launch once trigger ──────────────────────────────────────────────────
|
|
605
757
|
const handleLaunchOnce = useCallback(async () => {
|
|
606
758
|
if (!agentBaseUrl)
|
|
607
759
|
return;
|
|
760
|
+
const ready = await ensureRuntimeReady();
|
|
761
|
+
if (!ready)
|
|
762
|
+
return;
|
|
608
763
|
setIsLaunchingOnce(true);
|
|
609
764
|
setOnceFlash(null);
|
|
610
765
|
setStreamedOnceOutput(null);
|
|
@@ -644,9 +799,9 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
644
799
|
finally {
|
|
645
800
|
setIsLaunchingOnce(false);
|
|
646
801
|
}
|
|
647
|
-
}, [agentBaseUrl, agentId, streamRunMessages]);
|
|
802
|
+
}, [agentBaseUrl, agentId, streamRunMessages, ensureRuntimeReady]);
|
|
648
803
|
// ── Loading / Error ──────────────────────────────────────────────────────
|
|
649
|
-
if (
|
|
804
|
+
if (runtimeStatus === 'launching') {
|
|
650
805
|
return (_jsxs(Box, { sx: {
|
|
651
806
|
display: 'flex',
|
|
652
807
|
flexDirection: 'column',
|
|
@@ -654,405 +809,509 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
654
809
|
justifyContent: 'center',
|
|
655
810
|
height: '100vh',
|
|
656
811
|
gap: 3,
|
|
657
|
-
}, children: [_jsx(Spinner, { size: "large" }), _jsx(Text, { sx: { color: 'fg.muted' }, children:
|
|
658
|
-
? 'Launching runtime for trigger agent…'
|
|
659
|
-
: 'Creating trigger demo agent…' })] }));
|
|
812
|
+
}, children: [_jsx(Spinner, { size: "large" }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Launching runtime for trigger agent..." })] }));
|
|
660
813
|
}
|
|
661
814
|
if (runtimeStatus === 'error' || hookError) {
|
|
662
815
|
return _jsx(ErrorView, { error: hookError, onLogout: onLogout });
|
|
663
816
|
}
|
|
664
817
|
const triggerRunCurl = `curl -N -X POST '${agentBaseUrl}/api/v1/vercel-ai/${agentId}' -H 'Content-Type: application/json' -H 'Accept: text/event-stream'${token ? " -H 'Authorization: Bearer <TOKEN>'" : ''} --data '{"messages":[{"role":"user","parts":[{"type":"text","text":"${ONCE_TRIGGER_PROMPT.replace(/"/g, '\\"')}"}]}],"trigger":"submit-message","sdkVersion":6}'`;
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
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
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
818
|
+
const isAgentLaunching = isLaunchingOnce || isLaunchingApproval;
|
|
819
|
+
return (_jsx("fieldset", { disabled: isAgentLaunching, style: {
|
|
820
|
+
border: 0,
|
|
821
|
+
margin: 0,
|
|
822
|
+
padding: 0,
|
|
823
|
+
minWidth: 0,
|
|
824
|
+
}, children: _jsxs(Box, { sx: {
|
|
825
|
+
height: 'calc(100vh - 60px)',
|
|
826
|
+
display: 'flex',
|
|
827
|
+
flexDirection: 'column',
|
|
828
|
+
}, children: [_jsxs(Box, { sx: {
|
|
829
|
+
display: 'flex',
|
|
830
|
+
alignItems: 'center',
|
|
831
|
+
gap: 2,
|
|
832
|
+
px: 3,
|
|
833
|
+
py: 2,
|
|
834
|
+
borderBottom: '1px solid',
|
|
835
|
+
borderColor: 'border.default',
|
|
836
|
+
flexShrink: 0,
|
|
837
|
+
}, children: [_jsx(ClockIcon, { size: 16 }), _jsxs(Heading, { as: "h3", sx: { fontSize: 2, flex: 1 }, children: ["Triggers \u2014 ", agentId] }), !isReady && (_jsx(Button, { size: "small", variant: "primary", onClick: () => void createAgent(), children: "Start Runtime" })), _jsxs(Label, { variant: aiAgentsConnectionState === 'connected' ? 'success' : 'secondary', children: ["Approvals WS: ", aiAgentsConnectionState] })] }), _jsx(ToolApprovalBanner, { pendingApprovals: pendingApprovals, onReview: approval => {
|
|
838
|
+
const req = approvals.find(a => a.id === approval.id) || null;
|
|
839
|
+
setActiveApproval(req);
|
|
840
|
+
}, onApproveAll: async () => {
|
|
841
|
+
for (const a of approvals) {
|
|
842
|
+
await handleApprove(a.id);
|
|
843
|
+
}
|
|
844
|
+
} }), _jsx(ToolApprovalDialog, { isOpen: !!activeApproval, toolName: activeApproval?.tool_name ?? '', toolDescription: activeApproval?.note, args: activeApproval?.tool_args ?? {}, onApprove: async () => {
|
|
845
|
+
if (activeApproval) {
|
|
846
|
+
const ok = await handleApprove(activeApproval.id);
|
|
847
|
+
if (ok) {
|
|
848
|
+
setActiveApproval(null);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
}, onDeny: async () => {
|
|
852
|
+
if (activeApproval) {
|
|
853
|
+
const ok = await handleReject(activeApproval.id, 'Rejected from dialog');
|
|
854
|
+
if (ok) {
|
|
855
|
+
setActiveApproval(null);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
}, onClose: () => setActiveApproval(null) }), approvalError && (_jsx(Box, { sx: { px: 3, py: 1 }, children: _jsx(Text, { sx: { color: 'danger.fg', fontSize: 0 }, children: approvalError }) })), _jsxs(Box, { sx: { flex: 1, minHeight: 0, display: 'flex' }, children: [_jsx(Box, { sx: { flex: 1, minWidth: 0 }, children: isReady ? (_jsx(Chat, { protocol: "vercel-ai", baseUrl: agentBaseUrl, agentId: agentId, authToken: chatAuthToken, title: "Trigger Agent", brandIcon: _jsx(ZapIcon, { size: 16 }), description: `View-only trigger output. Cron: ${cronExpr} | Webhook: ${webhookEnabled ? 'on' : 'off'} | Event: ${eventSubscribed ? eventTopic : 'none'}`, showHeader: true, autoFocus: false, height: "100%", runtimeId: agentId, showInput: true, disableInputPrompt: true, showModelSelector: false, showToolsMenu: true, showSkillsMenu: true })) : (_jsx(Box, { sx: {
|
|
859
|
+
height: '100%',
|
|
703
860
|
display: 'flex',
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
}, children:
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
borderRadius:
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
new Date(
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
: 'success.muted',
|
|
771
|
-
borderRadius: 2,
|
|
772
|
-
overflow: 'hidden',
|
|
773
|
-
}, children: [_jsxs(Box, { sx: {
|
|
861
|
+
alignItems: 'center',
|
|
862
|
+
justifyContent: 'center',
|
|
863
|
+
p: 4,
|
|
864
|
+
}, children: _jsx(Text, { sx: { color: 'fg.muted' }, children: "Runtime is not started yet. Use Start Runtime or launch a trigger to start it." }) })) }), _jsxs(Box, { sx: {
|
|
865
|
+
width: 380,
|
|
866
|
+
borderLeft: '1px solid',
|
|
867
|
+
borderColor: 'border.default',
|
|
868
|
+
display: 'flex',
|
|
869
|
+
flexDirection: 'column',
|
|
870
|
+
overflow: 'auto',
|
|
871
|
+
}, children: [_jsx(Box, { sx: {
|
|
872
|
+
display: 'flex',
|
|
873
|
+
borderBottom: '1px solid',
|
|
874
|
+
borderColor: 'border.default',
|
|
875
|
+
flexShrink: 0,
|
|
876
|
+
}, children: [
|
|
877
|
+
{ key: 'once', icon: ZapIcon, label: 'Once' },
|
|
878
|
+
{ key: 'cron', icon: ClockIcon, label: 'Cron' },
|
|
879
|
+
{
|
|
880
|
+
key: 'webhook',
|
|
881
|
+
icon: GlobeIcon,
|
|
882
|
+
label: 'Webhook',
|
|
883
|
+
},
|
|
884
|
+
{ key: 'event', icon: ZapIcon, label: 'Event' },
|
|
885
|
+
{
|
|
886
|
+
key: 'manual',
|
|
887
|
+
icon: PlayIcon,
|
|
888
|
+
label: 'Manual',
|
|
889
|
+
},
|
|
890
|
+
].map(t => (_jsx(Button, { size: "small", variant: "invisible", leadingVisual: t.icon, onClick: () => setActiveTab(t.key), sx: {
|
|
891
|
+
flex: 1,
|
|
892
|
+
borderRadius: 0,
|
|
893
|
+
borderBottom: activeTab === t.key
|
|
894
|
+
? '2px solid'
|
|
895
|
+
: '2px solid transparent',
|
|
896
|
+
borderColor: activeTab === t.key ? 'accent.fg' : 'transparent',
|
|
897
|
+
fontWeight: activeTab === t.key ? 'bold' : 'normal',
|
|
898
|
+
}, children: t.label }, t.key))) }), activeTab === 'once' && (_jsxs(Box, { sx: {
|
|
899
|
+
p: 3,
|
|
900
|
+
borderBottom: '1px solid',
|
|
901
|
+
borderColor: 'border.default',
|
|
902
|
+
}, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1, mb: 2 }, children: [_jsx(ZapIcon, { size: 16 }), _jsx(Heading, { as: "h3", sx: { fontSize: 2 }, children: "Once Trigger" })] }), _jsx(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', mb: 3 }, children: "Launch a single streaming run for the once-trigger prompt and watch tool calls, tool results, and assistant text update in real time." }), _jsxs(Box, { sx: {
|
|
903
|
+
bg: 'canvas.subtle',
|
|
904
|
+
border: '1px solid',
|
|
905
|
+
borderColor: 'border.default',
|
|
906
|
+
borderRadius: 2,
|
|
907
|
+
p: 2,
|
|
908
|
+
mb: 2,
|
|
909
|
+
display: 'grid',
|
|
910
|
+
gap: 1,
|
|
911
|
+
}, children: [_jsxs(Text, { sx: { fontSize: 0 }, children: [_jsx("strong", { children: "Agent ID:" }), " ", agentId] }), _jsxs(Text, { sx: { fontSize: 0 }, children: [_jsx("strong", { children: "Base URL:" }), " ", agentBaseUrl] }), lastOnceStartedAt && (_jsxs(Text, { sx: { fontSize: 0 }, children: [_jsx("strong", { children: "Last once launch:" }), ' ', new Date(lastOnceStartedAt).toLocaleString()] }))] }), _jsx(Button, { size: "small", variant: "primary", leadingVisual: ZapIcon, onClick: handleLaunchOnce, disabled: isLaunchingOnce, sx: { width: '100%' }, children: isLaunchingOnce ? 'Launching…' : 'Launch Once' }), _jsx(Button, { size: "small", variant: "danger", leadingVisual: ZapIcon, onClick: handleLaunchOnceApproval, disabled: isLaunchingApproval, sx: { width: '100%', mt: 2 }, children: isLaunchingApproval
|
|
912
|
+
? 'Launching…'
|
|
913
|
+
: 'Launch Once with Approval' }), onceFlash && (_jsx(Flash, { variant: onceFlash.includes('launched') ? 'success' : 'danger', sx: { mt: 2, fontSize: 0 }, children: onceFlash })), approvalFlash && (_jsx(Flash, { variant: approvalFlash.includes('launched') ? 'success' : 'danger', sx: { mt: 2, fontSize: 0 }, children: approvalFlash })), hasTriggeredOnce && (_jsxs(_Fragment, { children: [_jsx(Heading, { as: "h4", sx: { fontSize: 1, mt: 3, mb: 2 }, children: "Generated Output" }), (() => {
|
|
914
|
+
const outputEvent = lastOnceStartedAt
|
|
915
|
+
? [...agentEvents]
|
|
916
|
+
.filter(e => e.kind === 'agent-output' &&
|
|
917
|
+
new Date(e.created_at).getTime() >=
|
|
918
|
+
new Date(lastOnceStartedAt).getTime() - 5000)
|
|
919
|
+
.sort((a, b) => new Date(b.created_at).getTime() -
|
|
920
|
+
new Date(a.created_at).getTime())[0]
|
|
921
|
+
: undefined;
|
|
922
|
+
const hasStreamFallback = !outputEvent &&
|
|
923
|
+
!isLaunchingOnce &&
|
|
924
|
+
streamedOnceOutput !== null;
|
|
925
|
+
if (!outputEvent && !hasStreamFallback) {
|
|
926
|
+
return (_jsxs(Box, { sx: {
|
|
774
927
|
display: 'flex',
|
|
775
928
|
alignItems: 'center',
|
|
776
|
-
gap:
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
color: 'fg.default',
|
|
799
|
-
display: 'block',
|
|
800
|
-
whiteSpace: 'pre-wrap',
|
|
801
|
-
wordBreak: 'break-word',
|
|
802
|
-
fontFamily: 'mono',
|
|
803
|
-
}, children: outputText || '(empty output)' }) })] }));
|
|
804
|
-
})()] })), hasTriggeredOnce && (_jsxs(_Fragment, { children: [_jsxs(Heading, { as: "h4", sx: { fontSize: 1, mt: 3, mb: 2 }, children: ["Streaming Messages", isLaunchingOnce && (_jsx(Spinner, { size: "small", sx: {
|
|
805
|
-
ml: 2,
|
|
806
|
-
verticalAlign: 'middle',
|
|
807
|
-
width: 14,
|
|
808
|
-
height: 14,
|
|
809
|
-
minWidth: 14,
|
|
810
|
-
minHeight: 14,
|
|
811
|
-
} }))] }), sidebarMessagesError ? (_jsx(Flash, { variant: "danger", sx: { fontSize: 0, mb: 2 }, children: sidebarMessagesError })) : sidebarMessages.filter(msg => msg.role !== 'user').length === 0 ? (_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2, mb: 2 }, children: [_jsx(Spinner, { size: "small", sx: { width: 16, height: 16, minWidth: 16, minHeight: 16 } }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: "Waiting for streaming messages\u2026" })] })) : (_jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2, mb: 2 }, children: sidebarMessages
|
|
812
|
-
.slice()
|
|
813
|
-
.filter(msg => msg.role !== 'user')
|
|
814
|
-
.sort((a, b) => {
|
|
815
|
-
const ta = a.createdAt ? new Date(a.createdAt).getTime() : 0;
|
|
816
|
-
const tb = b.createdAt ? new Date(b.createdAt).getTime() : 0;
|
|
817
|
-
return tb - ta;
|
|
818
|
-
})
|
|
819
|
-
.slice(0, 10)
|
|
820
|
-
.map(msg => {
|
|
821
|
-
const content = typeof msg.content === 'string'
|
|
822
|
-
? msg.content
|
|
823
|
-
: JSON.stringify(msg.content);
|
|
929
|
+
gap: 2,
|
|
930
|
+
mb: 2,
|
|
931
|
+
}, children: [_jsx(Spinner, { size: "small" }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: "Waiting for agent output\u2026" })] }));
|
|
932
|
+
}
|
|
933
|
+
const p = outputEvent?.payload;
|
|
934
|
+
const outputText = outputEvent
|
|
935
|
+
? p?.outputs
|
|
936
|
+
? String(p.outputs)
|
|
937
|
+
: ''
|
|
938
|
+
: (streamedOnceOutput ?? '');
|
|
939
|
+
const exitStatus = outputEvent
|
|
940
|
+
? p?.exit_status
|
|
941
|
+
: 'completed';
|
|
942
|
+
const durationMs = outputEvent
|
|
943
|
+
? p?.duration_ms
|
|
944
|
+
: lastOnceStartedAt && streamedOnceEndedAt
|
|
945
|
+
? new Date(streamedOnceEndedAt).getTime() -
|
|
946
|
+
new Date(lastOnceStartedAt).getTime()
|
|
947
|
+
: undefined;
|
|
948
|
+
const endedAt = outputEvent
|
|
949
|
+
? p?.ended_at
|
|
950
|
+
: streamedOnceEndedAt;
|
|
824
951
|
return (_jsxs(Box, { sx: {
|
|
825
|
-
|
|
826
|
-
bg: 'canvas.subtle',
|
|
827
|
-
borderRadius: 2,
|
|
952
|
+
mb: 2,
|
|
828
953
|
border: '1px solid',
|
|
829
|
-
borderColor: '
|
|
954
|
+
borderColor: exitStatus === 'error'
|
|
955
|
+
? 'danger.muted'
|
|
956
|
+
: 'success.muted',
|
|
957
|
+
borderRadius: 2,
|
|
958
|
+
overflow: 'hidden',
|
|
830
959
|
}, children: [_jsxs(Box, { sx: {
|
|
831
960
|
display: 'flex',
|
|
832
961
|
alignItems: 'center',
|
|
833
962
|
gap: 1,
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
963
|
+
px: 2,
|
|
964
|
+
py: 1,
|
|
965
|
+
bg: exitStatus === 'error'
|
|
966
|
+
? 'danger.subtle'
|
|
967
|
+
: 'success.subtle',
|
|
968
|
+
borderBottom: '1px solid',
|
|
969
|
+
borderColor: exitStatus === 'error'
|
|
970
|
+
? 'danger.muted'
|
|
971
|
+
: 'success.muted',
|
|
972
|
+
}, children: [_jsx(Label, { variant: exitStatus === 'error' ? 'danger' : 'success', size: "small", children: exitStatus ?? 'completed' }), durationMs != null && (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: [(Number(durationMs) / 1000).toFixed(1), "s"] })), endedAt && (_jsx(Text, { sx: {
|
|
840
973
|
fontSize: 0,
|
|
841
974
|
color: 'fg.muted',
|
|
842
|
-
|
|
975
|
+
ml: 'auto',
|
|
843
976
|
whiteSpace: 'nowrap',
|
|
844
|
-
}, children: new Date(
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
977
|
+
}, children: new Date(endedAt).toLocaleString() })), _jsx(Button, { size: "small", variant: "invisible", sx: { p: 1 }, onClick: () => navigator.clipboard.writeText(outputText), children: _jsx(CopyIcon, { size: 12 }) })] }), _jsx(Box, { sx: {
|
|
978
|
+
p: 2,
|
|
979
|
+
bg: 'canvas.default',
|
|
980
|
+
maxHeight: 300,
|
|
981
|
+
overflow: 'auto',
|
|
982
|
+
}, children: _jsx(Text, { sx: {
|
|
983
|
+
fontSize: 0,
|
|
984
|
+
color: 'fg.default',
|
|
985
|
+
display: 'block',
|
|
986
|
+
whiteSpace: 'pre-wrap',
|
|
987
|
+
wordBreak: 'break-word',
|
|
988
|
+
fontFamily: 'mono',
|
|
989
|
+
}, children: outputText || '(empty output)' }) })] }));
|
|
990
|
+
})()] })), hasTriggeredOnce && (_jsxs(_Fragment, { children: [_jsxs(Heading, { as: "h4", sx: { fontSize: 1, mt: 3, mb: 2 }, children: ["Streaming Messages", isLaunchingOnce && (_jsx(Spinner, { size: "small", sx: {
|
|
991
|
+
ml: 2,
|
|
992
|
+
verticalAlign: 'middle',
|
|
993
|
+
width: 14,
|
|
994
|
+
height: 14,
|
|
995
|
+
minWidth: 14,
|
|
996
|
+
minHeight: 14,
|
|
997
|
+
} }))] }), sidebarMessagesError ? (_jsx(Flash, { variant: "danger", sx: { fontSize: 0, mb: 2 }, children: sidebarMessagesError })) : sidebarMessages.filter(msg => msg.role !== 'user')
|
|
998
|
+
.length === 0 ? (_jsxs(Box, { sx: {
|
|
999
|
+
display: 'flex',
|
|
1000
|
+
alignItems: 'center',
|
|
1001
|
+
gap: 2,
|
|
1002
|
+
mb: 2,
|
|
1003
|
+
}, children: [_jsx(Spinner, { size: "small", sx: {
|
|
1004
|
+
width: 16,
|
|
1005
|
+
height: 16,
|
|
1006
|
+
minWidth: 16,
|
|
1007
|
+
minHeight: 16,
|
|
1008
|
+
} }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: "Waiting for streaming messages\u2026" })] })) : (_jsx(Box, { sx: {
|
|
1009
|
+
display: 'flex',
|
|
1010
|
+
flexDirection: 'column',
|
|
1011
|
+
gap: 2,
|
|
1012
|
+
mb: 2,
|
|
1013
|
+
}, children: sidebarMessages
|
|
1014
|
+
.slice()
|
|
1015
|
+
.filter(msg => msg.role !== 'user')
|
|
1016
|
+
.sort((a, b) => {
|
|
1017
|
+
const ta = a.createdAt
|
|
1018
|
+
? new Date(a.createdAt).getTime()
|
|
1019
|
+
: 0;
|
|
1020
|
+
const tb = b.createdAt
|
|
1021
|
+
? new Date(b.createdAt).getTime()
|
|
1022
|
+
: 0;
|
|
1023
|
+
return tb - ta;
|
|
1024
|
+
})
|
|
1025
|
+
.slice(0, 10)
|
|
1026
|
+
.map(msg => {
|
|
1027
|
+
const content = typeof msg.content === 'string'
|
|
1028
|
+
? msg.content
|
|
1029
|
+
: JSON.stringify(msg.content);
|
|
880
1030
|
return (_jsxs(Box, { sx: {
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
borderColor: 'success.muted',
|
|
1031
|
+
p: 2,
|
|
1032
|
+
bg: 'canvas.subtle',
|
|
884
1033
|
borderRadius: 2,
|
|
885
|
-
|
|
1034
|
+
border: '1px solid',
|
|
1035
|
+
borderColor: 'border.default',
|
|
886
1036
|
}, children: [_jsxs(Box, { sx: {
|
|
887
1037
|
display: 'flex',
|
|
888
1038
|
alignItems: 'center',
|
|
889
1039
|
gap: 1,
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
1040
|
+
mb: 1,
|
|
1041
|
+
}, children: [_jsx(Label, { size: "small", variant: msg.role === 'assistant'
|
|
1042
|
+
? 'accent'
|
|
1043
|
+
: msg.role === 'tool'
|
|
1044
|
+
? 'success'
|
|
1045
|
+
: 'secondary', children: msg.role }), msg.createdAt && (_jsx(Text, { sx: {
|
|
896
1046
|
fontSize: 0,
|
|
897
1047
|
color: 'fg.muted',
|
|
898
|
-
|
|
1048
|
+
marginLeft: 'auto',
|
|
899
1049
|
whiteSpace: 'nowrap',
|
|
900
|
-
}, children: new Date(
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
.
|
|
915
|
-
.filter(msg => msg.role !== 'user')
|
|
916
|
-
.sort((a, b) => {
|
|
917
|
-
const ta = a.createdAt ? new Date(a.createdAt).getTime() : 0;
|
|
918
|
-
const tb = b.createdAt ? new Date(b.createdAt).getTime() : 0;
|
|
919
|
-
return tb - ta;
|
|
920
|
-
})
|
|
921
|
-
.slice(0, 10)
|
|
922
|
-
.map(msg => {
|
|
923
|
-
const content = typeof msg.content === 'string'
|
|
924
|
-
? msg.content
|
|
925
|
-
: JSON.stringify(msg.content);
|
|
926
|
-
return (_jsxs(Box, { sx: {
|
|
1050
|
+
}, children: new Date(msg.createdAt).toLocaleTimeString() }))] }), _jsx(Text, { sx: {
|
|
1051
|
+
fontSize: 0,
|
|
1052
|
+
color: 'fg.default',
|
|
1053
|
+
display: 'block',
|
|
1054
|
+
whiteSpace: 'pre-wrap',
|
|
1055
|
+
wordBreak: 'break-word',
|
|
1056
|
+
}, children: content.length > 320
|
|
1057
|
+
? `${content.slice(0, 320)}…`
|
|
1058
|
+
: content })] }, `once-msg-${msg.id}`));
|
|
1059
|
+
}) }))] })), hasTriggeredApproval && approvals.length > 0 && (_jsxs(_Fragment, { children: [_jsx(Heading, { as: "h4", sx: { fontSize: 1, mt: 3, mb: 2 }, children: "Pending Tool Approvals" }), _jsx(Box, { sx: {
|
|
1060
|
+
display: 'flex',
|
|
1061
|
+
flexDirection: 'column',
|
|
1062
|
+
gap: 2,
|
|
1063
|
+
mb: 2,
|
|
1064
|
+
}, children: approvals.map(a => (_jsxs(Box, { sx: {
|
|
927
1065
|
p: 2,
|
|
928
|
-
bg: 'canvas.subtle',
|
|
929
|
-
borderRadius: 2,
|
|
930
1066
|
border: '1px solid',
|
|
931
|
-
borderColor: '
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
1067
|
+
borderColor: 'attention.muted',
|
|
1068
|
+
borderRadius: 2,
|
|
1069
|
+
bg: 'attention.subtle',
|
|
1070
|
+
}, children: [_jsxs(Text, { sx: {
|
|
1071
|
+
fontWeight: 600,
|
|
1072
|
+
fontSize: 1,
|
|
1073
|
+
display: 'block',
|
|
936
1074
|
mb: 1,
|
|
937
|
-
}, children: [_jsx(
|
|
938
|
-
? 'accent'
|
|
939
|
-
: msg.role === 'tool'
|
|
940
|
-
? 'success'
|
|
941
|
-
: 'secondary', children: msg.role }), msg.createdAt && (_jsx(Text, { sx: {
|
|
942
|
-
fontSize: 0,
|
|
943
|
-
color: 'fg.muted',
|
|
944
|
-
marginLeft: 'auto',
|
|
945
|
-
whiteSpace: 'nowrap',
|
|
946
|
-
}, children: new Date(msg.createdAt).toLocaleTimeString() }))] }), _jsx(Text, { sx: {
|
|
1075
|
+
}, children: ["\uD83D\uDEE1\uFE0F ", a.tool_name] }), a.tool_args && (_jsx(Text, { sx: {
|
|
947
1076
|
fontSize: 0,
|
|
948
|
-
color: 'fg.
|
|
1077
|
+
color: 'fg.muted',
|
|
949
1078
|
display: 'block',
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
}, children:
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
fontSize:
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1079
|
+
mb: 2,
|
|
1080
|
+
fontFamily: 'mono',
|
|
1081
|
+
}, children: JSON.stringify(a.tool_args) })), _jsxs(Box, { sx: { display: 'flex', gap: 1 }, children: [_jsx(Button, { size: "small", variant: "primary", onClick: () => void handleApprove(a.id), disabled: approvalLoading === a.id, children: approvalLoading === a.id
|
|
1082
|
+
? 'Approving…'
|
|
1083
|
+
: 'Approve' }), _jsx(Button, { size: "small", variant: "danger", onClick: () => void handleReject(a.id, 'Rejected from trigger panel'), disabled: approvalLoading === a.id, children: "Reject" })] })] }, a.id))) })] })), hasTriggeredApproval && (_jsxs(_Fragment, { children: [_jsxs(Heading, { as: "h4", sx: { fontSize: 1, mt: 3, mb: 2 }, children: ["Approval Agent Messages", isLaunchingApproval && (_jsx(Spinner, { size: "small", sx: {
|
|
1084
|
+
ml: 2,
|
|
1085
|
+
verticalAlign: 'middle',
|
|
1086
|
+
width: 14,
|
|
1087
|
+
height: 14,
|
|
1088
|
+
minWidth: 14,
|
|
1089
|
+
minHeight: 14,
|
|
1090
|
+
} }))] }), hasTriggeredApproval && (_jsxs(_Fragment, { children: [_jsx(Heading, { as: "h4", sx: { fontSize: 1, mt: 3, mb: 2 }, children: "Generated Output" }), (() => {
|
|
1091
|
+
const latestAssistantOutput = approvalSidebarMessages
|
|
1092
|
+
.filter(msg => msg.role === 'assistant')
|
|
1093
|
+
.sort((a, b) => {
|
|
1094
|
+
const ta = a.createdAt
|
|
1095
|
+
? new Date(a.createdAt).getTime()
|
|
1096
|
+
: 0;
|
|
1097
|
+
const tb = b.createdAt
|
|
1098
|
+
? new Date(b.createdAt).getTime()
|
|
1099
|
+
: 0;
|
|
1100
|
+
return tb - ta;
|
|
1101
|
+
})[0];
|
|
1102
|
+
if (!latestAssistantOutput) {
|
|
1103
|
+
return (_jsxs(Box, { sx: {
|
|
1104
|
+
display: 'flex',
|
|
1105
|
+
alignItems: 'center',
|
|
1106
|
+
gap: 2,
|
|
1107
|
+
mb: 2,
|
|
1108
|
+
}, children: [_jsx(Spinner, { size: "small", sx: {
|
|
1109
|
+
width: 16,
|
|
1110
|
+
height: 16,
|
|
1111
|
+
minWidth: 16,
|
|
1112
|
+
minHeight: 16,
|
|
1113
|
+
} }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: "Waiting for agent output\u2026" })] }));
|
|
1114
|
+
}
|
|
1115
|
+
const outputText = typeof latestAssistantOutput.content === 'string'
|
|
1116
|
+
? latestAssistantOutput.content
|
|
1117
|
+
: JSON.stringify(latestAssistantOutput.content);
|
|
1118
|
+
return (_jsxs(Box, { sx: {
|
|
1119
|
+
mb: 2,
|
|
1120
|
+
border: '1px solid',
|
|
1121
|
+
borderColor: 'success.muted',
|
|
1122
|
+
borderRadius: 2,
|
|
1123
|
+
overflow: 'hidden',
|
|
1124
|
+
}, children: [_jsxs(Box, { sx: {
|
|
1125
|
+
display: 'flex',
|
|
1126
|
+
alignItems: 'center',
|
|
1127
|
+
gap: 1,
|
|
1128
|
+
px: 2,
|
|
1129
|
+
py: 1,
|
|
1130
|
+
bg: 'success.subtle',
|
|
1131
|
+
borderBottom: '1px solid',
|
|
1132
|
+
borderColor: 'success.muted',
|
|
1133
|
+
}, children: [_jsx(Label, { variant: "success", size: "small", children: "completed" }), latestAssistantOutput.createdAt && (_jsx(Text, { sx: {
|
|
1134
|
+
fontSize: 0,
|
|
1135
|
+
color: 'fg.muted',
|
|
1136
|
+
ml: 'auto',
|
|
1137
|
+
whiteSpace: 'nowrap',
|
|
1138
|
+
}, children: new Date(latestAssistantOutput.createdAt).toLocaleString() })), _jsx(Button, { size: "small", variant: "invisible", sx: { p: 1 }, onClick: () => navigator.clipboard.writeText(outputText), children: _jsx(CopyIcon, { size: 12 }) })] }), _jsx(Box, { sx: {
|
|
1139
|
+
p: 2,
|
|
1140
|
+
bg: 'canvas.default',
|
|
1141
|
+
maxHeight: 300,
|
|
1142
|
+
overflow: 'auto',
|
|
1143
|
+
}, children: _jsx(Text, { sx: {
|
|
1144
|
+
fontSize: 0,
|
|
1145
|
+
color: 'fg.default',
|
|
1146
|
+
display: 'block',
|
|
1147
|
+
whiteSpace: 'pre-wrap',
|
|
1148
|
+
wordBreak: 'break-word',
|
|
1149
|
+
fontFamily: 'mono',
|
|
1150
|
+
}, children: outputText || '(empty output)' }) })] }));
|
|
1151
|
+
})()] })), approvalSidebarMessages.filter(msg => msg.role !== 'user')
|
|
1152
|
+
.length === 0 ? (_jsxs(Box, { sx: {
|
|
1010
1153
|
display: 'flex',
|
|
1011
1154
|
alignItems: 'center',
|
|
1012
|
-
gap:
|
|
1013
|
-
mb:
|
|
1014
|
-
}, children: [_jsx(
|
|
1015
|
-
|
|
1016
|
-
:
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1155
|
+
gap: 2,
|
|
1156
|
+
mb: 2,
|
|
1157
|
+
}, children: [_jsx(Spinner, { size: "small", sx: {
|
|
1158
|
+
width: 16,
|
|
1159
|
+
height: 16,
|
|
1160
|
+
minWidth: 16,
|
|
1161
|
+
minHeight: 16,
|
|
1162
|
+
} }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: "Waiting for approval agent messages\u2026" })] })) : (_jsx(Box, { sx: {
|
|
1163
|
+
display: 'flex',
|
|
1164
|
+
flexDirection: 'column',
|
|
1165
|
+
gap: 2,
|
|
1166
|
+
mb: 2,
|
|
1167
|
+
}, children: approvalSidebarMessages
|
|
1168
|
+
.slice()
|
|
1169
|
+
.filter(msg => msg.role !== 'user')
|
|
1170
|
+
.sort((a, b) => {
|
|
1171
|
+
const ta = a.createdAt
|
|
1172
|
+
? new Date(a.createdAt).getTime()
|
|
1173
|
+
: 0;
|
|
1174
|
+
const tb = b.createdAt
|
|
1175
|
+
? new Date(b.createdAt).getTime()
|
|
1176
|
+
: 0;
|
|
1177
|
+
return tb - ta;
|
|
1178
|
+
})
|
|
1179
|
+
.slice(0, 10)
|
|
1180
|
+
.map(msg => {
|
|
1181
|
+
const content = typeof msg.content === 'string'
|
|
1182
|
+
? msg.content
|
|
1183
|
+
: JSON.stringify(msg.content);
|
|
1184
|
+
return (_jsxs(Box, { sx: {
|
|
1185
|
+
p: 2,
|
|
1186
|
+
bg: 'canvas.subtle',
|
|
1187
|
+
borderRadius: 2,
|
|
1188
|
+
border: '1px solid',
|
|
1189
|
+
borderColor: 'border.default',
|
|
1190
|
+
}, children: [_jsxs(Box, { sx: {
|
|
1191
|
+
display: 'flex',
|
|
1192
|
+
alignItems: 'center',
|
|
1193
|
+
gap: 1,
|
|
1194
|
+
mb: 1,
|
|
1195
|
+
}, children: [_jsx(Label, { size: "small", variant: msg.role === 'assistant'
|
|
1196
|
+
? 'accent'
|
|
1197
|
+
: msg.role === 'tool'
|
|
1198
|
+
? 'success'
|
|
1199
|
+
: 'secondary', children: msg.role }), msg.createdAt && (_jsx(Text, { sx: {
|
|
1200
|
+
fontSize: 0,
|
|
1201
|
+
color: 'fg.muted',
|
|
1202
|
+
marginLeft: 'auto',
|
|
1203
|
+
whiteSpace: 'nowrap',
|
|
1204
|
+
}, children: new Date(msg.createdAt).toLocaleTimeString() }))] }), _jsx(Text, { sx: {
|
|
1205
|
+
fontSize: 0,
|
|
1206
|
+
color: 'fg.default',
|
|
1207
|
+
display: 'block',
|
|
1208
|
+
whiteSpace: 'pre-wrap',
|
|
1209
|
+
wordBreak: 'break-word',
|
|
1210
|
+
}, children: content.length > 320
|
|
1211
|
+
? `${content.slice(0, 320)}…`
|
|
1212
|
+
: content })] }, `approval-msg-${msg.id}`));
|
|
1213
|
+
}) }))] })), _jsxs(Box, { sx: { mt: 2, display: 'grid', gap: 2 }, children: [_jsxs(Box, { children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Stream once (local curl)" }), _jsx(Box, { sx: {
|
|
1214
|
+
mt: 1,
|
|
1215
|
+
bg: 'canvas.subtle',
|
|
1216
|
+
borderRadius: 2,
|
|
1217
|
+
p: 2,
|
|
1218
|
+
fontFamily: 'mono',
|
|
1219
|
+
fontSize: 0,
|
|
1220
|
+
wordBreak: 'break-all',
|
|
1221
|
+
}, children: triggerRunCurl })] }), _jsx(Button, { size: "small", leadingVisual: CopyIcon, onClick: () => navigator.clipboard.writeText(triggerRunCurl), children: "Copy streaming command" })] })] })), activeTab === 'cron' && (_jsxs(Box, { sx: {
|
|
1222
|
+
p: 3,
|
|
1223
|
+
borderBottom: '1px solid',
|
|
1224
|
+
borderColor: 'border.default',
|
|
1225
|
+
}, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1, mb: 2 }, children: [_jsx(ClockIcon, { size: 16 }), _jsx(Heading, { as: "h3", sx: { fontSize: 2 }, children: "Cron Schedule" })] }), _jsxs(Label, { variant: "primary", sx: { mb: 2, display: 'inline-block' }, children: ["Current: ", cronExpr] }), nextRun && (_jsxs(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', mb: 2 }, children: ["Next run: ", new Date(nextRun).toLocaleString()] })), _jsxs(Box, { sx: { display: 'flex', gap: 2, mb: 2 }, children: [_jsx(TextInput, { value: editCron, onChange: e => setEditCron(e.target.value), placeholder: "* * * * *", sx: { flex: 1 }, size: "small" }), _jsx(Button, { size: "small", variant: "primary", leadingVisual: SyncIcon, onClick: handleUpdateCron, disabled: isUpdating, children: isUpdating ? 'Saving…' : 'Update' })] }), _jsx(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted' }, children: "Standard cron syntax: minute hour day month weekday" })] })), activeTab === 'webhook' && (_jsxs(Box, { sx: {
|
|
1226
|
+
p: 3,
|
|
1227
|
+
borderBottom: '1px solid',
|
|
1228
|
+
borderColor: 'border.default',
|
|
1229
|
+
}, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1, mb: 2 }, children: [_jsx(GlobeIcon, { size: 16 }), _jsx(Heading, { as: "h3", sx: { fontSize: 2 }, children: "Webhook Trigger" })] }), _jsx(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', mb: 3 }, children: "Generate a unique URL that triggers this agent on incoming HTTP POST requests. Useful for CI/CD pipelines, external services, or custom integrations." }), webhookUrl ? (_jsxs(_Fragment, { children: [_jsx(Label, { variant: webhookEnabled ? 'success' : 'secondary', sx: { mb: 2, display: 'inline-block' }, children: webhookEnabled ? 'Active' : 'Disabled' }), _jsx(Box, { sx: {
|
|
1230
|
+
bg: 'canvas.subtle',
|
|
1231
|
+
p: 2,
|
|
1232
|
+
borderRadius: 2,
|
|
1233
|
+
mb: 2,
|
|
1234
|
+
fontFamily: 'mono',
|
|
1235
|
+
fontSize: 0,
|
|
1236
|
+
wordBreak: 'break-all',
|
|
1237
|
+
}, children: webhookUrl }), webhookSecret && (_jsxs(Box, { sx: { mb: 2 }, children: [_jsx(Text, { sx: { fontSize: 0, fontWeight: 'bold' }, children: "Secret:" }), _jsx(Box, { sx: {
|
|
1238
|
+
bg: 'canvas.subtle',
|
|
1239
|
+
p: 2,
|
|
1240
|
+
borderRadius: 2,
|
|
1241
|
+
mt: 1,
|
|
1242
|
+
fontFamily: 'mono',
|
|
1021
1243
|
fontSize: 0,
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1244
|
+
}, children: webhookSecret })] })), _jsxs(Box, { sx: { display: 'flex', gap: 2 }, children: [_jsx(Button, { size: "small", leadingVisual: CopyIcon, onClick: () => navigator.clipboard.writeText(webhookUrl), children: "Copy URL" }), _jsx(Button, { size: "small", variant: webhookEnabled ? 'danger' : 'primary', onClick: handleToggleWebhook, children: webhookEnabled ? 'Disable' : 'Enable' })] })] })) : (_jsx(Button, { size: "small", variant: "primary", leadingVisual: GlobeIcon, onClick: handleGenerateWebhook, disabled: isUpdating, sx: { width: '100%' }, children: isUpdating ? 'Generating…' : 'Generate Webhook URL' }))] })), activeTab === 'event' && (_jsxs(Box, { sx: {
|
|
1245
|
+
p: 3,
|
|
1246
|
+
borderBottom: '1px solid',
|
|
1247
|
+
borderColor: 'border.default',
|
|
1248
|
+
}, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1, mb: 2 }, children: [_jsx(ZapIcon, { size: 16 }), _jsx(Heading, { as: "h3", sx: { fontSize: 2 }, children: "Event Trigger" })] }), _jsx(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', mb: 3 }, children: "Subscribe to a Kafka topic or internal event stream. The agent triggers on matching messages." }), eventSubscribed ? (_jsxs(_Fragment, { children: [_jsxs(Label, { variant: "success", sx: { mb: 2, display: 'inline-block' }, children: ["Subscribed to: ", eventTopic] }), eventFilter && (_jsxs(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', mb: 2 }, children: ["Filter: ", eventFilter] })), _jsx(Button, { size: "small", variant: "danger", onClick: () => setEventSubscribed(false), sx: { width: '100%' }, children: "Unsubscribe" })] })) : (_jsxs(_Fragment, { children: [_jsx(TextInput, { value: eventTopic, onChange: e => setEventTopic(e.target.value), placeholder: "e.g. kpi.daily-report", sx: { width: '100%', mb: 2 }, size: "small" }), _jsx(TextInput, { value: eventFilter, onChange: e => setEventFilter(e.target.value), placeholder: "Optional JSONPath filter", sx: { width: '100%', mb: 2 }, size: "small" }), _jsx(Button, { size: "small", variant: "primary", leadingVisual: ZapIcon, onClick: handleSubscribeEvent, disabled: isUpdating || !eventTopic.trim(), sx: { width: '100%' }, children: isUpdating ? 'Subscribing…' : 'Subscribe' })] }))] })), activeTab === 'manual' && (_jsxs(Box, { sx: {
|
|
1249
|
+
p: 3,
|
|
1250
|
+
borderBottom: '1px solid',
|
|
1251
|
+
borderColor: 'border.default',
|
|
1252
|
+
}, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1, mb: 2 }, children: [_jsx(PlayIcon, { size: 16 }), _jsx(Heading, { as: "h3", sx: { fontSize: 2 }, children: "Manual Trigger" })] }), _jsx(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', mb: 3 }, children: "Fire the agent immediately. This is equivalent to the cron job executing but bypasses the schedule." }), _jsx(Button, { size: "small", leadingVisual: PlayIcon, onClick: handleTriggerNow, disabled: isTriggeringNow, sx: { width: '100%' }, children: isTriggeringNow ? 'Triggering…' : 'Trigger Now' }), triggerFlash && (_jsx(Flash, { variant: triggerFlash.includes('success') ? 'success' : 'danger', sx: { mt: 2, fontSize: 0 }, children: triggerFlash }))] })), _jsxs(Box, { sx: { p: 3, flex: 1, overflow: 'auto' }, children: [_jsx(Heading, { as: "h4", sx: { fontSize: 1, mb: 2 }, children: "Trigger History" }), triggerHistory.length === 0 ? (_jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: "No trigger runs recorded yet." })) : (_jsx(Timeline, { children: triggerHistory.slice(0, 20).map(rec => (_jsxs(Timeline.Item, { children: [_jsx(Timeline.Badge, { children: rec.status === 'success' ? (_jsx(CheckCircleIcon, {})) : rec.status === 'failure' ? (_jsx(XCircleIcon, {})) : (_jsx(Spinner, { size: "small" })) }), _jsx(Timeline.Body, { children: _jsxs(Text, { sx: { fontSize: 0 }, children: [new Date(rec.timestamp).toLocaleString(), ' — ', _jsx(Label, { variant: rec.status === 'success'
|
|
1253
|
+
? 'success'
|
|
1254
|
+
: rec.status === 'failure'
|
|
1255
|
+
? 'danger'
|
|
1256
|
+
: 'accent', size: "small", children: rec.status }), rec.duration_ms != null && (_jsxs(Text, { sx: { ml: 1, color: 'fg.muted' }, children: ["(", (rec.duration_ms / 1000).toFixed(1), "s)"] }))] }) })] }, rec.id))) })), _jsx(Heading, { as: "h4", sx: { fontSize: 1, mt: 3, mb: 2 }, children: "Agent Events" }), agentEvents.length === 0 ? (_jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: "No agent events yet." })) : (_jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: [...agentEvents]
|
|
1257
|
+
.sort((a, b) => new Date(b.created_at).getTime() -
|
|
1258
|
+
new Date(a.created_at).getTime())
|
|
1259
|
+
.map((evt) => (_jsxs(Box, { sx: {
|
|
1260
|
+
p: 2,
|
|
1261
|
+
bg: evt.read ? 'canvas.subtle' : 'accent.subtle',
|
|
1262
|
+
borderRadius: 2,
|
|
1263
|
+
border: '1px solid',
|
|
1264
|
+
borderColor: evt.read
|
|
1265
|
+
? 'border.default'
|
|
1266
|
+
: 'accent.muted',
|
|
1267
|
+
}, children: [_jsxs(Box, { sx: {
|
|
1268
|
+
display: 'flex',
|
|
1269
|
+
alignItems: 'center',
|
|
1270
|
+
gap: 1,
|
|
1271
|
+
mb: 1,
|
|
1272
|
+
}, children: [_jsx(Label, { variant: evt.kind === 'agent-started'
|
|
1273
|
+
? 'accent'
|
|
1274
|
+
: evt.kind === 'agent-output'
|
|
1275
|
+
? 'success'
|
|
1276
|
+
: evt.kind?.includes('alert')
|
|
1277
|
+
? 'danger'
|
|
1278
|
+
: 'attention', size: "small", children: evt.kind }), _jsx(Text, { sx: { flex: 1, fontSize: 0, fontWeight: 'bold' }, children: evt.title }), _jsx(Text, { sx: {
|
|
1279
|
+
fontSize: 0,
|
|
1280
|
+
color: 'fg.muted',
|
|
1281
|
+
whiteSpace: 'nowrap',
|
|
1282
|
+
}, children: new Date(evt.created_at).toLocaleString() }), _jsx(Button, { size: "small", variant: "invisible", onClick: () => {
|
|
1283
|
+
const payload = {
|
|
1284
|
+
eventId: String(evt.id),
|
|
1285
|
+
eventAgentId: String(evt.agent_id || agentId || ''),
|
|
1286
|
+
};
|
|
1287
|
+
if (evt.read) {
|
|
1288
|
+
markUnreadMutation.mutate(payload);
|
|
1289
|
+
}
|
|
1290
|
+
else {
|
|
1291
|
+
markReadMutation.mutate(payload);
|
|
1292
|
+
}
|
|
1293
|
+
}, sx: { p: 1 }, children: evt.read ? (_jsx(EyeClosedIcon, { size: 12 })) : (_jsx(EyeIcon, { size: 12 })) }), _jsx(Button, { size: "small", variant: "invisible", onClick: () => deleteEventMutation.mutate(evt.id), sx: { p: 1, color: 'danger.fg' }, children: _jsx(TrashIcon, { size: 12 }) })] }), evt.payload &&
|
|
1294
|
+
(() => {
|
|
1295
|
+
const p = evt.payload;
|
|
1296
|
+
return (_jsxs(Box, { sx: { fontSize: 0, color: 'fg.muted' }, children: [evt.kind === 'agent-output' && p.outputs && (_jsx(Tooltip, { text: String(p.outputs), direction: "n", children: _jsx("button", { type: "button", "aria-label": String(p.outputs), style: {
|
|
1297
|
+
all: 'unset',
|
|
1298
|
+
display: 'block',
|
|
1299
|
+
width: '100%',
|
|
1300
|
+
cursor: 'default',
|
|
1301
|
+
}, children: _jsxs(Box, { sx: {
|
|
1302
|
+
mb: 1,
|
|
1303
|
+
display: 'flex',
|
|
1304
|
+
alignItems: 'baseline',
|
|
1305
|
+
gap: 1,
|
|
1306
|
+
minWidth: 0,
|
|
1307
|
+
}, children: [_jsx(Text, { as: "span", sx: {
|
|
1308
|
+
fontWeight: 'bold',
|
|
1309
|
+
color: 'fg.default',
|
|
1310
|
+
whiteSpace: 'nowrap',
|
|
1311
|
+
}, children: "Output:" }), _jsx(Truncate, { title: String(p.outputs), maxWidth: "100%", sx: { minWidth: 0, flex: 1 }, children: String(p.outputs) })] }) }) })), evt.kind === 'agent-output' &&
|
|
1312
|
+
p.duration_ms != null && (_jsxs(Text, { as: "p", children: ["Duration:", ' ', (Number(p.duration_ms) / 1000).toFixed(1), "s"] })), evt.kind?.includes('guardrail') &&
|
|
1313
|
+
p.message && (_jsx(Text, { as: "p", children: String(p.message) }))] }));
|
|
1314
|
+
})()] }, evt.id))) }))] })] })] })] }) }));
|
|
1056
1315
|
};
|
|
1057
1316
|
// ─── Sync token to core IAM store ──────────────────────────────────────────
|
|
1058
1317
|
const syncTokenToIamStore = (token) => {
|
|
@@ -1062,7 +1321,7 @@ const syncTokenToIamStore = (token) => {
|
|
|
1062
1321
|
};
|
|
1063
1322
|
// ─── Main component with auth gate ─────────────────────────────────────────
|
|
1064
1323
|
const AgentTriggersExample = () => {
|
|
1065
|
-
const { token,
|
|
1324
|
+
const { token, clearAuth } = useSimpleAuthStore();
|
|
1066
1325
|
const hasSynced = useRef(false);
|
|
1067
1326
|
useEffect(() => {
|
|
1068
1327
|
if (token && !hasSynced.current) {
|
|
@@ -1070,11 +1329,6 @@ const AgentTriggersExample = () => {
|
|
|
1070
1329
|
syncTokenToIamStore(token);
|
|
1071
1330
|
}
|
|
1072
1331
|
}, [token]);
|
|
1073
|
-
const handleSignIn = useCallback((newToken, handle) => {
|
|
1074
|
-
setAuth(newToken, handle);
|
|
1075
|
-
hasSynced.current = true;
|
|
1076
|
-
syncTokenToIamStore(newToken);
|
|
1077
|
-
}, [setAuth]);
|
|
1078
1332
|
const handleLogout = useCallback(() => {
|
|
1079
1333
|
clearAuth();
|
|
1080
1334
|
hasSynced.current = false;
|
|
@@ -1083,7 +1337,7 @@ const AgentTriggersExample = () => {
|
|
|
1083
1337
|
});
|
|
1084
1338
|
}, [clearAuth]);
|
|
1085
1339
|
if (!token) {
|
|
1086
|
-
return (_jsx(ThemedProvider, { children: _jsx(
|
|
1340
|
+
return (_jsx(ThemedProvider, { children: _jsx(AuthRequiredView, {}) }));
|
|
1087
1341
|
}
|
|
1088
1342
|
return (_jsx(QueryClientProvider, { client: queryClient, children: _jsx(ThemedProvider, { children: _jsx(AgentTriggerInner, { onLogout: handleLogout }) }) }));
|
|
1089
1343
|
};
|