@getcatalystiq/agent-plane-ui 0.1.30 → 0.1.32
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/dist/index.cjs +711 -261
- package/dist/index.d.cts +83 -2
- package/dist/index.d.ts +83 -2
- package/dist/index.js +464 -27
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -1,16 +1,76 @@
|
|
|
1
1
|
import { Card, CardHeader, CardTitle, CardContent, useApi, Skeleton, FileTreeEditor } from './chunk-CE2RHDPY.js';
|
|
2
2
|
export { Card, CardContent, CardDescription, CardHeader, CardTitle, Skeleton, useApi } from './chunk-CE2RHDPY.js';
|
|
3
|
-
import { cn, Dialog, DialogContent, DialogHeader, DialogTitle, DialogBody, DialogFooter, Button, Badge, useNavigation,
|
|
3
|
+
import { cn, useAgentPlaneClient, Dialog, DialogContent, DialogHeader, DialogTitle, DialogBody, DialogFooter, Button, Badge, useNavigation, DialogDescription, FormField, Input, supportsClaudeRunner, buttonVariants } from './chunk-XFI227OB.js';
|
|
4
4
|
export { AgentPlaneProvider, Badge, Button, Dialog, DialogBody, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, FormField, Input, badgeVariants, buttonVariants, cn, useAgentPlaneClient, useAuthError, useNavigation } from './chunk-XFI227OB.js';
|
|
5
|
+
import * as React4 from 'react';
|
|
6
|
+
import React4__default, { lazy, useState, useRef, useCallback, useEffect, useMemo, Suspense } from 'react';
|
|
5
7
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
6
|
-
import * as React from 'react';
|
|
7
|
-
import React__default, { lazy, useState, useCallback, useMemo, useRef, useEffect, Suspense } from 'react';
|
|
8
8
|
import { useSWRConfig } from 'swr';
|
|
9
9
|
import ReactMarkdown from 'react-markdown';
|
|
10
10
|
import { Command } from 'cmdk';
|
|
11
11
|
import * as Popover from '@radix-ui/react-popover';
|
|
12
12
|
import remarkGfm from 'remark-gfm';
|
|
13
|
+
import * as ToastPrimitives from '@radix-ui/react-toast';
|
|
14
|
+
import { cva } from 'class-variance-authority';
|
|
13
15
|
|
|
16
|
+
var ACTIVE_STATUSES = /* @__PURE__ */ new Set(["running", "pending"]);
|
|
17
|
+
function useRunStream(runId, status) {
|
|
18
|
+
const client = useAgentPlaneClient();
|
|
19
|
+
const [events, setEvents] = useState([]);
|
|
20
|
+
const [isStreaming, setIsStreaming] = useState(false);
|
|
21
|
+
const [terminalEvent, setTerminalEvent] = useState(null);
|
|
22
|
+
const [streamingText, setStreamingText] = useState("");
|
|
23
|
+
const [error, setError] = useState(null);
|
|
24
|
+
const abortRef = useRef(null);
|
|
25
|
+
const startStream = useCallback(async (id, signal) => {
|
|
26
|
+
setIsStreaming(true);
|
|
27
|
+
setError(null);
|
|
28
|
+
try {
|
|
29
|
+
const stream = await client.runs.stream(id, { signal });
|
|
30
|
+
for await (const event of stream) {
|
|
31
|
+
if (signal.aborted) break;
|
|
32
|
+
if (event.type === "text_delta") {
|
|
33
|
+
const text = event.text ?? "";
|
|
34
|
+
setStreamingText((prev) => prev + text);
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (event.type === "assistant") {
|
|
38
|
+
setStreamingText("");
|
|
39
|
+
}
|
|
40
|
+
if (event.type === "stream_detached") {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
setEvents((prev) => [...prev, event]);
|
|
44
|
+
if (event.type === "result" || event.type === "error") {
|
|
45
|
+
setTerminalEvent(event);
|
|
46
|
+
setIsStreaming(false);
|
|
47
|
+
setStreamingText("");
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
setIsStreaming(false);
|
|
52
|
+
} catch (err) {
|
|
53
|
+
if (signal.aborted) return;
|
|
54
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
55
|
+
setIsStreaming(false);
|
|
56
|
+
}
|
|
57
|
+
}, [client]);
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (!runId || !ACTIVE_STATUSES.has(status)) {
|
|
60
|
+
setIsStreaming(false);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (terminalEvent) return;
|
|
64
|
+
const controller = new AbortController();
|
|
65
|
+
abortRef.current = controller;
|
|
66
|
+
startStream(runId, controller.signal);
|
|
67
|
+
return () => {
|
|
68
|
+
controller.abort();
|
|
69
|
+
abortRef.current = null;
|
|
70
|
+
};
|
|
71
|
+
}, [runId, status, startStream, terminalEvent]);
|
|
72
|
+
return { events, isStreaming, terminalEvent, streamingText, error };
|
|
73
|
+
}
|
|
14
74
|
function Select({ className = "", ...props }) {
|
|
15
75
|
return /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
|
|
16
76
|
/* @__PURE__ */ jsx(
|
|
@@ -27,7 +87,7 @@ function Select({ className = "", ...props }) {
|
|
|
27
87
|
] }) })
|
|
28
88
|
] });
|
|
29
89
|
}
|
|
30
|
-
var Textarea =
|
|
90
|
+
var Textarea = React4.forwardRef(
|
|
31
91
|
({ className, ...props }, ref) => {
|
|
32
92
|
return /* @__PURE__ */ jsx(
|
|
33
93
|
"textarea",
|
|
@@ -133,7 +193,7 @@ function PaginationBar({
|
|
|
133
193
|
return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-3 py-2 border-t border-border bg-muted/20 text-sm", children: [
|
|
134
194
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-muted-foreground", children: [
|
|
135
195
|
/* @__PURE__ */ jsx("span", { children: "Rows per page:" }),
|
|
136
|
-
PAGE_SIZE_OPTIONS.map((ps) => /* @__PURE__ */ jsx(
|
|
196
|
+
PAGE_SIZE_OPTIONS.map((ps) => /* @__PURE__ */ jsx(React4__default.Fragment, { children: renderLink(
|
|
137
197
|
buildHref(1, ps),
|
|
138
198
|
ps,
|
|
139
199
|
`px-2 py-0.5 rounded text-xs ${pageSize === ps ? "bg-primary text-primary-foreground font-medium" : "hover:bg-muted"}`
|
|
@@ -299,6 +359,102 @@ function DashboardPage({ initialData, chartComponent: ChartComponent }) {
|
|
|
299
359
|
ChartComponent && /* @__PURE__ */ jsx(ChartComponent, { stats: daily_stats })
|
|
300
360
|
] });
|
|
301
361
|
}
|
|
362
|
+
var TOAST_LIMIT = 5;
|
|
363
|
+
var TOAST_REMOVE_DELAY = 1e6;
|
|
364
|
+
var count = 0;
|
|
365
|
+
function genId() {
|
|
366
|
+
count = (count + 1) % Number.MAX_SAFE_INTEGER;
|
|
367
|
+
return count.toString();
|
|
368
|
+
}
|
|
369
|
+
var toastTimeouts = /* @__PURE__ */ new Map();
|
|
370
|
+
function addToRemoveQueue(toastId) {
|
|
371
|
+
if (toastTimeouts.has(toastId)) return;
|
|
372
|
+
const timeout = setTimeout(() => {
|
|
373
|
+
toastTimeouts.delete(toastId);
|
|
374
|
+
dispatch({ type: "REMOVE_TOAST", toastId });
|
|
375
|
+
}, TOAST_REMOVE_DELAY);
|
|
376
|
+
toastTimeouts.set(toastId, timeout);
|
|
377
|
+
}
|
|
378
|
+
function reducer(state, action) {
|
|
379
|
+
switch (action.type) {
|
|
380
|
+
case "ADD_TOAST":
|
|
381
|
+
return {
|
|
382
|
+
...state,
|
|
383
|
+
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT)
|
|
384
|
+
};
|
|
385
|
+
case "UPDATE_TOAST":
|
|
386
|
+
return {
|
|
387
|
+
...state,
|
|
388
|
+
toasts: state.toasts.map(
|
|
389
|
+
(t) => t.id === action.toast.id ? { ...t, ...action.toast } : t
|
|
390
|
+
)
|
|
391
|
+
};
|
|
392
|
+
case "DISMISS_TOAST": {
|
|
393
|
+
const { toastId } = action;
|
|
394
|
+
if (toastId) {
|
|
395
|
+
addToRemoveQueue(toastId);
|
|
396
|
+
} else {
|
|
397
|
+
state.toasts.forEach((t) => addToRemoveQueue(t.id));
|
|
398
|
+
}
|
|
399
|
+
return {
|
|
400
|
+
...state,
|
|
401
|
+
toasts: state.toasts.map(
|
|
402
|
+
(t) => t.id === toastId || toastId === void 0 ? { ...t, open: false } : t
|
|
403
|
+
)
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
case "REMOVE_TOAST":
|
|
407
|
+
if (action.toastId === void 0) {
|
|
408
|
+
return { ...state, toasts: [] };
|
|
409
|
+
}
|
|
410
|
+
return {
|
|
411
|
+
...state,
|
|
412
|
+
toasts: state.toasts.filter((t) => t.id !== action.toastId)
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
var listeners = [];
|
|
417
|
+
var memoryState = { toasts: [] };
|
|
418
|
+
function dispatch(action) {
|
|
419
|
+
memoryState = reducer(memoryState, action);
|
|
420
|
+
listeners.forEach((listener) => listener(memoryState));
|
|
421
|
+
}
|
|
422
|
+
function toast(props) {
|
|
423
|
+
const id = genId();
|
|
424
|
+
const update = (updateProps) => dispatch({ type: "UPDATE_TOAST", toast: { ...updateProps, id } });
|
|
425
|
+
const dismiss2 = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
|
|
426
|
+
dispatch({
|
|
427
|
+
type: "ADD_TOAST",
|
|
428
|
+
toast: {
|
|
429
|
+
...props,
|
|
430
|
+
id,
|
|
431
|
+
open: true,
|
|
432
|
+
onOpenChange: (open) => {
|
|
433
|
+
if (!open) dismiss2();
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
return { id, dismiss: dismiss2, update };
|
|
438
|
+
}
|
|
439
|
+
function dismiss(toastId) {
|
|
440
|
+
const action = toastId !== void 0 ? { type: "DISMISS_TOAST", toastId } : { type: "DISMISS_TOAST" };
|
|
441
|
+
dispatch(action);
|
|
442
|
+
}
|
|
443
|
+
function useToast() {
|
|
444
|
+
const [state, setState] = React4.useState(memoryState);
|
|
445
|
+
React4.useEffect(() => {
|
|
446
|
+
listeners.push(setState);
|
|
447
|
+
return () => {
|
|
448
|
+
const index = listeners.indexOf(setState);
|
|
449
|
+
if (index > -1) listeners.splice(index, 1);
|
|
450
|
+
};
|
|
451
|
+
}, [state]);
|
|
452
|
+
return {
|
|
453
|
+
...state,
|
|
454
|
+
toast,
|
|
455
|
+
dismiss
|
|
456
|
+
};
|
|
457
|
+
}
|
|
302
458
|
var SOURCES = [
|
|
303
459
|
{ value: "", label: "All Sources" },
|
|
304
460
|
{ value: "api", label: "API" },
|
|
@@ -308,11 +464,18 @@ var SOURCES = [
|
|
|
308
464
|
{ value: "a2a", label: "A2A" }
|
|
309
465
|
];
|
|
310
466
|
var VALID_SOURCES = SOURCES.filter((s) => s.value).map((s) => s.value);
|
|
467
|
+
var ACTIVE_STATUSES2 = /* @__PURE__ */ new Set(["running", "pending"]);
|
|
468
|
+
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled", "timed_out"]);
|
|
311
469
|
function RunListPage({ initialData }) {
|
|
312
470
|
const { LinkComponent, basePath } = useNavigation();
|
|
471
|
+
const { toast: toast2 } = useToast();
|
|
313
472
|
const [page, setPage] = useState(1);
|
|
314
473
|
const [pageSize, setPageSize] = useState(20);
|
|
315
474
|
const [sourceFilter, setSourceFilter] = useState(null);
|
|
475
|
+
const prevStatusesRef = useRef(/* @__PURE__ */ new Map());
|
|
476
|
+
const [activeRunsExist, setActiveRunsExist] = useState(
|
|
477
|
+
() => initialData?.data?.some((r) => ACTIVE_STATUSES2.has(r.status)) ?? false
|
|
478
|
+
);
|
|
316
479
|
const cacheKey = `runs-${page}-${pageSize}-${sourceFilter || "all"}`;
|
|
317
480
|
const { data, error, isLoading } = useApi(
|
|
318
481
|
cacheKey,
|
|
@@ -323,8 +486,29 @@ function RunListPage({ initialData }) {
|
|
|
323
486
|
...sourceFilter ? { triggered_by: sourceFilter } : {}
|
|
324
487
|
});
|
|
325
488
|
},
|
|
326
|
-
|
|
489
|
+
{
|
|
490
|
+
...initialData ? { fallbackData: initialData } : {},
|
|
491
|
+
refreshInterval: activeRunsExist ? 5e3 : 0
|
|
492
|
+
}
|
|
327
493
|
);
|
|
494
|
+
useEffect(() => {
|
|
495
|
+
if (!data?.data) return;
|
|
496
|
+
const prev = prevStatusesRef.current;
|
|
497
|
+
const next = /* @__PURE__ */ new Map();
|
|
498
|
+
for (const run of data.data) {
|
|
499
|
+
next.set(run.id, run.status);
|
|
500
|
+
const oldStatus = prev.get(run.id);
|
|
501
|
+
if (oldStatus && ACTIVE_STATUSES2.has(oldStatus) && TERMINAL_STATUSES.has(run.status)) {
|
|
502
|
+
toast2({
|
|
503
|
+
title: `Run ${run.status}`,
|
|
504
|
+
description: run.agent_name,
|
|
505
|
+
variant: run.status === "completed" ? "success" : "destructive"
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
prevStatusesRef.current = next;
|
|
510
|
+
setActiveRunsExist(data.data.some((r) => ACTIVE_STATUSES2.has(r.status)));
|
|
511
|
+
}, [data, toast2]);
|
|
328
512
|
const handleSourceChange = useCallback((e) => {
|
|
329
513
|
const value = e.target.value;
|
|
330
514
|
setSourceFilter(VALID_SOURCES.includes(value) ? value : null);
|
|
@@ -372,7 +556,8 @@ function RunListPage({ initialData }) {
|
|
|
372
556
|
/* @__PURE__ */ jsx(Th, { align: "right", children: "Cost" }),
|
|
373
557
|
/* @__PURE__ */ jsx(Th, { align: "right", children: "Turns" }),
|
|
374
558
|
/* @__PURE__ */ jsx(Th, { align: "right", children: "Duration" }),
|
|
375
|
-
/* @__PURE__ */ jsx(Th, { children: "Created" })
|
|
559
|
+
/* @__PURE__ */ jsx(Th, { children: "Created" }),
|
|
560
|
+
/* @__PURE__ */ jsx(Th, {})
|
|
376
561
|
] }),
|
|
377
562
|
/* @__PURE__ */ jsxs("tbody", { children: [
|
|
378
563
|
runs.map((r) => /* @__PURE__ */ jsxs(AdminTableRow, { children: [
|
|
@@ -393,13 +578,47 @@ function RunListPage({ initialData }) {
|
|
|
393
578
|
] }),
|
|
394
579
|
/* @__PURE__ */ jsx("td", { className: "p-3 text-right", children: r.num_turns }),
|
|
395
580
|
/* @__PURE__ */ jsx("td", { className: "p-3 text-right text-muted-foreground text-xs", children: r.duration_ms > 0 ? `${(r.duration_ms / 1e3).toFixed(1)}s` : "\u2014" }),
|
|
396
|
-
/* @__PURE__ */ jsx("td", { className: "p-3 text-muted-foreground text-xs", children: /* @__PURE__ */ jsx(LocalDate, { value: r.created_at }) })
|
|
581
|
+
/* @__PURE__ */ jsx("td", { className: "p-3 text-muted-foreground text-xs", children: /* @__PURE__ */ jsx(LocalDate, { value: r.created_at }) }),
|
|
582
|
+
/* @__PURE__ */ jsx("td", { className: "p-3", children: (r.status === "failed" || r.status === "cancelled" || r.status === "timed_out") && /* @__PURE__ */ jsx(RerunRowButton, { agentId: r.agent_id, prompt: r.prompt }) })
|
|
397
583
|
] }, r.id)),
|
|
398
|
-
runs.length === 0 && /* @__PURE__ */ jsx(EmptyRow, { colSpan:
|
|
584
|
+
runs.length === 0 && /* @__PURE__ */ jsx(EmptyRow, { colSpan: 10, children: "No runs found" })
|
|
399
585
|
] })
|
|
400
586
|
] })
|
|
401
587
|
] });
|
|
402
588
|
}
|
|
589
|
+
function RerunRowButton({ agentId, prompt }) {
|
|
590
|
+
const [rerunning, setRerunning] = useState(false);
|
|
591
|
+
const client = useAgentPlaneClient();
|
|
592
|
+
const { onNavigate, basePath } = useNavigation();
|
|
593
|
+
const { toast: toast2 } = useToast();
|
|
594
|
+
async function handleRerun(e) {
|
|
595
|
+
e.preventDefault();
|
|
596
|
+
e.stopPropagation();
|
|
597
|
+
setRerunning(true);
|
|
598
|
+
try {
|
|
599
|
+
const stream = await client.runs.create({ agent_id: agentId, prompt });
|
|
600
|
+
let newRunId = null;
|
|
601
|
+
for await (const event of stream) {
|
|
602
|
+
if (event.type === "run_started") {
|
|
603
|
+
newRunId = event.run_id;
|
|
604
|
+
break;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
if (newRunId) {
|
|
608
|
+
onNavigate(`${basePath}/runs/${newRunId}`);
|
|
609
|
+
}
|
|
610
|
+
} catch (err) {
|
|
611
|
+
toast2({
|
|
612
|
+
title: "Failed to rerun",
|
|
613
|
+
description: err instanceof Error ? err.message : "Unknown error",
|
|
614
|
+
variant: "destructive"
|
|
615
|
+
});
|
|
616
|
+
} finally {
|
|
617
|
+
setRerunning(false);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
return /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: handleRerun, disabled: rerunning, children: rerunning ? "\u2026" : "Rerun" });
|
|
621
|
+
}
|
|
403
622
|
function buildConversation(events) {
|
|
404
623
|
const items = [];
|
|
405
624
|
const toolCallMap = /* @__PURE__ */ new Map();
|
|
@@ -505,17 +724,51 @@ function buildConversation(events) {
|
|
|
505
724
|
}
|
|
506
725
|
return items;
|
|
507
726
|
}
|
|
508
|
-
function TranscriptViewer({ transcript, prompt }) {
|
|
727
|
+
function TranscriptViewer({ transcript, prompt, isStreaming = false }) {
|
|
509
728
|
const conversation = useMemo(() => buildConversation(transcript), [transcript]);
|
|
729
|
+
const scrollContainerRef = useRef(null);
|
|
730
|
+
const sentinelRef = useRef(null);
|
|
731
|
+
const userHasScrolledUpRef = useRef(false);
|
|
732
|
+
const [userHasScrolledUp, setUserHasScrolledUp] = useState(false);
|
|
733
|
+
const handleScroll = useCallback(() => {
|
|
734
|
+
const el = scrollContainerRef.current;
|
|
735
|
+
if (!el) return;
|
|
736
|
+
const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 50;
|
|
737
|
+
if (atBottom) {
|
|
738
|
+
userHasScrolledUpRef.current = false;
|
|
739
|
+
setUserHasScrolledUp(false);
|
|
740
|
+
} else {
|
|
741
|
+
userHasScrolledUpRef.current = true;
|
|
742
|
+
setUserHasScrolledUp(true);
|
|
743
|
+
}
|
|
744
|
+
}, []);
|
|
745
|
+
useEffect(() => {
|
|
746
|
+
if (isStreaming && !userHasScrolledUpRef.current) {
|
|
747
|
+
sentinelRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
748
|
+
}
|
|
749
|
+
}, [transcript.length, isStreaming]);
|
|
510
750
|
return /* @__PURE__ */ jsxs(Card, { children: [
|
|
511
751
|
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: "Transcript" }) }),
|
|
512
|
-
/* @__PURE__ */
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
752
|
+
/* @__PURE__ */ jsx(CardContent, { className: "p-0", children: /* @__PURE__ */ jsxs(
|
|
753
|
+
"div",
|
|
754
|
+
{
|
|
755
|
+
ref: scrollContainerRef,
|
|
756
|
+
onScroll: isStreaming ? handleScroll : void 0,
|
|
757
|
+
className: "space-y-3 overflow-y-auto px-6 pb-6",
|
|
758
|
+
children: [
|
|
759
|
+
prompt && /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-border bg-muted/20 px-4 py-3", children: [
|
|
760
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-muted-foreground mb-1", children: "Prompt" }),
|
|
761
|
+
/* @__PURE__ */ jsx("pre", { className: "text-xs font-mono whitespace-pre-wrap", children: prompt })
|
|
762
|
+
] }),
|
|
763
|
+
transcript.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "No transcript available" }) : /* @__PURE__ */ jsx(ConversationView, { items: conversation }),
|
|
764
|
+
isStreaming && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-4 py-2 text-xs text-muted-foreground", children: [
|
|
765
|
+
/* @__PURE__ */ jsx("span", { className: "h-2 w-2 rounded-full bg-blue-400 animate-pulse" }),
|
|
766
|
+
"Streaming..."
|
|
767
|
+
] }),
|
|
768
|
+
/* @__PURE__ */ jsx("div", { ref: sentinelRef })
|
|
769
|
+
]
|
|
770
|
+
}
|
|
771
|
+
) })
|
|
519
772
|
] });
|
|
520
773
|
}
|
|
521
774
|
function ConversationView({ items }) {
|
|
@@ -669,6 +922,7 @@ function ErrorItem({ item }) {
|
|
|
669
922
|
}
|
|
670
923
|
function RunDetailPage({ runId, initialData, initialTranscript }) {
|
|
671
924
|
const { mutate } = useSWRConfig();
|
|
925
|
+
const { toast: toast2 } = useToast();
|
|
672
926
|
const { data: run, error, isLoading } = useApi(
|
|
673
927
|
`run-${runId}`,
|
|
674
928
|
(client) => client.runs.get(runId),
|
|
@@ -679,6 +933,20 @@ function RunDetailPage({ runId, initialData, initialTranscript }) {
|
|
|
679
933
|
(client) => client.runs.transcriptArray(runId),
|
|
680
934
|
initialTranscript ? { fallbackData: initialTranscript } : void 0
|
|
681
935
|
);
|
|
936
|
+
const isActive = run?.status === "running" || run?.status === "pending";
|
|
937
|
+
const { events, isStreaming, terminalEvent, streamingText } = useRunStream(
|
|
938
|
+
runId,
|
|
939
|
+
run?.status ?? ""
|
|
940
|
+
);
|
|
941
|
+
useEffect(() => {
|
|
942
|
+
if (!terminalEvent) return;
|
|
943
|
+
toast2({
|
|
944
|
+
title: terminalEvent.type === "result" ? "Run completed" : "Run failed",
|
|
945
|
+
variant: terminalEvent.type === "result" ? "success" : "destructive"
|
|
946
|
+
});
|
|
947
|
+
mutate(`run-${runId}`);
|
|
948
|
+
mutate(`transcript-${runId}`);
|
|
949
|
+
}, [terminalEvent, toast2, mutate, runId]);
|
|
682
950
|
if (error) {
|
|
683
951
|
return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-[40vh]", children: /* @__PURE__ */ jsxs("p", { className: "text-destructive", children: [
|
|
684
952
|
"Failed to load run: ",
|
|
@@ -693,6 +961,7 @@ function RunDetailPage({ runId, initialData, initialTranscript }) {
|
|
|
693
961
|
}
|
|
694
962
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
695
963
|
(run.status === "running" || run.status === "pending") && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-end", children: /* @__PURE__ */ jsx(CancelRunButton, { runId: run.id, onCancelled: () => mutate(`run-${runId}`) }) }),
|
|
964
|
+
(run.status === "failed" || run.status === "cancelled" || run.status === "timed_out") && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-end", children: /* @__PURE__ */ jsx(RerunButton, { agentId: run.agent_id, prompt: run.prompt }) }),
|
|
696
965
|
run.triggered_by === "a2a" && run.requested_by_key_name && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
|
|
697
966
|
/* @__PURE__ */ jsx(Badge, { variant: "outline", className: "text-[10px]", children: "A2A" }),
|
|
698
967
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
@@ -711,16 +980,26 @@ function RunDetailPage({ runId, initialData, initialTranscript }) {
|
|
|
711
980
|
] }),
|
|
712
981
|
/* @__PURE__ */ jsx(MetricCard, { label: "Cost", children: /* @__PURE__ */ jsxs("span", { className: "font-mono", children: [
|
|
713
982
|
"$",
|
|
714
|
-
|
|
983
|
+
(() => {
|
|
984
|
+
const cost = terminalEvent?.cost_usd ?? run.cost_usd;
|
|
985
|
+
return cost != null ? Number(cost).toFixed(4) : "\u2014";
|
|
986
|
+
})()
|
|
715
987
|
] }) }),
|
|
716
|
-
/* @__PURE__ */ jsx(MetricCard, { label: "Turns", children: run.num_turns }),
|
|
717
|
-
/* @__PURE__ */ jsx(MetricCard, { label: "Duration", children:
|
|
988
|
+
/* @__PURE__ */ jsx(MetricCard, { label: "Turns", children: terminalEvent?.num_turns ?? run.num_turns }),
|
|
989
|
+
/* @__PURE__ */ jsx(MetricCard, { label: "Duration", children: (() => {
|
|
990
|
+
const ms = terminalEvent?.duration_ms ?? run.duration_ms;
|
|
991
|
+
return ms > 0 ? `${(ms / 1e3).toFixed(1)}s` : "\u2014";
|
|
992
|
+
})() }),
|
|
718
993
|
/* @__PURE__ */ jsxs(MetricCard, { label: "Tokens", children: [
|
|
719
|
-
(
|
|
994
|
+
(() => {
|
|
995
|
+
const inTok = terminalEvent?.total_input_tokens ?? run.total_input_tokens;
|
|
996
|
+
const outTok = terminalEvent?.total_output_tokens ?? run.total_output_tokens;
|
|
997
|
+
return (inTok + outTok).toLocaleString();
|
|
998
|
+
})(),
|
|
720
999
|
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground mt-0.5 font-normal", children: [
|
|
721
|
-
run.total_input_tokens.toLocaleString(),
|
|
1000
|
+
(terminalEvent?.total_input_tokens ?? run.total_input_tokens).toLocaleString(),
|
|
722
1001
|
" in / ",
|
|
723
|
-
run.total_output_tokens.toLocaleString(),
|
|
1002
|
+
(terminalEvent?.total_output_tokens ?? run.total_output_tokens).toLocaleString(),
|
|
724
1003
|
" out"
|
|
725
1004
|
] })
|
|
726
1005
|
] })
|
|
@@ -732,7 +1011,18 @@ function RunDetailPage({ runId, initialData, initialTranscript }) {
|
|
|
732
1011
|
run.error_messages.map((msg, i) => /* @__PURE__ */ jsx("pre", { className: "whitespace-pre-wrap text-sm text-destructive font-mono bg-destructive/10 rounded-md p-3", children: msg }, i))
|
|
733
1012
|
] })
|
|
734
1013
|
] }),
|
|
735
|
-
/* @__PURE__ */ jsx(
|
|
1014
|
+
/* @__PURE__ */ jsx(
|
|
1015
|
+
TranscriptViewer,
|
|
1016
|
+
{
|
|
1017
|
+
transcript: isActive && isStreaming ? events : transcript || [],
|
|
1018
|
+
prompt: run.prompt,
|
|
1019
|
+
isStreaming: isActive && isStreaming
|
|
1020
|
+
}
|
|
1021
|
+
),
|
|
1022
|
+
isActive && streamingText && /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-muted/30 p-4", children: /* @__PURE__ */ jsxs("pre", { className: "whitespace-pre-wrap text-sm font-mono", children: [
|
|
1023
|
+
streamingText,
|
|
1024
|
+
/* @__PURE__ */ jsx("span", { className: "inline-block w-2 h-4 ml-0.5 bg-foreground/70 animate-pulse align-text-bottom" })
|
|
1025
|
+
] }) }),
|
|
736
1026
|
/* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs("details", { children: [
|
|
737
1027
|
/* @__PURE__ */ jsxs("summary", { className: "flex items-center justify-between px-6 py-4 cursor-pointer list-none hover:bg-muted/30 transition-colors rounded-xl", children: [
|
|
738
1028
|
/* @__PURE__ */ jsx("span", { className: "text-base font-semibold", children: "Metadata" }),
|
|
@@ -757,6 +1047,37 @@ function RunDetailPage({ runId, initialData, initialTranscript }) {
|
|
|
757
1047
|
] }) })
|
|
758
1048
|
] });
|
|
759
1049
|
}
|
|
1050
|
+
function RerunButton({ agentId, prompt }) {
|
|
1051
|
+
const [rerunning, setRerunning] = useState(false);
|
|
1052
|
+
const client = useAgentPlaneClient();
|
|
1053
|
+
const { onNavigate, basePath } = useNavigation();
|
|
1054
|
+
const { toast: toast2 } = useToast();
|
|
1055
|
+
async function handleRerun() {
|
|
1056
|
+
setRerunning(true);
|
|
1057
|
+
try {
|
|
1058
|
+
const stream = await client.runs.create({ agent_id: agentId, prompt });
|
|
1059
|
+
let newRunId = null;
|
|
1060
|
+
for await (const event of stream) {
|
|
1061
|
+
if (event.type === "run_started") {
|
|
1062
|
+
newRunId = event.run_id;
|
|
1063
|
+
break;
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
if (newRunId) {
|
|
1067
|
+
onNavigate(`${basePath}/runs/${newRunId}`);
|
|
1068
|
+
}
|
|
1069
|
+
} catch (err) {
|
|
1070
|
+
toast2({
|
|
1071
|
+
title: "Failed to rerun",
|
|
1072
|
+
description: err instanceof Error ? err.message : "Unknown error",
|
|
1073
|
+
variant: "destructive"
|
|
1074
|
+
});
|
|
1075
|
+
} finally {
|
|
1076
|
+
setRerunning(false);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
return /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: handleRerun, disabled: rerunning, children: rerunning ? "Rerunning\u2026" : "\u21BB Rerun" });
|
|
1080
|
+
}
|
|
760
1081
|
function CancelRunButton({ runId, onCancelled }) {
|
|
761
1082
|
const [open, setOpen] = useState(false);
|
|
762
1083
|
const [cancelling, setCancelling] = useState(false);
|
|
@@ -4693,7 +5014,7 @@ function renderEvent(event, idx) {
|
|
|
4693
5014
|
/* @__PURE__ */ jsx(CollapsibleJson, { data: event, maxHeight: "8rem" })
|
|
4694
5015
|
] }, idx);
|
|
4695
5016
|
}
|
|
4696
|
-
var
|
|
5017
|
+
var TERMINAL_STATUSES2 = /* @__PURE__ */ new Set(["completed", "failed", "cancelled", "timed_out"]);
|
|
4697
5018
|
function PlaygroundPage({ agentId }) {
|
|
4698
5019
|
const client = useAgentPlaneClient();
|
|
4699
5020
|
const { LinkComponent, basePath } = useNavigation();
|
|
@@ -4735,7 +5056,7 @@ function PlaygroundPage({ agentId }) {
|
|
|
4735
5056
|
if (abortRef.current?.signal.aborted) break;
|
|
4736
5057
|
try {
|
|
4737
5058
|
const run = await client.runs.get(runId);
|
|
4738
|
-
if (
|
|
5059
|
+
if (TERMINAL_STATUSES2.has(run.status)) {
|
|
4739
5060
|
try {
|
|
4740
5061
|
const transcriptEvents = await client.runs.transcriptArray(runId);
|
|
4741
5062
|
if (transcriptEvents.length > 0) {
|
|
@@ -4971,5 +5292,121 @@ function PlaygroundPage({ agentId }) {
|
|
|
4971
5292
|
] })
|
|
4972
5293
|
] });
|
|
4973
5294
|
}
|
|
5295
|
+
var ToastProvider = ToastPrimitives.Provider;
|
|
5296
|
+
var ToastViewport = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
5297
|
+
ToastPrimitives.Viewport,
|
|
5298
|
+
{
|
|
5299
|
+
ref,
|
|
5300
|
+
className: cn(
|
|
5301
|
+
"fixed top-0 right-0 z-[100] flex max-h-screen w-full flex-col-reverse gap-2 p-4 sm:flex-col sm:max-w-[420px]",
|
|
5302
|
+
className
|
|
5303
|
+
),
|
|
5304
|
+
...props
|
|
5305
|
+
}
|
|
5306
|
+
));
|
|
5307
|
+
ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
|
|
5308
|
+
var toastVariants = cva(
|
|
5309
|
+
"group pointer-events-auto relative flex w-full items-center justify-between gap-4 overflow-hidden rounded-lg border p-4 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-right-full",
|
|
5310
|
+
{
|
|
5311
|
+
variants: {
|
|
5312
|
+
variant: {
|
|
5313
|
+
default: "bg-zinc-800 border-zinc-700 text-zinc-100",
|
|
5314
|
+
success: "bg-green-950 border-green-900 text-green-400",
|
|
5315
|
+
destructive: "bg-red-950 border-red-900 text-red-400"
|
|
5316
|
+
}
|
|
5317
|
+
},
|
|
5318
|
+
defaultVariants: {
|
|
5319
|
+
variant: "default"
|
|
5320
|
+
}
|
|
5321
|
+
}
|
|
5322
|
+
);
|
|
5323
|
+
var Toast = React4.forwardRef(({ className, variant, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
5324
|
+
ToastPrimitives.Root,
|
|
5325
|
+
{
|
|
5326
|
+
ref,
|
|
5327
|
+
className: cn(toastVariants({ variant }), className),
|
|
5328
|
+
...props
|
|
5329
|
+
}
|
|
5330
|
+
));
|
|
5331
|
+
Toast.displayName = ToastPrimitives.Root.displayName;
|
|
5332
|
+
var ToastAction = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
5333
|
+
ToastPrimitives.Action,
|
|
5334
|
+
{
|
|
5335
|
+
ref,
|
|
5336
|
+
className: cn(
|
|
5337
|
+
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border border-zinc-600 bg-transparent px-3 text-sm font-medium transition-colors hover:bg-zinc-700 focus:outline-none focus:ring-1 focus:ring-zinc-500 disabled:pointer-events-none disabled:opacity-50",
|
|
5338
|
+
"group-[.destructive]:border-red-800 group-[.destructive]:hover:border-red-700 group-[.destructive]:hover:bg-red-900 group-[.destructive]:focus:ring-red-800",
|
|
5339
|
+
"group-[.success]:border-green-800 group-[.success]:hover:border-green-700 group-[.success]:hover:bg-green-900 group-[.success]:focus:ring-green-800",
|
|
5340
|
+
className
|
|
5341
|
+
),
|
|
5342
|
+
...props
|
|
5343
|
+
}
|
|
5344
|
+
));
|
|
5345
|
+
ToastAction.displayName = ToastPrimitives.Action.displayName;
|
|
5346
|
+
var ToastClose = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
5347
|
+
ToastPrimitives.Close,
|
|
5348
|
+
{
|
|
5349
|
+
ref,
|
|
5350
|
+
className: cn(
|
|
5351
|
+
"absolute right-1 top-1 rounded-md p-1 text-zinc-400 opacity-0 transition-opacity hover:text-zinc-100 focus:opacity-100 focus:outline-none group-hover:opacity-100",
|
|
5352
|
+
"group-[.destructive]:text-red-400 group-[.destructive]:hover:text-red-300",
|
|
5353
|
+
"group-[.success]:text-green-400 group-[.success]:hover:text-green-300",
|
|
5354
|
+
className
|
|
5355
|
+
),
|
|
5356
|
+
"toast-close": "",
|
|
5357
|
+
...props,
|
|
5358
|
+
children: /* @__PURE__ */ jsxs(
|
|
5359
|
+
"svg",
|
|
5360
|
+
{
|
|
5361
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
5362
|
+
width: "16",
|
|
5363
|
+
height: "16",
|
|
5364
|
+
viewBox: "0 0 24 24",
|
|
5365
|
+
fill: "none",
|
|
5366
|
+
stroke: "currentColor",
|
|
5367
|
+
strokeWidth: "2",
|
|
5368
|
+
strokeLinecap: "round",
|
|
5369
|
+
strokeLinejoin: "round",
|
|
5370
|
+
children: [
|
|
5371
|
+
/* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }),
|
|
5372
|
+
/* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })
|
|
5373
|
+
]
|
|
5374
|
+
}
|
|
5375
|
+
)
|
|
5376
|
+
}
|
|
5377
|
+
));
|
|
5378
|
+
ToastClose.displayName = ToastPrimitives.Close.displayName;
|
|
5379
|
+
var ToastTitle = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
5380
|
+
ToastPrimitives.Title,
|
|
5381
|
+
{
|
|
5382
|
+
ref,
|
|
5383
|
+
className: cn("text-sm font-semibold [&+div]:text-xs", className),
|
|
5384
|
+
...props
|
|
5385
|
+
}
|
|
5386
|
+
));
|
|
5387
|
+
ToastTitle.displayName = ToastPrimitives.Title.displayName;
|
|
5388
|
+
var ToastDescription = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
5389
|
+
ToastPrimitives.Description,
|
|
5390
|
+
{
|
|
5391
|
+
ref,
|
|
5392
|
+
className: cn("text-sm opacity-90", className),
|
|
5393
|
+
...props
|
|
5394
|
+
}
|
|
5395
|
+
));
|
|
5396
|
+
ToastDescription.displayName = ToastPrimitives.Description.displayName;
|
|
5397
|
+
function Toaster() {
|
|
5398
|
+
const { toasts } = useToast();
|
|
5399
|
+
return /* @__PURE__ */ jsxs(ToastProvider, { children: [
|
|
5400
|
+
toasts.map(({ id, title, description, action, ...props }) => /* @__PURE__ */ jsxs(Toast, { ...props, children: [
|
|
5401
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-1", children: [
|
|
5402
|
+
title && /* @__PURE__ */ jsx(ToastTitle, { children: title }),
|
|
5403
|
+
description && /* @__PURE__ */ jsx(ToastDescription, { children: description })
|
|
5404
|
+
] }),
|
|
5405
|
+
action,
|
|
5406
|
+
/* @__PURE__ */ jsx(ToastClose, {})
|
|
5407
|
+
] }, id)),
|
|
5408
|
+
/* @__PURE__ */ jsx(ToastViewport, {})
|
|
5409
|
+
] });
|
|
5410
|
+
}
|
|
4974
5411
|
|
|
4975
|
-
export { AdminTable, AdminTableHead, AdminTableRow, AgentA2aInfo, AgentConnectorsManager, AgentDetailPage, AgentEditForm, AgentListPage, AgentPluginManager, AgentRuns, AgentScheduleForm, AgentSkillManager, ConfirmDialog, CopyButton, DashboardPage, DetailPageHeader, EmptyRow, FormError, LocalDate, McpServerListPage, MetricCard, ModelSelector, PaginationBar, PlaygroundPage, PluginDetailPage, PluginMarketplaceDetailPage, PluginMarketplaceListPage, RunDetailPage, RunListPage, RunSourceBadge, RunStatusBadge, SectionHeader, Select, SettingsPage, Tabs, Textarea, Th, ToolkitMultiselect, TranscriptViewer, parsePaginationParams };
|
|
5412
|
+
export { AdminTable, AdminTableHead, AdminTableRow, AgentA2aInfo, AgentConnectorsManager, AgentDetailPage, AgentEditForm, AgentListPage, AgentPluginManager, AgentRuns, AgentScheduleForm, AgentSkillManager, ConfirmDialog, CopyButton, DashboardPage, DetailPageHeader, EmptyRow, FormError, LocalDate, McpServerListPage, MetricCard, ModelSelector, PaginationBar, PlaygroundPage, PluginDetailPage, PluginMarketplaceDetailPage, PluginMarketplaceListPage, RunDetailPage, RunListPage, RunSourceBadge, RunStatusBadge, SectionHeader, Select, SettingsPage, Tabs, Textarea, Th, Toast, ToastAction, ToastClose, ToastDescription, ToastProvider, ToastTitle, ToastViewport, Toaster, ToolkitMultiselect, TranscriptViewer, parsePaginationParams, toast, toastVariants, useRunStream, useToast };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getcatalystiq/agent-plane-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.32",
|
|
4
4
|
"description": "Embeddable React component library for AgentPlane",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -92,6 +92,7 @@
|
|
|
92
92
|
},
|
|
93
93
|
"dependencies": {
|
|
94
94
|
"@radix-ui/react-popover": "^1.0.0",
|
|
95
|
+
"@radix-ui/react-toast": "^1.2.15",
|
|
95
96
|
"class-variance-authority": "^0.7.0",
|
|
96
97
|
"clsx": "^2.0.0",
|
|
97
98
|
"cmdk": "^1.0.0",
|