@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.
Files changed (73) hide show
  1. package/README.md +13 -131
  2. package/lib/chat/Chat.d.ts +3 -1
  3. package/lib/chat/Chat.js +2 -2
  4. package/lib/chat/base/ChatBase.js +52 -1
  5. package/lib/chat/messages/ChatMessageList.js +17 -4
  6. package/lib/client/AgentsMixin.d.ts +48 -1
  7. package/lib/client/AgentsMixin.js +109 -0
  8. package/lib/components/NotificationEventCard.js +51 -26
  9. package/lib/components/OutputCard.js +21 -7
  10. package/lib/components/ToolApprovalCard.js +20 -2
  11. package/lib/examples/AgentCheckpointsExample.js +2 -8
  12. package/lib/examples/AgentCodemodeExample.js +3 -9
  13. package/lib/examples/AgentEvalsExample.js +3 -9
  14. package/lib/examples/AgentGuardrailsExample.js +3 -9
  15. package/lib/examples/AgentMemoryExample.js +3 -9
  16. package/lib/examples/AgentMonitoringExample.js +3 -9
  17. package/lib/examples/AgentNotificationsExample.js +2 -8
  18. package/lib/examples/AgentOutputsExample.js +3 -9
  19. package/lib/examples/AgentSandboxExample.js +3 -9
  20. package/lib/examples/AgentSkillsExample.js +3 -9
  21. package/lib/examples/AgentToolApprovalsExample.js +89 -24
  22. package/lib/examples/AgentTriggersExample.js +604 -37
  23. package/lib/examples/ChatExample.js +2 -10
  24. package/lib/examples/components/ErrorView.d.ts +14 -0
  25. package/lib/examples/components/ErrorView.js +20 -0
  26. package/lib/examples/components/index.d.ts +2 -0
  27. package/lib/examples/components/index.js +1 -0
  28. package/lib/examples/main.d.ts +1 -0
  29. package/lib/examples/main.js +1 -0
  30. package/lib/protocols/VercelAIAdapter.d.ts +2 -0
  31. package/lib/protocols/VercelAIAdapter.js +86 -20
  32. package/lib/shims/json5.d.ts +4 -0
  33. package/lib/shims/json5.js +8 -0
  34. package/lib/specs/agents/agents.js +241 -1390
  35. package/lib/specs/agents/index.js +1 -3
  36. package/lib/specs/envvars.js +20 -27
  37. package/lib/specs/evals.js +6 -6
  38. package/lib/specs/events.d.ts +10 -2
  39. package/lib/specs/events.js +84 -126
  40. package/lib/specs/frontendTools.js +2 -2
  41. package/lib/specs/guardrails.d.ts +7 -0
  42. package/lib/specs/guardrails.js +159 -240
  43. package/lib/specs/mcpServers.js +6 -35
  44. package/lib/specs/memory.d.ts +2 -0
  45. package/lib/specs/memory.js +17 -4
  46. package/lib/specs/models.js +5 -25
  47. package/lib/specs/notifications.js +18 -102
  48. package/lib/specs/outputs.js +9 -15
  49. package/lib/specs/skills.js +18 -18
  50. package/lib/specs/teams/index.js +1 -3
  51. package/lib/specs/teams/teams.js +348 -468
  52. package/lib/specs/tools.js +6 -3
  53. package/lib/specs/triggers.js +11 -61
  54. package/lib/types/tools.d.ts +2 -0
  55. package/package.json +1 -1
  56. package/scripts/codegen/__pycache__/versioning.cpython-313.pyc +0 -0
  57. package/scripts/codegen/generate_agents.py +4 -1
  58. package/scripts/codegen/generate_events.py +12 -4
  59. package/scripts/codegen/generate_tools.py +20 -0
  60. package/style/primer-primitives.css +1 -6
  61. package/scripts/codegen/__pycache__/generate_agents.cpython-313.pyc +0 -0
  62. package/scripts/codegen/__pycache__/generate_envvars.cpython-313.pyc +0 -0
  63. package/scripts/codegen/__pycache__/generate_evals.cpython-313.pyc +0 -0
  64. package/scripts/codegen/__pycache__/generate_guardrails.cpython-313.pyc +0 -0
  65. package/scripts/codegen/__pycache__/generate_mcp_servers.cpython-313.pyc +0 -0
  66. package/scripts/codegen/__pycache__/generate_memory.cpython-313.pyc +0 -0
  67. package/scripts/codegen/__pycache__/generate_models.cpython-313.pyc +0 -0
  68. package/scripts/codegen/__pycache__/generate_notifications.cpython-313.pyc +0 -0
  69. package/scripts/codegen/__pycache__/generate_outputs.cpython-313.pyc +0 -0
  70. package/scripts/codegen/__pycache__/generate_skills.cpython-313.pyc +0 -0
  71. package/scripts/codegen/__pycache__/generate_teams.cpython-313.pyc +0 -0
  72. package/scripts/codegen/__pycache__/generate_tools.cpython-313.pyc +0 -0
  73. 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 { AlertIcon, ClockIcon, SyncIcon, CheckCircleIcon, XCircleIcon, PlayIcon, SignOutIcon, GlobeIcon, ZapIcon, EyeIcon, EyeClosedIcon, TrashIcon, CopyIcon, } from '@primer/octicons-react';
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: 'ag-ui',
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
- const res = await authFetch(`${agentBaseUrl}/api/v1/agents/${agentId}/trigger/run`, { method: 'POST', body: JSON.stringify({ source: 'once' }) });
295
- if (res.ok) {
296
- setOnceFlash('Once trigger launched — agent will run and terminate.');
297
- setTriggerHistory(prev => prev.map(r => r.id === runId
298
- ? {
299
- ...r,
300
- status: 'success',
301
- duration_ms: Date.now() - new Date(startTime).getTime(),
302
- }
303
- : r));
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, authFetch]);
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 (_jsxs(Box, { sx: {
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" })] }), _jsxs(Box, { sx: { flex: 1, minHeight: 0, display: 'flex' }, children: [_jsx(Box, { sx: { flex: 1, minWidth: 0 }, children: _jsx(Chat, { protocol: "ag-ui", baseUrl: agentBaseUrl, agentId: agentId, authToken: chatAuthToken, title: "Trigger Agent", placeholder: "Ask about your scheduled or event-driven KPI reports\u2026", description: `Cron: ${cronExpr} | Webhook: ${webhookEnabled ? 'on' : 'off'} | Event: ${eventSubscribed ? eventTopic : 'none'}`, showHeader: true, autoFocus: true, height: "100%", runtimeId: agentId, historyEndpoint: `${agentBaseUrl}/api/v1/history`, suggestions: [
355
- {
356
- title: 'Last run',
357
- message: 'What happened in the last scheduled run?',
358
- },
359
- { title: 'KPIs today', message: "Show me today's KPI summary" },
360
- ], submitOnSuggestionClick: true }) }), _jsxs(Box, { sx: {
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 the agent once. It will execute its trigger prompt, emit lifecycle events (AGENT_STARTED / AGENT_ENDED), and then terminate the runtime automatically." }), _jsx(Button, { size: "small", variant: "primary", leadingVisual: ZapIcon, onClick: handleLaunchOnce, disabled: isLaunchingOnce, sx: { width: '100%' }, children: isLaunchingOnce ? 'Launching…' : 'Launch Once' }), onceFlash && (_jsx(Flash, { variant: onceFlash.includes('launched') ? 'success' : 'danger', sx: { mt: 2, fontSize: 0 }, children: onceFlash }))] })), activeTab === 'cron' && (_jsxs(Box, { sx: {
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-ended'
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-ended' && p.outputs && (_jsx(Tooltip, { text: String(p.outputs), direction: "n", children: _jsx("button", { type: "button", "aria-label": String(p.outputs), style: {
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-ended' &&
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
  };