@datalayer/agent-runtimes 1.0.3 → 1.0.4
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 +13 -131
- package/lib/chat/Chat.d.ts +3 -1
- package/lib/chat/Chat.js +2 -2
- package/lib/chat/base/ChatBase.js +52 -1
- package/lib/chat/messages/ChatMessageList.js +17 -4
- package/lib/client/AgentsMixin.d.ts +48 -1
- package/lib/client/AgentsMixin.js +109 -0
- package/lib/components/NotificationEventCard.js +51 -26
- package/lib/components/OutputCard.js +21 -7
- package/lib/components/ToolApprovalCard.js +20 -2
- package/lib/examples/AgentCheckpointsExample.js +2 -8
- package/lib/examples/AgentCodemodeExample.js +3 -9
- package/lib/examples/AgentEvalsExample.js +3 -9
- package/lib/examples/AgentGuardrailsExample.js +3 -9
- package/lib/examples/AgentMemoryExample.js +3 -9
- package/lib/examples/AgentMonitoringExample.js +3 -9
- package/lib/examples/AgentNotificationsExample.js +2 -8
- package/lib/examples/AgentOutputsExample.js +3 -9
- package/lib/examples/AgentSandboxExample.js +3 -9
- package/lib/examples/AgentSkillsExample.js +3 -9
- package/lib/examples/AgentToolApprovalsExample.js +89 -24
- package/lib/examples/AgentTriggersExample.js +604 -37
- package/lib/examples/ChatExample.js +2 -10
- package/lib/examples/components/ErrorView.d.ts +14 -0
- package/lib/examples/components/ErrorView.js +20 -0
- package/lib/examples/components/index.d.ts +2 -0
- package/lib/examples/components/index.js +1 -0
- package/lib/examples/main.d.ts +1 -0
- package/lib/examples/main.js +1 -0
- package/lib/protocols/VercelAIAdapter.d.ts +2 -0
- package/lib/protocols/VercelAIAdapter.js +86 -20
- package/lib/shims/json5.d.ts +4 -0
- package/lib/shims/json5.js +8 -0
- package/lib/specs/agents/agents.js +241 -1390
- package/lib/specs/agents/index.js +1 -3
- package/lib/specs/envvars.js +20 -27
- package/lib/specs/evals.js +6 -6
- package/lib/specs/events.d.ts +10 -2
- package/lib/specs/events.js +84 -126
- package/lib/specs/frontendTools.js +2 -2
- package/lib/specs/guardrails.d.ts +7 -0
- package/lib/specs/guardrails.js +159 -240
- package/lib/specs/mcpServers.js +6 -35
- package/lib/specs/memory.d.ts +2 -0
- package/lib/specs/memory.js +17 -4
- package/lib/specs/models.js +5 -25
- package/lib/specs/notifications.js +18 -102
- package/lib/specs/outputs.js +9 -15
- package/lib/specs/skills.js +18 -18
- package/lib/specs/teams/index.js +1 -3
- package/lib/specs/teams/teams.js +348 -468
- package/lib/specs/tools.js +6 -3
- package/lib/specs/triggers.js +11 -61
- package/lib/types/tools.d.ts +2 -0
- package/package.json +1 -1
- package/scripts/codegen/__pycache__/versioning.cpython-313.pyc +0 -0
- package/scripts/codegen/generate_agents.py +4 -1
- package/scripts/codegen/generate_events.py +12 -4
- package/scripts/codegen/generate_tools.py +20 -0
- package/style/primer-primitives.css +1 -6
- package/scripts/codegen/__pycache__/generate_agents.cpython-313.pyc +0 -0
- 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
|
@@ -18,18 +18,26 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
18
18
|
import { 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 {
|
|
21
|
+
import { ClockIcon, SyncIcon, CheckCircleIcon, XCircleIcon, PlayIcon, SignOutIcon, GlobeIcon, ZapIcon, EyeIcon, EyeClosedIcon, TrashIcon, CopyIcon, } from '@primer/octicons-react';
|
|
22
22
|
import { Box } from '@datalayer/primer-addons';
|
|
23
|
+
import { ErrorView } from './components';
|
|
23
24
|
import { ThemedProvider } from './utils/themedProvider';
|
|
24
25
|
import { useSimpleAuthStore } from '@datalayer/core/lib/views/otel';
|
|
25
26
|
import { SignInSimple } from '@datalayer/core/lib/views/iam';
|
|
26
27
|
import { UserBadge } from '@datalayer/core/lib/views/profile';
|
|
27
28
|
import { Chat } from '../chat';
|
|
29
|
+
import { ToolApprovalBanner, ToolApprovalDialog, } from '../chat/tools';
|
|
28
30
|
import { useAgentEvents, useDeleteAgentEvent, useMarkEventRead, useMarkEventUnread, useAIAgentsWebSocket, } from '../hooks';
|
|
31
|
+
import { VercelAIAdapter } from '../protocols';
|
|
32
|
+
import { createUserMessage } from '../types/messages';
|
|
29
33
|
const queryClient = new QueryClient();
|
|
30
34
|
// ─── Constants ─────────────────────────────────────────────────────────────
|
|
31
35
|
const AGENT_NAME = 'trigger-demo-agent';
|
|
32
36
|
const AGENT_SPEC_ID = 'demo-one-trigger';
|
|
37
|
+
const APPROVAL_AGENT_NAME = 'trigger-approval-demo-agent';
|
|
38
|
+
const APPROVAL_AGENT_SPEC_ID = 'demo-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.";
|
|
40
|
+
const ONCE_TRIGGER_APPROVAL_PROMPT = "Call runtime_sensitive_echo exactly once with message='Tool approval demo executed' and reason='audit'. Do not call any other tool.";
|
|
33
41
|
const DEFAULT_LOCAL_BASE_URL = import.meta.env.VITE_BASE_URL || 'http://localhost:8765';
|
|
34
42
|
const DEFAULT_CRON = '0 8 * * *'; // daily at 08:00 UTC
|
|
35
43
|
// ─── Inner component (rendered after auth) ─────────────────────────────────
|
|
@@ -54,14 +62,34 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
54
62
|
// Once trigger state
|
|
55
63
|
const [isLaunchingOnce, setIsLaunchingOnce] = useState(false);
|
|
56
64
|
const [onceFlash, setOnceFlash] = useState(null);
|
|
65
|
+
const [lastOnceStartedAt, setLastOnceStartedAt] = useState(null);
|
|
66
|
+
const [hasTriggeredOnce, setHasTriggeredOnce] = useState(false);
|
|
67
|
+
const [streamedOnceOutput, setStreamedOnceOutput] = useState(null);
|
|
68
|
+
const [streamedOnceEndedAt, setStreamedOnceEndedAt] = useState(null);
|
|
57
69
|
// Webhook state
|
|
58
70
|
const [webhookUrl, setWebhookUrl] = useState(null);
|
|
59
71
|
const [webhookSecret, setWebhookSecret] = useState(null);
|
|
60
72
|
const [webhookEnabled, setWebhookEnabled] = useState(false);
|
|
73
|
+
// Sidebar messages state
|
|
74
|
+
const [sidebarMessages, setSidebarMessages] = useState([]);
|
|
75
|
+
const [sidebarMessagesError, setSidebarMessagesError] = useState(null);
|
|
61
76
|
// Event state
|
|
62
77
|
const [eventTopic, setEventTopic] = useState('');
|
|
63
78
|
const [eventFilter, setEventFilter] = useState('');
|
|
64
79
|
const [eventSubscribed, setEventSubscribed] = useState(false);
|
|
80
|
+
// Approval agent state
|
|
81
|
+
const [approvalAgentId, setApprovalAgentId] = useState(APPROVAL_AGENT_NAME);
|
|
82
|
+
const [approvalAgentReady, setApprovalAgentReady] = useState(false);
|
|
83
|
+
const [isLaunchingApproval, setIsLaunchingApproval] = useState(false);
|
|
84
|
+
const [approvalFlash, setApprovalFlash] = useState(null);
|
|
85
|
+
const [hasTriggeredApproval, setHasTriggeredApproval] = useState(false);
|
|
86
|
+
const [approvals, setApprovals] = useState([]);
|
|
87
|
+
const [approvalLoading, setApprovalLoading] = useState(null);
|
|
88
|
+
const [approvalError, setApprovalError] = useState(null);
|
|
89
|
+
const [activeApproval, setActiveApproval] = useState(null);
|
|
90
|
+
// Approval sidebar messages
|
|
91
|
+
const [approvalSidebarMessages, setApprovalSidebarMessages] = useState([]);
|
|
92
|
+
const approvalStreamRef = useRef(null);
|
|
65
93
|
// Events hooks
|
|
66
94
|
const eventsQuery = useAgentEvents(agentId);
|
|
67
95
|
const deleteEventMutation = useDeleteAgentEvent(agentId);
|
|
@@ -95,7 +123,7 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
95
123
|
name: AGENT_NAME,
|
|
96
124
|
description: 'Agent with cron, webhook, event, and manual triggers',
|
|
97
125
|
agent_library: 'pydantic-ai',
|
|
98
|
-
transport: '
|
|
126
|
+
transport: 'vercel-ai',
|
|
99
127
|
agent_spec_id: AGENT_SPEC_ID,
|
|
100
128
|
tools: [],
|
|
101
129
|
}),
|
|
@@ -148,6 +176,306 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
148
176
|
// /trigger/history endpoints on the platform API.
|
|
149
177
|
// Currently these endpoints don't exist on either the local
|
|
150
178
|
// agent-runtimes server or the ai-agents service.
|
|
179
|
+
const upsertSidebarMessage = useCallback((setMessages, message) => {
|
|
180
|
+
setMessages(prev => {
|
|
181
|
+
const idx = prev.findIndex(m => m.id === message.id);
|
|
182
|
+
if (idx >= 0) {
|
|
183
|
+
const next = [...prev];
|
|
184
|
+
next[idx] = { ...next[idx], ...message };
|
|
185
|
+
return next;
|
|
186
|
+
}
|
|
187
|
+
return [...prev, message];
|
|
188
|
+
});
|
|
189
|
+
}, []);
|
|
190
|
+
const streamRunMessages = useCallback(async (targetAgentId, prompt, setMessages, setError, options) => {
|
|
191
|
+
const endpoint = `${agentBaseUrl}/api/v1/vercel-ai/${encodeURIComponent(targetAgentId)}`;
|
|
192
|
+
if (options?.keepAliveForApproval && approvalStreamRef.current) {
|
|
193
|
+
approvalStreamRef.current.unsubscribe();
|
|
194
|
+
approvalStreamRef.current.adapter.disconnect();
|
|
195
|
+
approvalStreamRef.current = null;
|
|
196
|
+
}
|
|
197
|
+
const adapter = new VercelAIAdapter({
|
|
198
|
+
protocol: 'vercel-ai',
|
|
199
|
+
baseUrl: endpoint,
|
|
200
|
+
agentId: targetAgentId,
|
|
201
|
+
headers: token ? { Authorization: `Bearer ${token}` } : undefined,
|
|
202
|
+
});
|
|
203
|
+
let latestAssistantText = null;
|
|
204
|
+
let pendingApproval = false;
|
|
205
|
+
const unsubscribe = adapter.subscribe(event => {
|
|
206
|
+
if (event.type === 'message' && event.message) {
|
|
207
|
+
const msg = event.message;
|
|
208
|
+
const content = typeof msg.content === 'string'
|
|
209
|
+
? msg.content
|
|
210
|
+
: JSON.stringify(msg.content);
|
|
211
|
+
if (msg.role === 'assistant') {
|
|
212
|
+
latestAssistantText = content;
|
|
213
|
+
}
|
|
214
|
+
upsertSidebarMessage(setMessages, {
|
|
215
|
+
id: msg.id,
|
|
216
|
+
role: msg.role,
|
|
217
|
+
content,
|
|
218
|
+
createdAt: msg.createdAt?.toISOString(),
|
|
219
|
+
});
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (event.type === 'tool-call' && event.toolCall) {
|
|
223
|
+
upsertSidebarMessage(setMessages, {
|
|
224
|
+
id: `tool-call-${event.toolCall.toolCallId}`,
|
|
225
|
+
role: 'tool',
|
|
226
|
+
content: `${event.toolCall.toolName}(${JSON.stringify(event.toolCall.args)})`,
|
|
227
|
+
createdAt: event.timestamp.toISOString(),
|
|
228
|
+
});
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
if (event.type === 'tool-result' && event.toolResult) {
|
|
232
|
+
const resultObj = event.toolResult.result &&
|
|
233
|
+
typeof event.toolResult.result === 'object'
|
|
234
|
+
? event.toolResult.result
|
|
235
|
+
: undefined;
|
|
236
|
+
if (resultObj?.pending_approval === true) {
|
|
237
|
+
pendingApproval = true;
|
|
238
|
+
}
|
|
239
|
+
upsertSidebarMessage(setMessages, {
|
|
240
|
+
id: `tool-result-${event.toolResult.toolCallId}`,
|
|
241
|
+
role: 'tool',
|
|
242
|
+
content: JSON.stringify(event.toolResult.result),
|
|
243
|
+
createdAt: event.timestamp.toISOString(),
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
try {
|
|
248
|
+
setError(null);
|
|
249
|
+
await adapter.connect();
|
|
250
|
+
await adapter.sendMessage(createUserMessage(prompt));
|
|
251
|
+
return { finalAssistantText: latestAssistantText, pendingApproval };
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
setError(error instanceof Error ? error.message : 'Streaming request failed');
|
|
255
|
+
throw error;
|
|
256
|
+
}
|
|
257
|
+
finally {
|
|
258
|
+
if (options?.keepAliveForApproval && pendingApproval) {
|
|
259
|
+
approvalStreamRef.current = { adapter, unsubscribe };
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
unsubscribe();
|
|
263
|
+
adapter.disconnect();
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}, [agentBaseUrl, token, upsertSidebarMessage]);
|
|
267
|
+
useEffect(() => {
|
|
268
|
+
return () => {
|
|
269
|
+
if (approvalStreamRef.current) {
|
|
270
|
+
approvalStreamRef.current.unsubscribe();
|
|
271
|
+
approvalStreamRef.current.adapter.disconnect();
|
|
272
|
+
approvalStreamRef.current = null;
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
}, []);
|
|
276
|
+
// ── Create approval agent on local server ────────────────────────────────
|
|
277
|
+
useEffect(() => {
|
|
278
|
+
if (!isReady)
|
|
279
|
+
return;
|
|
280
|
+
let isCancelled = false;
|
|
281
|
+
const createApprovalAgent = async () => {
|
|
282
|
+
try {
|
|
283
|
+
const response = await authFetch(`${agentBaseUrl}/api/v1/agents`, {
|
|
284
|
+
method: 'POST',
|
|
285
|
+
body: JSON.stringify({
|
|
286
|
+
name: APPROVAL_AGENT_NAME,
|
|
287
|
+
description: 'Agent with once trigger and tool approval',
|
|
288
|
+
agent_library: 'pydantic-ai',
|
|
289
|
+
transport: 'vercel-ai',
|
|
290
|
+
agent_spec_id: APPROVAL_AGENT_SPEC_ID,
|
|
291
|
+
tools: ['runtime-sensitive-echo'],
|
|
292
|
+
}),
|
|
293
|
+
});
|
|
294
|
+
let resolvedId = APPROVAL_AGENT_NAME;
|
|
295
|
+
if (response.ok) {
|
|
296
|
+
const data = await response.json();
|
|
297
|
+
resolvedId = data?.id || APPROVAL_AGENT_NAME;
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
const contentType = response.headers.get('content-type') || '';
|
|
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
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (!isCancelled) {
|
|
318
|
+
setApprovalAgentId(resolvedId);
|
|
319
|
+
setApprovalAgentReady(true);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
catch (error) {
|
|
323
|
+
console.warn('Approval agent creation failed:', error);
|
|
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);
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
// Non-fatal
|
|
344
|
+
}
|
|
345
|
+
}, [approvalAgentReady, agentBaseUrl, approvalAgentId, authFetch]);
|
|
346
|
+
useEffect(() => {
|
|
347
|
+
if (!hasTriggeredApproval)
|
|
348
|
+
return;
|
|
349
|
+
void pollApprovals();
|
|
350
|
+
const interval = setInterval(pollApprovals, 2000);
|
|
351
|
+
return () => clearInterval(interval);
|
|
352
|
+
}, [hasTriggeredApproval, pollApprovals]);
|
|
353
|
+
// Approval sidebar messages are populated from live Vercel stream events.
|
|
354
|
+
// ── Approve / Reject handlers ───────────────────────────────────────────
|
|
355
|
+
const handleApprove = useCallback(async (requestId) => {
|
|
356
|
+
setApprovalLoading(requestId);
|
|
357
|
+
setApprovalError(null);
|
|
358
|
+
try {
|
|
359
|
+
const approval = approvals.find(a => a.id === requestId);
|
|
360
|
+
const res = await authFetch(`${agentBaseUrl}/api/v1/tool-approvals/${requestId}/approve`, { method: 'POST', body: JSON.stringify({}) });
|
|
361
|
+
if (!res.ok) {
|
|
362
|
+
throw new Error(`Approve failed (${res.status})`);
|
|
363
|
+
}
|
|
364
|
+
if (approval?.tool_call_id && approvalStreamRef.current) {
|
|
365
|
+
await approvalStreamRef.current.adapter.sendToolResult(approval.tool_call_id, {
|
|
366
|
+
toolCallId: approval.tool_call_id,
|
|
367
|
+
success: true,
|
|
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();
|
|
376
|
+
approvalStreamRef.current = null;
|
|
377
|
+
}
|
|
378
|
+
setApprovals(prev => prev.filter(a => a.id !== requestId));
|
|
379
|
+
void pollApprovals();
|
|
380
|
+
}
|
|
381
|
+
catch (error) {
|
|
382
|
+
setApprovalError(error instanceof Error ? error.message : 'Failed to approve');
|
|
383
|
+
}
|
|
384
|
+
finally {
|
|
385
|
+
setApprovalLoading(null);
|
|
386
|
+
}
|
|
387
|
+
}, [agentBaseUrl, authFetch, pollApprovals, approvals]);
|
|
388
|
+
const handleReject = useCallback(async (requestId, note) => {
|
|
389
|
+
setApprovalLoading(requestId);
|
|
390
|
+
setApprovalError(null);
|
|
391
|
+
try {
|
|
392
|
+
const approval = approvals.find(a => a.id === requestId);
|
|
393
|
+
const res = await authFetch(`${agentBaseUrl}/api/v1/tool-approvals/${requestId}/reject`, { method: 'POST', body: JSON.stringify(note ? { note } : {}) });
|
|
394
|
+
if (!res.ok) {
|
|
395
|
+
throw new Error(`Reject failed (${res.status})`);
|
|
396
|
+
}
|
|
397
|
+
if (approval?.tool_call_id && approvalStreamRef.current) {
|
|
398
|
+
await approvalStreamRef.current.adapter.sendToolResult(approval.tool_call_id, {
|
|
399
|
+
toolCallId: approval.tool_call_id,
|
|
400
|
+
success: true,
|
|
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();
|
|
409
|
+
approvalStreamRef.current = null;
|
|
410
|
+
}
|
|
411
|
+
setApprovals(prev => prev.filter(a => a.id !== requestId));
|
|
412
|
+
void pollApprovals();
|
|
413
|
+
}
|
|
414
|
+
catch (error) {
|
|
415
|
+
setApprovalError(error instanceof Error ? error.message : 'Failed to reject');
|
|
416
|
+
}
|
|
417
|
+
finally {
|
|
418
|
+
setApprovalLoading(null);
|
|
419
|
+
}
|
|
420
|
+
}, [agentBaseUrl, authFetch, pollApprovals, approvals]);
|
|
421
|
+
// ── Launch once trigger with approval ────────────────────────────────────
|
|
422
|
+
const handleLaunchOnceApproval = useCallback(async () => {
|
|
423
|
+
if (!agentBaseUrl || !approvalAgentReady)
|
|
424
|
+
return;
|
|
425
|
+
setIsLaunchingApproval(true);
|
|
426
|
+
setApprovalFlash(null);
|
|
427
|
+
setApprovalSidebarMessages([]);
|
|
428
|
+
setSidebarMessagesError(null);
|
|
429
|
+
setHasTriggeredApproval(true);
|
|
430
|
+
const runId = `once-approval-${Date.now()}`;
|
|
431
|
+
const startTime = new Date().toISOString();
|
|
432
|
+
setTriggerHistory(prev => [
|
|
433
|
+
{
|
|
434
|
+
id: runId,
|
|
435
|
+
timestamp: startTime,
|
|
436
|
+
status: 'running',
|
|
437
|
+
source: 'once',
|
|
438
|
+
},
|
|
439
|
+
...prev,
|
|
440
|
+
]);
|
|
441
|
+
try {
|
|
442
|
+
setApprovalFlash('Once trigger with approval launched — waiting for tool approval.');
|
|
443
|
+
const streamResult = await streamRunMessages(approvalAgentId, ONCE_TRIGGER_APPROVAL_PROMPT, setApprovalSidebarMessages, setSidebarMessagesError, { keepAliveForApproval: true });
|
|
444
|
+
if (!streamResult.pendingApproval && approvalStreamRef.current) {
|
|
445
|
+
approvalStreamRef.current.unsubscribe();
|
|
446
|
+
approvalStreamRef.current.adapter.disconnect();
|
|
447
|
+
approvalStreamRef.current = null;
|
|
448
|
+
}
|
|
449
|
+
setTriggerHistory(prev => prev.map(r => r.id === runId
|
|
450
|
+
? {
|
|
451
|
+
...r,
|
|
452
|
+
status: 'success',
|
|
453
|
+
duration_ms: Date.now() - new Date(startTime).getTime(),
|
|
454
|
+
}
|
|
455
|
+
: r));
|
|
456
|
+
}
|
|
457
|
+
catch {
|
|
458
|
+
setApprovalFlash('Network error');
|
|
459
|
+
setTriggerHistory(prev => prev.map(r => r.id === runId ? { ...r, status: 'failure' } : r));
|
|
460
|
+
}
|
|
461
|
+
finally {
|
|
462
|
+
setIsLaunchingApproval(false);
|
|
463
|
+
}
|
|
464
|
+
}, [
|
|
465
|
+
agentBaseUrl,
|
|
466
|
+
approvalAgentReady,
|
|
467
|
+
approvalAgentId,
|
|
468
|
+
streamRunMessages,
|
|
469
|
+
]);
|
|
470
|
+
// ── Pending approvals for banner/dialog ──────────────────────────────────
|
|
471
|
+
const pendingApprovals = approvals.map(req => ({
|
|
472
|
+
id: req.id,
|
|
473
|
+
toolName: req.tool_name,
|
|
474
|
+
toolDescription: req.note,
|
|
475
|
+
args: req.tool_args ?? {},
|
|
476
|
+
agentId: approvalAgentId,
|
|
477
|
+
requestedAt: req.created_at ?? new Date().toISOString(),
|
|
478
|
+
}));
|
|
151
479
|
// ── Update cron ──────────────────────────────────────────────────────────
|
|
152
480
|
const handleUpdateCron = useCallback(async () => {
|
|
153
481
|
if (!agentBaseUrl || !editCron.trim())
|
|
@@ -279,8 +607,14 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
279
607
|
return;
|
|
280
608
|
setIsLaunchingOnce(true);
|
|
281
609
|
setOnceFlash(null);
|
|
610
|
+
setStreamedOnceOutput(null);
|
|
611
|
+
setStreamedOnceEndedAt(null);
|
|
612
|
+
setSidebarMessages([]);
|
|
613
|
+
setSidebarMessagesError(null);
|
|
282
614
|
const runId = `once-${Date.now()}`;
|
|
283
615
|
const startTime = new Date().toISOString();
|
|
616
|
+
setLastOnceStartedAt(startTime);
|
|
617
|
+
setHasTriggeredOnce(true);
|
|
284
618
|
setTriggerHistory(prev => [
|
|
285
619
|
{
|
|
286
620
|
id: runId,
|
|
@@ -291,21 +625,17 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
291
625
|
...prev,
|
|
292
626
|
]);
|
|
293
627
|
try {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
else {
|
|
306
|
-
setOnceFlash(`Launch failed (${res.status})`);
|
|
307
|
-
setTriggerHistory(prev => prev.map(r => r.id === runId ? { ...r, status: 'failure' } : r));
|
|
308
|
-
}
|
|
628
|
+
setOnceFlash('Once trigger launched — streaming live output.');
|
|
629
|
+
const streamResult = await streamRunMessages(agentId, ONCE_TRIGGER_PROMPT, setSidebarMessages, setSidebarMessagesError);
|
|
630
|
+
setStreamedOnceOutput(streamResult.finalAssistantText);
|
|
631
|
+
setStreamedOnceEndedAt(new Date().toISOString());
|
|
632
|
+
setTriggerHistory(prev => prev.map(r => r.id === runId
|
|
633
|
+
? {
|
|
634
|
+
...r,
|
|
635
|
+
status: 'success',
|
|
636
|
+
duration_ms: Date.now() - new Date(startTime).getTime(),
|
|
637
|
+
}
|
|
638
|
+
: r));
|
|
309
639
|
}
|
|
310
640
|
catch {
|
|
311
641
|
setOnceFlash('Network error');
|
|
@@ -314,7 +644,7 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
314
644
|
finally {
|
|
315
645
|
setIsLaunchingOnce(false);
|
|
316
646
|
}
|
|
317
|
-
}, [agentBaseUrl, agentId,
|
|
647
|
+
}, [agentBaseUrl, agentId, streamRunMessages]);
|
|
318
648
|
// ── Loading / Error ──────────────────────────────────────────────────────
|
|
319
649
|
if (!isReady && runtimeStatus !== 'error') {
|
|
320
650
|
return (_jsxs(Box, { sx: {
|
|
@@ -329,15 +659,9 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
329
659
|
: 'Creating trigger demo agent…' })] }));
|
|
330
660
|
}
|
|
331
661
|
if (runtimeStatus === 'error' || hookError) {
|
|
332
|
-
return (
|
|
333
|
-
display: 'flex',
|
|
334
|
-
flexDirection: 'column',
|
|
335
|
-
alignItems: 'center',
|
|
336
|
-
justifyContent: 'center',
|
|
337
|
-
height: '100vh',
|
|
338
|
-
gap: 3,
|
|
339
|
-
}, children: [_jsx(AlertIcon, { size: 48 }), _jsx(Text, { sx: { color: 'danger.fg' }, children: hookError || 'Agent failed to start' })] }));
|
|
662
|
+
return _jsx(ErrorView, { error: hookError, onLogout: onLogout });
|
|
340
663
|
}
|
|
664
|
+
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}'`;
|
|
341
665
|
return (_jsxs(Box, { sx: {
|
|
342
666
|
height: 'calc(100vh - 60px)',
|
|
343
667
|
display: 'flex',
|
|
@@ -351,13 +675,24 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
351
675
|
borderBottom: '1px solid',
|
|
352
676
|
borderColor: 'border.default',
|
|
353
677
|
flexShrink: 0,
|
|
354
|
-
}, children: [_jsx(ClockIcon, { size: 16 }), _jsxs(Heading, { as: "h3", sx: { fontSize: 2, flex: 1 }, children: ["Triggers \u2014 ", agentId] }), token && _jsx(UserBadge, { token: token, variant: "small" }), _jsx(Button, { size: "small", variant: "invisible", onClick: onLogout, leadingVisual: SignOutIcon, sx: { color: 'fg.muted' }, children: "Sign out" })] }),
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
678
|
+
}, children: [_jsx(ClockIcon, { size: 16 }), _jsxs(Heading, { as: "h3", sx: { fontSize: 2, flex: 1 }, children: ["Triggers \u2014 ", agentId] }), token && _jsx(UserBadge, { token: token, variant: "small" }), _jsx(Button, { size: "small", variant: "invisible", onClick: onLogout, leadingVisual: SignOutIcon, sx: { color: 'fg.muted' }, children: "Sign out" })] }), _jsx(ToolApprovalBanner, { pendingApprovals: pendingApprovals, onReview: approval => {
|
|
679
|
+
const req = approvals.find(a => a.id === approval.id) || null;
|
|
680
|
+
setActiveApproval(req);
|
|
681
|
+
}, onApproveAll: async () => {
|
|
682
|
+
for (const a of approvals) {
|
|
683
|
+
await handleApprove(a.id);
|
|
684
|
+
}
|
|
685
|
+
} }), _jsx(ToolApprovalDialog, { isOpen: !!activeApproval, toolName: activeApproval?.tool_name ?? '', toolDescription: activeApproval?.note, args: activeApproval?.tool_args ?? {}, onApprove: async () => {
|
|
686
|
+
if (activeApproval) {
|
|
687
|
+
await handleApprove(activeApproval.id);
|
|
688
|
+
setActiveApproval(null);
|
|
689
|
+
}
|
|
690
|
+
}, onDeny: async () => {
|
|
691
|
+
if (activeApproval) {
|
|
692
|
+
await handleReject(activeApproval.id, 'Rejected from dialog');
|
|
693
|
+
setActiveApproval(null);
|
|
694
|
+
}
|
|
695
|
+
}, 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: _jsx(Chat, { protocol: "vercel-ai", baseUrl: agentBaseUrl, agentId: agentId, authToken: chatAuthToken, title: "Trigger Agent", description: `View-only trigger output. Cron: ${cronExpr} | Webhook: ${webhookEnabled ? 'on' : 'off'} | Event: ${eventSubscribed ? eventTopic : 'none'}`, showHeader: true, autoFocus: false, height: "100%", runtimeId: agentId, showInput: false, showModelSelector: false, showToolsMenu: false, showSkillsMenu: false }) }), _jsxs(Box, { sx: {
|
|
361
696
|
width: 380,
|
|
362
697
|
borderLeft: '1px solid',
|
|
363
698
|
borderColor: 'border.default',
|
|
@@ -393,7 +728,239 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
393
728
|
p: 3,
|
|
394
729
|
borderBottom: '1px solid',
|
|
395
730
|
borderColor: 'border.default',
|
|
396
|
-
}, 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
|
|
731
|
+
}, 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: {
|
|
732
|
+
bg: 'canvas.subtle',
|
|
733
|
+
border: '1px solid',
|
|
734
|
+
borderColor: 'border.default',
|
|
735
|
+
borderRadius: 2,
|
|
736
|
+
p: 2,
|
|
737
|
+
mb: 2,
|
|
738
|
+
display: 'grid',
|
|
739
|
+
gap: 1,
|
|
740
|
+
}, 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 || !approvalAgentReady, sx: { width: '100%', mt: 2 }, children: isLaunchingApproval ? 'Launching…' : '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" }), (() => {
|
|
741
|
+
const outputEvent = lastOnceStartedAt
|
|
742
|
+
? [...agentEvents]
|
|
743
|
+
.filter(e => e.kind === 'agent-output' &&
|
|
744
|
+
new Date(e.created_at).getTime() >=
|
|
745
|
+
new Date(lastOnceStartedAt).getTime() - 5000)
|
|
746
|
+
.sort((a, b) => new Date(b.created_at).getTime() -
|
|
747
|
+
new Date(a.created_at).getTime())[0]
|
|
748
|
+
: undefined;
|
|
749
|
+
const hasStreamFallback = !outputEvent && !isLaunchingOnce && streamedOnceOutput !== null;
|
|
750
|
+
if (!outputEvent && !hasStreamFallback) {
|
|
751
|
+
return (_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2, mb: 2 }, children: [_jsx(Spinner, { size: "small" }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: "Waiting for agent output\u2026" })] }));
|
|
752
|
+
}
|
|
753
|
+
const p = outputEvent?.payload;
|
|
754
|
+
const outputText = outputEvent
|
|
755
|
+
? (p?.outputs ? String(p.outputs) : '')
|
|
756
|
+
: (streamedOnceOutput ?? '');
|
|
757
|
+
const exitStatus = outputEvent ? p?.exit_status : 'completed';
|
|
758
|
+
const durationMs = outputEvent
|
|
759
|
+
? p?.duration_ms
|
|
760
|
+
: (lastOnceStartedAt && streamedOnceEndedAt
|
|
761
|
+
? new Date(streamedOnceEndedAt).getTime() -
|
|
762
|
+
new Date(lastOnceStartedAt).getTime()
|
|
763
|
+
: undefined);
|
|
764
|
+
const endedAt = outputEvent ? p?.ended_at : streamedOnceEndedAt;
|
|
765
|
+
return (_jsxs(Box, { sx: {
|
|
766
|
+
mb: 2,
|
|
767
|
+
border: '1px solid',
|
|
768
|
+
borderColor: exitStatus === 'error'
|
|
769
|
+
? 'danger.muted'
|
|
770
|
+
: 'success.muted',
|
|
771
|
+
borderRadius: 2,
|
|
772
|
+
overflow: 'hidden',
|
|
773
|
+
}, children: [_jsxs(Box, { sx: {
|
|
774
|
+
display: 'flex',
|
|
775
|
+
alignItems: 'center',
|
|
776
|
+
gap: 1,
|
|
777
|
+
px: 2,
|
|
778
|
+
py: 1,
|
|
779
|
+
bg: exitStatus === 'error'
|
|
780
|
+
? 'danger.subtle'
|
|
781
|
+
: 'success.subtle',
|
|
782
|
+
borderBottom: '1px solid',
|
|
783
|
+
borderColor: exitStatus === 'error'
|
|
784
|
+
? 'danger.muted'
|
|
785
|
+
: 'success.muted',
|
|
786
|
+
}, 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: {
|
|
787
|
+
fontSize: 0,
|
|
788
|
+
color: 'fg.muted',
|
|
789
|
+
ml: 'auto',
|
|
790
|
+
whiteSpace: 'nowrap',
|
|
791
|
+
}, 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: {
|
|
792
|
+
p: 2,
|
|
793
|
+
bg: 'canvas.default',
|
|
794
|
+
maxHeight: 300,
|
|
795
|
+
overflow: 'auto',
|
|
796
|
+
}, children: _jsx(Text, { sx: {
|
|
797
|
+
fontSize: 0,
|
|
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);
|
|
824
|
+
return (_jsxs(Box, { sx: {
|
|
825
|
+
p: 2,
|
|
826
|
+
bg: 'canvas.subtle',
|
|
827
|
+
borderRadius: 2,
|
|
828
|
+
border: '1px solid',
|
|
829
|
+
borderColor: 'border.default',
|
|
830
|
+
}, children: [_jsxs(Box, { sx: {
|
|
831
|
+
display: 'flex',
|
|
832
|
+
alignItems: 'center',
|
|
833
|
+
gap: 1,
|
|
834
|
+
mb: 1,
|
|
835
|
+
}, children: [_jsx(Label, { size: "small", variant: msg.role === 'assistant'
|
|
836
|
+
? 'accent'
|
|
837
|
+
: msg.role === 'tool'
|
|
838
|
+
? 'success'
|
|
839
|
+
: 'secondary', children: msg.role }), msg.createdAt && (_jsx(Text, { sx: {
|
|
840
|
+
fontSize: 0,
|
|
841
|
+
color: 'fg.muted',
|
|
842
|
+
marginLeft: 'auto',
|
|
843
|
+
whiteSpace: 'nowrap',
|
|
844
|
+
}, children: new Date(msg.createdAt).toLocaleTimeString() }))] }), _jsx(Text, { sx: {
|
|
845
|
+
fontSize: 0,
|
|
846
|
+
color: 'fg.default',
|
|
847
|
+
display: 'block',
|
|
848
|
+
whiteSpace: 'pre-wrap',
|
|
849
|
+
wordBreak: 'break-word',
|
|
850
|
+
}, children: content.length > 320
|
|
851
|
+
? `${content.slice(0, 320)}…`
|
|
852
|
+
: content })] }, `once-msg-${msg.id}`));
|
|
853
|
+
}) }))] })), 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: { display: 'flex', flexDirection: 'column', gap: 2, mb: 2 }, children: approvals.map(a => (_jsxs(Box, { sx: {
|
|
854
|
+
p: 2,
|
|
855
|
+
border: '1px solid',
|
|
856
|
+
borderColor: 'attention.muted',
|
|
857
|
+
borderRadius: 2,
|
|
858
|
+
bg: 'attention.subtle',
|
|
859
|
+
}, children: [_jsxs(Text, { sx: { fontWeight: 600, fontSize: 1, display: 'block', mb: 1 }, children: ["\uD83D\uDEE1\uFE0F ", a.tool_name] }), a.tool_args && (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', display: 'block', mb: 2, fontFamily: 'mono' }, 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 ? 'Approving…' : '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: {
|
|
860
|
+
ml: 2,
|
|
861
|
+
verticalAlign: 'middle',
|
|
862
|
+
width: 14,
|
|
863
|
+
height: 14,
|
|
864
|
+
minWidth: 14,
|
|
865
|
+
minHeight: 14,
|
|
866
|
+
} }))] }), hasTriggeredApproval && (_jsxs(_Fragment, { children: [_jsx(Heading, { as: "h4", sx: { fontSize: 1, mt: 3, mb: 2 }, children: "Generated Output" }), (() => {
|
|
867
|
+
const latestAssistantOutput = approvalSidebarMessages
|
|
868
|
+
.filter(msg => msg.role === 'assistant')
|
|
869
|
+
.sort((a, b) => {
|
|
870
|
+
const ta = a.createdAt ? new Date(a.createdAt).getTime() : 0;
|
|
871
|
+
const tb = b.createdAt ? new Date(b.createdAt).getTime() : 0;
|
|
872
|
+
return tb - ta;
|
|
873
|
+
})[0];
|
|
874
|
+
if (!latestAssistantOutput) {
|
|
875
|
+
return (_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 agent output\u2026" })] }));
|
|
876
|
+
}
|
|
877
|
+
const outputText = typeof latestAssistantOutput.content === 'string'
|
|
878
|
+
? latestAssistantOutput.content
|
|
879
|
+
: JSON.stringify(latestAssistantOutput.content);
|
|
880
|
+
return (_jsxs(Box, { sx: {
|
|
881
|
+
mb: 2,
|
|
882
|
+
border: '1px solid',
|
|
883
|
+
borderColor: 'success.muted',
|
|
884
|
+
borderRadius: 2,
|
|
885
|
+
overflow: 'hidden',
|
|
886
|
+
}, children: [_jsxs(Box, { sx: {
|
|
887
|
+
display: 'flex',
|
|
888
|
+
alignItems: 'center',
|
|
889
|
+
gap: 1,
|
|
890
|
+
px: 2,
|
|
891
|
+
py: 1,
|
|
892
|
+
bg: 'success.subtle',
|
|
893
|
+
borderBottom: '1px solid',
|
|
894
|
+
borderColor: 'success.muted',
|
|
895
|
+
}, children: [_jsx(Label, { variant: "success", size: "small", children: "completed" }), latestAssistantOutput.createdAt && (_jsx(Text, { sx: {
|
|
896
|
+
fontSize: 0,
|
|
897
|
+
color: 'fg.muted',
|
|
898
|
+
ml: 'auto',
|
|
899
|
+
whiteSpace: 'nowrap',
|
|
900
|
+
}, 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: {
|
|
901
|
+
p: 2,
|
|
902
|
+
bg: 'canvas.default',
|
|
903
|
+
maxHeight: 300,
|
|
904
|
+
overflow: 'auto',
|
|
905
|
+
}, children: _jsx(Text, { sx: {
|
|
906
|
+
fontSize: 0,
|
|
907
|
+
color: 'fg.default',
|
|
908
|
+
display: 'block',
|
|
909
|
+
whiteSpace: 'pre-wrap',
|
|
910
|
+
wordBreak: 'break-word',
|
|
911
|
+
fontFamily: 'mono',
|
|
912
|
+
}, children: outputText || '(empty output)' }) })] }));
|
|
913
|
+
})()] })), approvalSidebarMessages.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 approval agent messages\u2026" })] })) : (_jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2, mb: 2 }, children: approvalSidebarMessages
|
|
914
|
+
.slice()
|
|
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: {
|
|
927
|
+
p: 2,
|
|
928
|
+
bg: 'canvas.subtle',
|
|
929
|
+
borderRadius: 2,
|
|
930
|
+
border: '1px solid',
|
|
931
|
+
borderColor: 'border.default',
|
|
932
|
+
}, children: [_jsxs(Box, { sx: {
|
|
933
|
+
display: 'flex',
|
|
934
|
+
alignItems: 'center',
|
|
935
|
+
gap: 1,
|
|
936
|
+
mb: 1,
|
|
937
|
+
}, children: [_jsx(Label, { size: "small", variant: msg.role === 'assistant'
|
|
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: {
|
|
947
|
+
fontSize: 0,
|
|
948
|
+
color: 'fg.default',
|
|
949
|
+
display: 'block',
|
|
950
|
+
whiteSpace: 'pre-wrap',
|
|
951
|
+
wordBreak: 'break-word',
|
|
952
|
+
}, children: content.length > 320
|
|
953
|
+
? `${content.slice(0, 320)}…`
|
|
954
|
+
: content })] }, `approval-msg-${msg.id}`));
|
|
955
|
+
}) }))] })), _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: {
|
|
956
|
+
mt: 1,
|
|
957
|
+
bg: 'canvas.subtle',
|
|
958
|
+
borderRadius: 2,
|
|
959
|
+
p: 2,
|
|
960
|
+
fontFamily: 'mono',
|
|
961
|
+
fontSize: 0,
|
|
962
|
+
wordBreak: 'break-all',
|
|
963
|
+
}, children: triggerRunCurl })] }), _jsx(Button, { size: "small", leadingVisual: CopyIcon, onClick: () => navigator.clipboard.writeText(triggerRunCurl), children: "Copy streaming command" })] })] })), activeTab === 'cron' && (_jsxs(Box, { sx: {
|
|
397
964
|
p: 3,
|
|
398
965
|
borderBottom: '1px solid',
|
|
399
966
|
borderColor: 'border.default',
|
|
@@ -446,7 +1013,7 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
446
1013
|
mb: 1,
|
|
447
1014
|
}, children: [_jsx(Label, { variant: evt.kind === 'agent-started'
|
|
448
1015
|
? 'accent'
|
|
449
|
-
: evt.kind === 'agent-
|
|
1016
|
+
: evt.kind === 'agent-output'
|
|
450
1017
|
? 'success'
|
|
451
1018
|
: evt.kind?.includes('alert')
|
|
452
1019
|
? 'danger'
|
|
@@ -468,7 +1035,7 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
468
1035
|
}, 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 &&
|
|
469
1036
|
(() => {
|
|
470
1037
|
const p = evt.payload;
|
|
471
|
-
return (_jsxs(Box, { sx: { fontSize: 0, color: 'fg.muted' }, children: [evt.kind === 'agent-
|
|
1038
|
+
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: {
|
|
472
1039
|
all: 'unset',
|
|
473
1040
|
display: 'block',
|
|
474
1041
|
width: '100%',
|
|
@@ -483,7 +1050,7 @@ const AgentTriggerInner = ({ onLogout, }) => {
|
|
|
483
1050
|
fontWeight: 'bold',
|
|
484
1051
|
color: 'fg.default',
|
|
485
1052
|
whiteSpace: 'nowrap',
|
|
486
|
-
}, children: "Output:" }), _jsx(Truncate, { title: String(p.outputs), maxWidth: "100%", sx: { minWidth: 0, flex: 1 }, children: String(p.outputs) })] }) }) })), evt.kind === 'agent-
|
|
1053
|
+
}, children: "Output:" }), _jsx(Truncate, { title: String(p.outputs), maxWidth: "100%", sx: { minWidth: 0, flex: 1 }, children: String(p.outputs) })] }) }) })), evt.kind === 'agent-output' &&
|
|
487
1054
|
p.duration_ms != null && (_jsxs(Text, { as: "p", children: ["Duration:", ' ', (Number(p.duration_ms) / 1000).toFixed(1), "s"] })), evt.kind?.includes('guardrail') && p.message && (_jsx(Text, { as: "p", children: String(p.message) }))] }));
|
|
488
1055
|
})()] }, evt.id))) }))] })] })] })] }));
|
|
489
1056
|
};
|