@getcatalystiq/agent-plane-ui 0.1.0

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.js ADDED
@@ -0,0 +1,3848 @@
1
+ import * as React3 from 'react';
2
+ import React3__default, { createContext, useRef, useMemo, useContext, useState, useCallback, useEffect } from 'react';
3
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
+ import useSWR, { useSWRConfig } from 'swr';
5
+ import { clsx } from 'clsx';
6
+ import { twMerge } from 'tailwind-merge';
7
+ import { cva } from 'class-variance-authority';
8
+ import ReactMarkdown from 'react-markdown';
9
+ import { Command } from 'cmdk';
10
+ import * as Popover from '@radix-ui/react-popover';
11
+
12
+ // src/provider.tsx
13
+ var ClientContext = createContext(null);
14
+ var NavigationContext = createContext(
15
+ null
16
+ );
17
+ function DefaultLink({ href, children, className }) {
18
+ return /* @__PURE__ */ jsx("a", { href, className, children });
19
+ }
20
+ function AgentPlaneProvider({
21
+ client,
22
+ onNavigate,
23
+ LinkComponent = DefaultLink,
24
+ onAuthError,
25
+ basePath = "",
26
+ children
27
+ }) {
28
+ const clientRef = useRef(client);
29
+ clientRef.current = client;
30
+ const onAuthErrorRef = useRef(onAuthError);
31
+ onAuthErrorRef.current = onAuthError;
32
+ const clientValue = useMemo(
33
+ () => ({
34
+ // Expose a stable object whose `.client` always points to the latest ref.
35
+ get client() {
36
+ return clientRef.current;
37
+ },
38
+ get onAuthError() {
39
+ return onAuthErrorRef.current;
40
+ }
41
+ }),
42
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- intentionally stable
43
+ []
44
+ );
45
+ const navigationValue = useMemo(
46
+ () => ({
47
+ onNavigate,
48
+ LinkComponent,
49
+ basePath
50
+ }),
51
+ [onNavigate, LinkComponent, basePath]
52
+ );
53
+ return /* @__PURE__ */ jsx(ClientContext.Provider, { value: clientValue, children: /* @__PURE__ */ jsx(NavigationContext.Provider, { value: navigationValue, children }) });
54
+ }
55
+ function useAgentPlaneClient() {
56
+ const ctx = useContext(ClientContext);
57
+ if (!ctx) {
58
+ throw new Error(
59
+ "useAgentPlaneClient must be used within an <AgentPlaneProvider>"
60
+ );
61
+ }
62
+ return ctx.client;
63
+ }
64
+ function useAuthError() {
65
+ const ctx = useContext(ClientContext);
66
+ if (!ctx) {
67
+ throw new Error(
68
+ "useAuthError must be used within an <AgentPlaneProvider>"
69
+ );
70
+ }
71
+ return ctx.onAuthError;
72
+ }
73
+ function useNavigation() {
74
+ const ctx = useContext(NavigationContext);
75
+ if (!ctx) {
76
+ throw new Error(
77
+ "useNavigation must be used within an <AgentPlaneProvider>"
78
+ );
79
+ }
80
+ return ctx;
81
+ }
82
+ function useApi(key, fetcher, options) {
83
+ const client = useAgentPlaneClient();
84
+ const onAuthError = useAuthError();
85
+ return useSWR(
86
+ key,
87
+ () => fetcher(client),
88
+ {
89
+ revalidateOnFocus: false,
90
+ onError: (err) => {
91
+ if (onAuthError && err && typeof err === "object" && "status" in err && err.status === 401) {
92
+ onAuthError(err instanceof Error ? err : new Error(String(err)));
93
+ }
94
+ },
95
+ ...options
96
+ }
97
+ );
98
+ }
99
+ function cn(...inputs) {
100
+ return twMerge(clsx(inputs));
101
+ }
102
+ function supportsClaudeRunner(model) {
103
+ return !model.includes("/") || model.startsWith("anthropic/");
104
+ }
105
+ var buttonVariants = cva(
106
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
107
+ {
108
+ variants: {
109
+ variant: {
110
+ default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
111
+ destructive: "bg-destructive text-white shadow-sm hover:bg-destructive/90",
112
+ outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
113
+ secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
114
+ ghost: "hover:bg-accent hover:text-accent-foreground",
115
+ link: "text-primary underline-offset-4 hover:underline"
116
+ },
117
+ size: {
118
+ default: "h-9 px-4 py-2",
119
+ sm: "h-8 rounded-md px-3 text-xs",
120
+ lg: "h-10 rounded-md px-8",
121
+ icon: "h-9 w-9"
122
+ }
123
+ },
124
+ defaultVariants: {
125
+ variant: "default",
126
+ size: "default"
127
+ }
128
+ }
129
+ );
130
+ var Button = React3.forwardRef(
131
+ ({ className, variant, size, ...props }, ref) => {
132
+ return /* @__PURE__ */ jsx(
133
+ "button",
134
+ {
135
+ className: cn(buttonVariants({ variant, size, className })),
136
+ ref,
137
+ ...props
138
+ }
139
+ );
140
+ }
141
+ );
142
+ Button.displayName = "Button";
143
+ var Card = React3.forwardRef(
144
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("rounded-xl border bg-card text-card-foreground shadow", className), ...props })
145
+ );
146
+ Card.displayName = "Card";
147
+ var CardHeader = React3.forwardRef(
148
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("flex flex-col space-y-1.5 p-6", className), ...props })
149
+ );
150
+ CardHeader.displayName = "CardHeader";
151
+ var CardTitle = React3.forwardRef(
152
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("font-semibold leading-none tracking-tight", className), ...props })
153
+ );
154
+ CardTitle.displayName = "CardTitle";
155
+ var CardDescription = React3.forwardRef(
156
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("text-sm text-muted-foreground", className), ...props })
157
+ );
158
+ CardDescription.displayName = "CardDescription";
159
+ var CardContent = React3.forwardRef(
160
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-6 pt-0", className), ...props })
161
+ );
162
+ CardContent.displayName = "CardContent";
163
+ var badgeVariants = cva(
164
+ "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
165
+ {
166
+ variants: {
167
+ variant: {
168
+ default: "border-transparent bg-primary text-primary-foreground shadow",
169
+ secondary: "border-transparent bg-secondary text-secondary-foreground",
170
+ destructive: "border-transparent bg-destructive text-white shadow",
171
+ outline: "text-foreground"
172
+ }
173
+ },
174
+ defaultVariants: {
175
+ variant: "default"
176
+ }
177
+ }
178
+ );
179
+ function Badge({ className, variant, ...props }) {
180
+ return /* @__PURE__ */ jsx("div", { className: cn(badgeVariants({ variant }), className), ...props });
181
+ }
182
+ var Input = React3.forwardRef(
183
+ ({ className, type, ...props }, ref) => {
184
+ return /* @__PURE__ */ jsx(
185
+ "input",
186
+ {
187
+ type,
188
+ className: cn(
189
+ "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
190
+ className
191
+ ),
192
+ ref,
193
+ ...props
194
+ }
195
+ );
196
+ }
197
+ );
198
+ Input.displayName = "Input";
199
+ function Select({ className = "", ...props }) {
200
+ return /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
201
+ /* @__PURE__ */ jsx(
202
+ "select",
203
+ {
204
+ className: `flex h-9 w-full appearance-none rounded-md border border-input bg-transparent pl-3 pr-8 py-1 text-sm shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 ${className}`,
205
+ ...props
206
+ }
207
+ ),
208
+ /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-y-0 right-2.5 flex items-center", children: /* @__PURE__ */ jsxs("svg", { className: "h-3.5 w-3.5 opacity-50", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
209
+ /* @__PURE__ */ jsx("path", { d: "m7 15 5 5 5-5" }),
210
+ /* @__PURE__ */ jsx("path", { d: "m7 9 5-5 5 5" })
211
+ ] }) })
212
+ ] });
213
+ }
214
+ var Textarea = React3.forwardRef(
215
+ ({ className, ...props }, ref) => {
216
+ return /* @__PURE__ */ jsx(
217
+ "textarea",
218
+ {
219
+ className: cn(
220
+ "flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
221
+ className
222
+ ),
223
+ ref,
224
+ ...props
225
+ }
226
+ );
227
+ }
228
+ );
229
+ Textarea.displayName = "Textarea";
230
+ function FormField({ label, children, error, hint }) {
231
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
232
+ /* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: label }),
233
+ children,
234
+ hint && !error && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-1", children: hint }),
235
+ error && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive mt-1", children: error })
236
+ ] });
237
+ }
238
+ function FormError({ error }) {
239
+ if (!error) return null;
240
+ return /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: error });
241
+ }
242
+ function SectionHeader({ title, children, className }) {
243
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between mb-3", className), children: [
244
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: title }),
245
+ children
246
+ ] });
247
+ }
248
+ function DetailPageHeader({ backHref, backLabel, title, actions, badge, subtitle, LinkComponent }) {
249
+ const BackLink = LinkComponent ?? "a";
250
+ return /* @__PURE__ */ jsxs("div", { children: [
251
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
252
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
253
+ /* @__PURE__ */ jsxs(BackLink, { href: backHref, className: "text-muted-foreground hover:text-foreground text-sm", children: [
254
+ "\u2190 ",
255
+ backLabel
256
+ ] }),
257
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "/" }),
258
+ /* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold", children: title }),
259
+ badge
260
+ ] }),
261
+ actions
262
+ ] }),
263
+ subtitle && /* @__PURE__ */ jsx("div", { className: "mt-1", children: subtitle })
264
+ ] });
265
+ }
266
+ function Skeleton({ className, ...props }) {
267
+ return /* @__PURE__ */ jsx("div", { className: cn("animate-pulse rounded-md bg-muted/50", className), ...props });
268
+ }
269
+ function MetricCard({ label, children, className }) {
270
+ return /* @__PURE__ */ jsxs(Card, { className, children: [
271
+ /* @__PURE__ */ jsx(CardHeader, { className: "pb-2", children: /* @__PURE__ */ jsx(CardTitle, { className: "text-sm font-medium text-muted-foreground", children: label }) }),
272
+ /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx("div", { className: "text-2xl font-bold", children }) })
273
+ ] });
274
+ }
275
+ function AdminTable({ children, footer, className }) {
276
+ return /* @__PURE__ */ jsxs("div", { className: cn("rounded-lg border border-border", className), children: [
277
+ /* @__PURE__ */ jsx("table", { className: "w-full text-sm", children }),
278
+ footer
279
+ ] });
280
+ }
281
+ function AdminTableHead({ children }) {
282
+ return /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { className: "border-b border-border bg-muted/50", children }) });
283
+ }
284
+ function Th({ children, className, align = "left" }) {
285
+ return /* @__PURE__ */ jsx("th", { className: cn("p-3 font-medium", align === "right" ? "text-right" : "text-left", className), children });
286
+ }
287
+ function AdminTableRow({ children, className }) {
288
+ return /* @__PURE__ */ jsx("tr", { className: cn("border-b border-border hover:bg-muted/30 transition-colors", className), children });
289
+ }
290
+ function EmptyRow({ colSpan, children = "No results found" }) {
291
+ return /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan, className: "p-8 text-center text-muted-foreground", children }) });
292
+ }
293
+ var PAGE_SIZE_OPTIONS = [10, 20, 50];
294
+ function PaginationBtn({
295
+ href,
296
+ children,
297
+ onClick,
298
+ LinkComponent
299
+ }) {
300
+ const cls = "inline-flex items-center justify-center h-7 w-7 rounded border border-border text-xs font-medium transition-colors";
301
+ if (!href) return /* @__PURE__ */ jsx("span", { className: `${cls} text-muted-foreground opacity-40 cursor-not-allowed`, children });
302
+ if (LinkComponent) {
303
+ return /* @__PURE__ */ jsx(LinkComponent, { href, className: `${cls} hover:bg-muted`, children });
304
+ }
305
+ if (onClick) {
306
+ return /* @__PURE__ */ jsx("button", { className: `${cls} hover:bg-muted`, onClick: () => onClick(href), children });
307
+ }
308
+ return /* @__PURE__ */ jsx("a", { href, className: `${cls} hover:bg-muted`, children });
309
+ }
310
+ function PaginationBar({
311
+ page,
312
+ pageSize,
313
+ total,
314
+ buildHref,
315
+ onNavigate,
316
+ LinkComponent
317
+ }) {
318
+ const totalPages = Math.max(1, Math.ceil(total / pageSize));
319
+ function renderLink(href, children, extraClass) {
320
+ if (LinkComponent) {
321
+ return /* @__PURE__ */ jsx(LinkComponent, { href, className: extraClass, children });
322
+ }
323
+ if (onNavigate) {
324
+ return /* @__PURE__ */ jsx("button", { className: extraClass, onClick: () => onNavigate(href), children });
325
+ }
326
+ return /* @__PURE__ */ jsx("a", { href, className: extraClass, children });
327
+ }
328
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-3 py-2 border-t border-border bg-muted/20 text-sm", children: [
329
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-muted-foreground", children: [
330
+ /* @__PURE__ */ jsx("span", { children: "Rows per page:" }),
331
+ PAGE_SIZE_OPTIONS.map((ps) => /* @__PURE__ */ jsx(React3__default.Fragment, { children: renderLink(
332
+ buildHref(1, ps),
333
+ ps,
334
+ `px-2 py-0.5 rounded text-xs ${pageSize === ps ? "bg-primary text-primary-foreground font-medium" : "hover:bg-muted"}`
335
+ ) }, ps)),
336
+ /* @__PURE__ */ jsxs("span", { className: "ml-2", children: [
337
+ total,
338
+ " total"
339
+ ] })
340
+ ] }),
341
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
342
+ /* @__PURE__ */ jsx(PaginationBtn, { href: page > 1 ? buildHref(1, pageSize) : null, onClick: onNavigate, LinkComponent, children: "\xAB" }),
343
+ /* @__PURE__ */ jsx(PaginationBtn, { href: page > 1 ? buildHref(page - 1, pageSize) : null, onClick: onNavigate, LinkComponent, children: "\u2039" }),
344
+ /* @__PURE__ */ jsxs("span", { className: "px-3 text-xs text-muted-foreground", children: [
345
+ "Page ",
346
+ page,
347
+ " of ",
348
+ totalPages
349
+ ] }),
350
+ /* @__PURE__ */ jsx(PaginationBtn, { href: page < totalPages ? buildHref(page + 1, pageSize) : null, onClick: onNavigate, LinkComponent, children: "\u203A" }),
351
+ /* @__PURE__ */ jsx(PaginationBtn, { href: page < totalPages ? buildHref(totalPages, pageSize) : null, onClick: onNavigate, LinkComponent, children: "\xBB" })
352
+ ] })
353
+ ] });
354
+ }
355
+ function parsePaginationParams(pageParam, pageSizeParam, defaultPageSize = 20) {
356
+ const pageSize = PAGE_SIZE_OPTIONS.includes(Number(pageSizeParam)) ? Number(pageSizeParam) : defaultPageSize;
357
+ const page = Math.max(1, parseInt(pageParam ?? "1", 10) || 1);
358
+ const offset = (page - 1) * pageSize;
359
+ return { page, pageSize, offset };
360
+ }
361
+ function Tabs({ tabs, defaultTab = 0 }) {
362
+ const [active, setActive] = useState(defaultTab);
363
+ return /* @__PURE__ */ jsxs("div", { children: [
364
+ /* @__PURE__ */ jsx("div", { className: "flex gap-4", children: tabs.map((tab, i) => /* @__PURE__ */ jsxs(
365
+ "button",
366
+ {
367
+ onClick: () => setActive(i),
368
+ className: `relative pb-2 text-sm font-medium transition-colors ${active === i ? "text-foreground" : "text-muted-foreground hover:text-foreground"}`,
369
+ children: [
370
+ tab.label,
371
+ active === i && /* @__PURE__ */ jsx("span", { className: "absolute inset-x-0 bottom-0 h-0.5 bg-foreground rounded-full" })
372
+ ]
373
+ },
374
+ tab.label
375
+ )) }),
376
+ /* @__PURE__ */ jsx("div", { className: "pt-6", children: tabs[active]?.content })
377
+ ] });
378
+ }
379
+ function Dialog({ open, onOpenChange, children }) {
380
+ React3.useEffect(() => {
381
+ if (!open) return;
382
+ function onKeyDown(e) {
383
+ if (e.key === "Escape") onOpenChange(false);
384
+ }
385
+ document.addEventListener("keydown", onKeyDown);
386
+ return () => document.removeEventListener("keydown", onKeyDown);
387
+ }, [open, onOpenChange]);
388
+ React3.useEffect(() => {
389
+ if (!open) return;
390
+ const prev = document.body.style.overflow;
391
+ document.body.style.overflow = "hidden";
392
+ return () => {
393
+ document.body.style.overflow = prev;
394
+ };
395
+ }, [open]);
396
+ if (!open) return null;
397
+ return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
398
+ /* @__PURE__ */ jsx(
399
+ "div",
400
+ {
401
+ className: "absolute inset-0 bg-black/60 animate-dialog-overlay",
402
+ onClick: () => onOpenChange(false)
403
+ }
404
+ ),
405
+ children
406
+ ] });
407
+ }
408
+ function DialogContent({ className, children, ...props }) {
409
+ return /* @__PURE__ */ jsx(
410
+ "div",
411
+ {
412
+ className: cn(
413
+ "relative z-10 w-full bg-background border border-border rounded-xl shadow-2xl mx-4 max-h-[85vh] overflow-y-auto animate-dialog-content",
414
+ className
415
+ ),
416
+ onClick: (e) => e.stopPropagation(),
417
+ ...props,
418
+ children
419
+ }
420
+ );
421
+ }
422
+ function DialogHeader({ className, children, ...props }) {
423
+ return /* @__PURE__ */ jsx("div", { className: cn("px-6 pt-6 pb-0", className), ...props, children });
424
+ }
425
+ function DialogBody({ className, children, ...props }) {
426
+ return /* @__PURE__ */ jsx("div", { className: cn("px-6 py-4", className), ...props, children });
427
+ }
428
+ function DialogFooter({ className, children, ...props }) {
429
+ return /* @__PURE__ */ jsx(
430
+ "div",
431
+ {
432
+ className: cn("flex items-center justify-end gap-2 px-6 py-4 border-t border-border bg-muted/30 rounded-b-xl", className),
433
+ ...props,
434
+ children
435
+ }
436
+ );
437
+ }
438
+ function DialogTitle({ className, children, ...props }) {
439
+ return /* @__PURE__ */ jsx("h2", { className: cn("text-base font-semibold", className), ...props, children });
440
+ }
441
+ function DialogDescription({ className, children, ...props }) {
442
+ return /* @__PURE__ */ jsx("p", { className: cn("text-sm text-muted-foreground mt-1", className), ...props, children });
443
+ }
444
+ function ConfirmDialog({
445
+ open,
446
+ onOpenChange,
447
+ title,
448
+ children,
449
+ confirmLabel = "Delete",
450
+ loadingLabel = "Deleting...",
451
+ loading = false,
452
+ error,
453
+ onConfirm,
454
+ variant = "destructive"
455
+ }) {
456
+ return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-w-sm", children: [
457
+ /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: title }) }),
458
+ /* @__PURE__ */ jsxs(DialogBody, { children: [
459
+ /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children }),
460
+ error && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive mt-3", children: error })
461
+ ] }),
462
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
463
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "outline", onClick: () => onOpenChange(false), disabled: loading, children: "Cancel" }),
464
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant, onClick: onConfirm, disabled: loading, children: loading ? loadingLabel : confirmLabel })
465
+ ] })
466
+ ] }) });
467
+ }
468
+ function CopyButton({ text, className = "" }) {
469
+ const [copied, setCopied] = useState(false);
470
+ async function handleCopy() {
471
+ await navigator.clipboard.writeText(text);
472
+ setCopied(true);
473
+ setTimeout(() => setCopied(false), 2e3);
474
+ }
475
+ return /* @__PURE__ */ jsx(
476
+ "button",
477
+ {
478
+ onClick: handleCopy,
479
+ className: `inline-flex items-center gap-1 rounded px-2 py-1 text-xs text-muted-foreground hover:text-foreground hover:bg-muted transition-colors ${className}`,
480
+ title: "Copy to clipboard",
481
+ children: copied ? /* @__PURE__ */ jsxs(Fragment, { children: [
482
+ /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("polyline", { points: "20 6 9 17 4 12" }) }),
483
+ "Copied"
484
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
485
+ /* @__PURE__ */ jsxs("svg", { className: "h-3.5 w-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
486
+ /* @__PURE__ */ jsx("rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2" }),
487
+ /* @__PURE__ */ jsx("path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" })
488
+ ] }),
489
+ "Copy"
490
+ ] })
491
+ }
492
+ );
493
+ }
494
+ function RunStatusBadge({ status }) {
495
+ const variant = status === "completed" ? "default" : status === "running" ? "secondary" : status === "failed" || status === "timed_out" ? "destructive" : "outline";
496
+ return /* @__PURE__ */ jsx(Badge, { variant, children: status.replace("_", " ") });
497
+ }
498
+ var STYLES = {
499
+ schedule: "bg-blue-500/10 text-blue-400",
500
+ playground: "bg-purple-500/10 text-purple-400",
501
+ api: "bg-zinc-500/10 text-zinc-400",
502
+ chat: "bg-green-500/10 text-green-400",
503
+ a2a: "bg-indigo-500/10 text-indigo-400"
504
+ };
505
+ var LABELS = {
506
+ api: "API",
507
+ schedule: "Schedule",
508
+ playground: "Playground",
509
+ chat: "Chat",
510
+ a2a: "A2A"
511
+ };
512
+ function RunSourceBadge({ triggeredBy }) {
513
+ const key = triggeredBy;
514
+ return /* @__PURE__ */ jsx("span", { className: `inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${STYLES[key] ?? STYLES.api}`, children: LABELS[key] ?? triggeredBy });
515
+ }
516
+ function LocalDate({ value, fallback = "\u2014" }) {
517
+ if (!value) return /* @__PURE__ */ jsx(Fragment, { children: fallback });
518
+ return /* @__PURE__ */ jsx(Fragment, { children: new Date(value).toLocaleString() });
519
+ }
520
+ function DashboardPage({ initialData, chartComponent: ChartComponent }) {
521
+ const { LinkComponent, basePath } = useNavigation();
522
+ const { data, error, isLoading } = useApi(
523
+ "dashboard",
524
+ async (client) => {
525
+ const [stats2, daily_stats2] = await Promise.all([
526
+ client.dashboard.stats(),
527
+ client.dashboard.charts()
528
+ ]);
529
+ return { stats: stats2, daily_stats: daily_stats2 };
530
+ },
531
+ initialData ? { fallbackData: initialData } : void 0
532
+ );
533
+ if (error) {
534
+ return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-[40vh]", children: /* @__PURE__ */ jsxs("p", { className: "text-destructive", children: [
535
+ "Failed to load dashboard: ",
536
+ error.message
537
+ ] }) });
538
+ }
539
+ if (isLoading || !data) {
540
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
541
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-4 gap-4", children: Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-24 rounded-lg" }, i)) }),
542
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
543
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-64 rounded-lg" }),
544
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-64 rounded-lg" })
545
+ ] })
546
+ ] });
547
+ }
548
+ const { stats, daily_stats } = data;
549
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
550
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-4 gap-4", children: [
551
+ /* @__PURE__ */ jsx(MetricCard, { label: "Agents", children: stats.agent_count }),
552
+ /* @__PURE__ */ jsx(LinkComponent, { href: `${basePath}/runs`, className: "block", children: /* @__PURE__ */ jsx(MetricCard, { label: "Total Runs", className: "hover:bg-muted/30 transition-colors cursor-pointer h-full", children: stats.total_runs }) }),
553
+ /* @__PURE__ */ jsx(MetricCard, { label: "Active Runs", children: /* @__PURE__ */ jsx("span", { className: "text-green-500", children: stats.active_runs }) }),
554
+ /* @__PURE__ */ jsx(MetricCard, { label: "Total Spend", children: /* @__PURE__ */ jsxs("span", { className: "font-mono", children: [
555
+ "$",
556
+ stats.total_spend.toFixed(2)
557
+ ] }) })
558
+ ] }),
559
+ ChartComponent && /* @__PURE__ */ jsx(ChartComponent, { stats: daily_stats })
560
+ ] });
561
+ }
562
+ var SOURCES = [
563
+ { value: "", label: "All Sources" },
564
+ { value: "api", label: "API" },
565
+ { value: "schedule", label: "Schedule" },
566
+ { value: "playground", label: "Playground" },
567
+ { value: "chat", label: "Chat" },
568
+ { value: "a2a", label: "A2A" }
569
+ ];
570
+ var VALID_SOURCES = SOURCES.filter((s) => s.value).map((s) => s.value);
571
+ function RunListPage({ initialData }) {
572
+ const { LinkComponent, basePath } = useNavigation();
573
+ const [page, setPage] = useState(1);
574
+ const [pageSize, setPageSize] = useState(20);
575
+ const [sourceFilter, setSourceFilter] = useState(null);
576
+ const cacheKey = `runs-${page}-${pageSize}-${sourceFilter || "all"}`;
577
+ const { data, error, isLoading } = useApi(
578
+ cacheKey,
579
+ (client) => {
580
+ return client.runs.list({
581
+ limit: pageSize,
582
+ offset: (page - 1) * pageSize,
583
+ ...sourceFilter ? { triggered_by: sourceFilter } : {}
584
+ });
585
+ },
586
+ initialData ? { fallbackData: initialData } : void 0
587
+ );
588
+ const handleSourceChange = useCallback((e) => {
589
+ const value = e.target.value;
590
+ setSourceFilter(VALID_SOURCES.includes(value) ? value : null);
591
+ setPage(1);
592
+ }, []);
593
+ const handlePaginationNavigate = useCallback((href) => {
594
+ const url = new URL(href, "http://localhost");
595
+ const p = parseInt(url.searchParams.get("page") || "1", 10);
596
+ const ps = parseInt(url.searchParams.get("pageSize") || "20", 10);
597
+ setPage(p);
598
+ setPageSize(ps);
599
+ }, []);
600
+ if (error) {
601
+ return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-[40vh]", children: /* @__PURE__ */ jsxs("p", { className: "text-destructive", children: [
602
+ "Failed to load runs: ",
603
+ error.message
604
+ ] }) });
605
+ }
606
+ if (isLoading || !data) {
607
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
608
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-40" }),
609
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-96 rounded-lg" })
610
+ ] });
611
+ }
612
+ const { runs, total } = data;
613
+ return /* @__PURE__ */ jsxs("div", { children: [
614
+ /* @__PURE__ */ jsx("div", { className: "flex items-center mb-6", children: /* @__PURE__ */ jsx("div", { className: "w-40", children: /* @__PURE__ */ jsx(Select, { value: sourceFilter ?? "", onChange: handleSourceChange, children: SOURCES.map((s) => /* @__PURE__ */ jsx("option", { value: s.value, children: s.label }, s.value)) }) }) }),
615
+ /* @__PURE__ */ jsxs(AdminTable, { className: "overflow-x-auto", footer: /* @__PURE__ */ jsx(
616
+ PaginationBar,
617
+ {
618
+ page,
619
+ pageSize,
620
+ total,
621
+ buildHref: (p, ps) => `?page=${p}&pageSize=${ps}${sourceFilter ? `&source=${sourceFilter}` : ""}`,
622
+ onNavigate: handlePaginationNavigate
623
+ }
624
+ ), children: [
625
+ /* @__PURE__ */ jsxs(AdminTableHead, { children: [
626
+ /* @__PURE__ */ jsx(Th, { children: "Run" }),
627
+ /* @__PURE__ */ jsx(Th, { children: "Agent" }),
628
+ /* @__PURE__ */ jsx(Th, { children: "Status" }),
629
+ /* @__PURE__ */ jsx(Th, { children: "Source" }),
630
+ /* @__PURE__ */ jsx(Th, { className: "max-w-xs", children: "Prompt" }),
631
+ /* @__PURE__ */ jsx(Th, { align: "right", children: "Cost" }),
632
+ /* @__PURE__ */ jsx(Th, { align: "right", children: "Turns" }),
633
+ /* @__PURE__ */ jsx(Th, { align: "right", children: "Duration" }),
634
+ /* @__PURE__ */ jsx(Th, { children: "Created" })
635
+ ] }),
636
+ /* @__PURE__ */ jsxs("tbody", { children: [
637
+ runs.map((r) => /* @__PURE__ */ jsxs(AdminTableRow, { children: [
638
+ /* @__PURE__ */ jsx("td", { className: "p-3 font-mono text-xs", children: /* @__PURE__ */ jsxs(LinkComponent, { href: `${basePath}/runs/${r.id}`, className: "text-primary hover:underline", children: [
639
+ r.id.slice(0, 8),
640
+ "..."
641
+ ] }) }),
642
+ /* @__PURE__ */ jsx("td", { className: "p-3 text-xs", children: r.agent_name }),
643
+ /* @__PURE__ */ jsx("td", { className: "p-3", children: /* @__PURE__ */ jsx(RunStatusBadge, { status: r.status }) }),
644
+ /* @__PURE__ */ jsx("td", { className: "p-3", children: /* @__PURE__ */ jsx(RunSourceBadge, { triggeredBy: r.triggered_by }) }),
645
+ /* @__PURE__ */ jsxs("td", { className: "p-3 max-w-xs truncate text-muted-foreground text-xs", title: r.prompt, children: [
646
+ r.prompt.slice(0, 80),
647
+ r.prompt.length > 80 ? "..." : ""
648
+ ] }),
649
+ /* @__PURE__ */ jsxs("td", { className: "p-3 text-right font-mono", children: [
650
+ "$",
651
+ r.cost_usd.toFixed(4)
652
+ ] }),
653
+ /* @__PURE__ */ jsx("td", { className: "p-3 text-right", children: r.num_turns }),
654
+ /* @__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" }),
655
+ /* @__PURE__ */ jsx("td", { className: "p-3 text-muted-foreground text-xs", children: /* @__PURE__ */ jsx(LocalDate, { value: r.created_at }) })
656
+ ] }, r.id)),
657
+ runs.length === 0 && /* @__PURE__ */ jsx(EmptyRow, { colSpan: 9, children: "No runs found" })
658
+ ] })
659
+ ] })
660
+ ] });
661
+ }
662
+ function buildConversation(events) {
663
+ const items = [];
664
+ const toolCallMap = /* @__PURE__ */ new Map();
665
+ for (const event of events) {
666
+ if (event.type === "system") {
667
+ items.push({
668
+ role: "system",
669
+ model: String(event.model || ""),
670
+ tools: event.tools || [],
671
+ skills: event.skills || []
672
+ });
673
+ } else if (event.type === "assistant") {
674
+ const msg = event.message;
675
+ const blocks = msg?.content || [];
676
+ for (const block of blocks) {
677
+ if (block.type === "text" && block.text) {
678
+ items.push({ role: "assistant", text: block.text });
679
+ } else if (block.type === "tool_use" && block.name) {
680
+ const idx = items.length;
681
+ items.push({
682
+ role: "tool",
683
+ toolName: block.name,
684
+ toolInput: block.input,
685
+ toolUseId: block.id
686
+ });
687
+ if (block.id) toolCallMap.set(block.id, idx);
688
+ }
689
+ }
690
+ } else if (event.type === "user") {
691
+ const msg = event.message;
692
+ const blocks = msg?.content || [];
693
+ for (const block of blocks) {
694
+ if (block.type === "tool_result" && block.tool_use_id) {
695
+ const idx = toolCallMap.get(block.tool_use_id);
696
+ if (idx !== void 0 && items[idx]) {
697
+ let output = "";
698
+ if (typeof block.content === "string") {
699
+ output = block.content;
700
+ } else if (Array.isArray(block.content)) {
701
+ output = block.content.filter((c) => c.type === "text" && c.text).map((c) => c.text).join("\n");
702
+ }
703
+ items[idx] = { ...items[idx], toolOutput: output };
704
+ }
705
+ }
706
+ }
707
+ } else if (event.type === "tool_use") {
708
+ const idx = items.length;
709
+ items.push({
710
+ role: "tool",
711
+ toolName: String(event.tool_name || event.name || "unknown"),
712
+ toolInput: event.input,
713
+ toolUseId: event.tool_use_id ? String(event.tool_use_id) : void 0
714
+ });
715
+ if (event.tool_use_id) toolCallMap.set(String(event.tool_use_id), idx);
716
+ } else if (event.type === "tool_result") {
717
+ const id = String(event.tool_use_id || "");
718
+ const idx = toolCallMap.get(id);
719
+ const output = typeof event.result === "string" ? event.result : JSON.stringify(event.result, null, 2);
720
+ if (idx !== void 0 && items[idx]) {
721
+ items[idx] = { ...items[idx], toolOutput: output };
722
+ } else {
723
+ items.push({
724
+ role: "tool",
725
+ toolName: String(event.tool_name || "tool"),
726
+ toolOutput: output,
727
+ toolUseId: id || void 0
728
+ });
729
+ }
730
+ } else if (event.type === "run_started") {
731
+ items.push({
732
+ role: "system",
733
+ model: String(event.model || ""),
734
+ tools: event.mcp_server_names || []
735
+ });
736
+ } else if (event.type === "mcp_error") {
737
+ items.push({
738
+ role: "error",
739
+ error: `MCP (${event.server}): ${event.error}`
740
+ });
741
+ } else if (event.type === "result") {
742
+ items.push({
743
+ role: "result",
744
+ subtype: String(event.subtype || ""),
745
+ costUsd: Number(event.cost_usd || 0),
746
+ numTurns: Number(event.num_turns || 0),
747
+ durationMs: Number(event.duration_ms || 0),
748
+ text: String(event.result || "")
749
+ });
750
+ } else if (event.type === "a2a_incoming") {
751
+ items.push({
752
+ role: "a2a_incoming",
753
+ sender: String(event.agent_name || event.sender || "unknown"),
754
+ text: event.prompt_preview ? String(event.prompt_preview) : void 0,
755
+ callbackUrl: event.callback_url ? String(event.callback_url) : void 0,
756
+ timestamp: event.timestamp ? String(event.timestamp) : void 0
757
+ });
758
+ } else if (event.type === "error") {
759
+ items.push({
760
+ role: "error",
761
+ error: String(event.error || "Unknown error")
762
+ });
763
+ }
764
+ }
765
+ return items;
766
+ }
767
+ function TranscriptViewer({ transcript, prompt }) {
768
+ const conversation = useMemo(() => buildConversation(transcript), [transcript]);
769
+ return /* @__PURE__ */ jsxs(Card, { children: [
770
+ /* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: "Transcript" }) }),
771
+ /* @__PURE__ */ jsxs(CardContent, { className: "space-y-3", children: [
772
+ prompt && /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-border bg-muted/20 px-4 py-3", children: [
773
+ /* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-muted-foreground mb-1", children: "Prompt" }),
774
+ /* @__PURE__ */ jsx("pre", { className: "text-xs font-mono whitespace-pre-wrap", children: prompt })
775
+ ] }),
776
+ transcript.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "No transcript available" }) : /* @__PURE__ */ jsx(ConversationView, { items: conversation })
777
+ ] })
778
+ ] });
779
+ }
780
+ function ConversationView({ items }) {
781
+ return /* @__PURE__ */ jsx("div", { className: "space-y-3", children: items.map((item, i) => {
782
+ switch (item.role) {
783
+ case "a2a_incoming":
784
+ return /* @__PURE__ */ jsx(A2AIncomingItem, { item }, i);
785
+ case "system":
786
+ return /* @__PURE__ */ jsx(SystemItem, { item }, i);
787
+ case "assistant":
788
+ return /* @__PURE__ */ jsx(AssistantItem, { item }, i);
789
+ case "tool":
790
+ return /* @__PURE__ */ jsx(ToolItem, { item }, i);
791
+ case "result":
792
+ return /* @__PURE__ */ jsx(ResultItem, { item }, i);
793
+ case "error":
794
+ return /* @__PURE__ */ jsx(ErrorItem, { item }, i);
795
+ default:
796
+ return null;
797
+ }
798
+ }) });
799
+ }
800
+ function A2AIncomingItem({ item }) {
801
+ const [expanded, setExpanded] = useState(false);
802
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-border bg-muted/30 overflow-hidden", children: [
803
+ /* @__PURE__ */ jsxs(
804
+ "button",
805
+ {
806
+ onClick: () => setExpanded(!expanded),
807
+ className: "w-full flex items-center gap-2 px-4 py-2 text-left hover:bg-muted/50 transition-colors",
808
+ children: [
809
+ /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "text-[10px]", children: "A2A incoming" }),
810
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
811
+ "to ",
812
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: item.sender })
813
+ ] }),
814
+ item.callbackUrl && /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground truncate", children: [
815
+ "via ",
816
+ /* @__PURE__ */ jsx("span", { className: "font-mono", children: new URL(item.callbackUrl).hostname })
817
+ ] }),
818
+ item.timestamp && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground ml-auto flex-shrink-0", children: new Date(item.timestamp).toLocaleTimeString() }),
819
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground flex-shrink-0", children: expanded ? "\u25B2" : "\u25BC" })
820
+ ]
821
+ }
822
+ ),
823
+ expanded && item.text && /* @__PURE__ */ jsx("div", { className: "px-4 py-3 border-t border-border bg-muted/10", children: /* @__PURE__ */ jsx("pre", { className: "text-xs font-mono whitespace-pre-wrap text-muted-foreground", children: item.text }) })
824
+ ] });
825
+ }
826
+ function SystemItem({ item }) {
827
+ const [expanded, setExpanded] = useState(false);
828
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-border bg-muted/30 overflow-hidden", children: [
829
+ /* @__PURE__ */ jsxs("button", { onClick: () => setExpanded(!expanded), className: "w-full flex items-center gap-2 px-4 py-2 text-left hover:bg-muted/50 transition-colors", children: [
830
+ /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "text-[10px]", children: "system" }),
831
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground flex-1", children: [
832
+ "Model: ",
833
+ /* @__PURE__ */ jsx("span", { className: "font-mono", children: item.model }),
834
+ " \xB7 ",
835
+ item.tools?.length || 0,
836
+ " tools",
837
+ (item.skills?.length || 0) > 0 ? ` \xB7 ${item.skills.length} skills` : ""
838
+ ] }),
839
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground flex-shrink-0", children: expanded ? "\u25B2" : "\u25BC" })
840
+ ] }),
841
+ expanded && /* @__PURE__ */ jsxs("div", { className: "px-4 py-3 border-t border-border bg-muted/10 text-xs text-muted-foreground space-y-1", children: [
842
+ item.tools && item.tools.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
843
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Tools:" }),
844
+ " ",
845
+ item.tools.join(", ")
846
+ ] }),
847
+ item.skills && item.skills.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
848
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Skills:" }),
849
+ " ",
850
+ item.skills.join(", ")
851
+ ] })
852
+ ] })
853
+ ] });
854
+ }
855
+ function AssistantItem({ item }) {
856
+ const [expanded, setExpanded] = useState(false);
857
+ const preview = item.text?.split("\n")[0]?.slice(0, 120) ?? "";
858
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-border overflow-hidden", children: [
859
+ /* @__PURE__ */ jsxs(
860
+ "button",
861
+ {
862
+ onClick: () => setExpanded(!expanded),
863
+ className: "w-full flex items-center gap-2 px-4 py-2 text-left hover:bg-muted/30 transition-colors",
864
+ children: [
865
+ /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "text-[10px]", children: "assistant" }),
866
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground truncate flex-1", children: preview }),
867
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground flex-shrink-0", children: expanded ? "\u25B2" : "\u25BC" })
868
+ ]
869
+ }
870
+ ),
871
+ expanded && /* @__PURE__ */ jsx("div", { className: "px-4 py-3 border-t border-border bg-muted/10 prose prose-sm dark:prose-invert max-w-none", children: /* @__PURE__ */ jsx(ReactMarkdown, { children: item.text }) })
872
+ ] });
873
+ }
874
+ function ToolItem({ item }) {
875
+ const [expanded, setExpanded] = useState(false);
876
+ const hasOutput = item.toolOutput && item.toolOutput.length > 0;
877
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-border overflow-hidden", children: [
878
+ /* @__PURE__ */ jsxs(
879
+ "button",
880
+ {
881
+ onClick: () => setExpanded(!expanded),
882
+ className: "w-full flex items-center gap-2 px-4 py-2 text-left hover:bg-muted/30 transition-colors",
883
+ children: [
884
+ /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-[10px]", children: "tool" }),
885
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium font-mono", children: item.toolName }),
886
+ hasOutput && /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "text-[10px] ml-auto", children: "has output" }),
887
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: expanded ? "\u25B2" : "\u25BC" })
888
+ ]
889
+ }
890
+ ),
891
+ expanded && /* @__PURE__ */ jsxs("div", { className: "border-t border-border", children: [
892
+ item.toolInput !== void 0 && /* @__PURE__ */ jsxs("div", { className: "px-4 py-2 bg-muted/20", children: [
893
+ /* @__PURE__ */ jsx("div", { className: "text-[10px] font-medium text-muted-foreground uppercase mb-1", children: "Input" }),
894
+ /* @__PURE__ */ jsx("pre", { className: "text-xs font-mono overflow-x-auto max-h-64 overflow-y-auto", children: typeof item.toolInput === "string" ? item.toolInput : JSON.stringify(item.toolInput, null, 2) })
895
+ ] }),
896
+ hasOutput && /* @__PURE__ */ jsxs("div", { className: "px-4 py-2 bg-muted/10 border-t border-border", children: [
897
+ /* @__PURE__ */ jsx("div", { className: "text-[10px] font-medium text-muted-foreground uppercase mb-1", children: "Output" }),
898
+ /* @__PURE__ */ jsx("div", { className: "prose prose-sm dark:prose-invert max-w-none max-h-96 overflow-y-auto", children: /* @__PURE__ */ jsx(ReactMarkdown, { children: item.toolOutput }) })
899
+ ] })
900
+ ] })
901
+ ] });
902
+ }
903
+ function ResultItem({ item }) {
904
+ const success = item.subtype === "success";
905
+ return /* @__PURE__ */ jsxs("div", { className: `rounded-md px-3 py-2 flex items-center gap-3 ${success ? "bg-green-950 border border-green-900" : "bg-red-950 border border-red-900"}`, children: [
906
+ /* @__PURE__ */ jsx("span", { className: `text-xs font-semibold ${success ? "text-green-400" : "text-red-400"}`, children: success ? "Completed" : "Failed" }),
907
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-3 text-xs text-zinc-400", children: [
908
+ item.numTurns != null && item.numTurns > 0 && /* @__PURE__ */ jsxs("span", { children: [
909
+ item.numTurns,
910
+ " turns"
911
+ ] }),
912
+ item.costUsd != null && item.costUsd > 0 && /* @__PURE__ */ jsxs("span", { children: [
913
+ "$",
914
+ item.costUsd.toFixed(4)
915
+ ] }),
916
+ item.durationMs != null && item.durationMs > 0 && /* @__PURE__ */ jsxs("span", { children: [
917
+ (item.durationMs / 1e3).toFixed(1),
918
+ "s"
919
+ ] })
920
+ ] })
921
+ ] });
922
+ }
923
+ function ErrorItem({ item }) {
924
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-destructive/30 bg-destructive/5 px-4 py-3", children: [
925
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx(Badge, { variant: "destructive", className: "text-[10px]", children: "error" }) }),
926
+ /* @__PURE__ */ jsx("pre", { className: "mt-1 text-xs font-mono text-destructive whitespace-pre-wrap", children: item.error })
927
+ ] });
928
+ }
929
+ function RunDetailPage({ runId, initialData, initialTranscript }) {
930
+ const { mutate } = useSWRConfig();
931
+ const { data: run, error, isLoading } = useApi(
932
+ `run-${runId}`,
933
+ (client) => client.runs.get(runId),
934
+ initialData ? { fallbackData: initialData } : void 0
935
+ );
936
+ const { data: transcript } = useApi(
937
+ run?.transcript_blob_url ? `transcript-${runId}` : null,
938
+ (client) => client.runs.transcript(runId),
939
+ initialTranscript ? { fallbackData: initialTranscript } : void 0
940
+ );
941
+ if (error) {
942
+ return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-[40vh]", children: /* @__PURE__ */ jsxs("p", { className: "text-destructive", children: [
943
+ "Failed to load run: ",
944
+ error.message
945
+ ] }) });
946
+ }
947
+ if (isLoading || !run) {
948
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
949
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-4 gap-4", children: Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-24 rounded-lg" }, i)) }),
950
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-96 rounded-lg" })
951
+ ] });
952
+ }
953
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
954
+ (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}`) }) }),
955
+ run.triggered_by === "a2a" && run.requested_by_key_name && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
956
+ /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "text-[10px]", children: "A2A" }),
957
+ /* @__PURE__ */ jsxs("span", { children: [
958
+ "Requested by ",
959
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: run.requested_by_key_name })
960
+ ] })
961
+ ] }),
962
+ /* @__PURE__ */ jsxs("div", { className: `grid gap-4 ${run.result_summary ? "grid-cols-5" : "grid-cols-4"}`, children: [
963
+ run.result_summary && /* @__PURE__ */ jsxs(MetricCard, { label: "Result Summary", children: [
964
+ /* @__PURE__ */ jsx("span", { className: "line-clamp-1", children: run.result_summary }),
965
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-0.5 font-normal", children: run.status })
966
+ ] }),
967
+ /* @__PURE__ */ jsxs(MetricCard, { label: "Model", children: [
968
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-xs", children: run.agent_model || "\u2014" }),
969
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-0.5 font-normal", children: /* @__PURE__ */ jsx("span", { className: `inline-flex items-center rounded-full px-1.5 py-0.5 text-[10px] font-medium ${run.runner === "vercel-ai-sdk" ? "bg-blue-500/10 text-blue-400" : "bg-orange-500/10 text-orange-400"}`, children: run.runner === "vercel-ai-sdk" ? "AI SDK" : "Claude SDK" }) })
970
+ ] }),
971
+ /* @__PURE__ */ jsx(MetricCard, { label: "Cost", children: /* @__PURE__ */ jsxs("span", { className: "font-mono", children: [
972
+ "$",
973
+ run.cost_usd != null ? run.cost_usd.toFixed(4) : "\u2014"
974
+ ] }) }),
975
+ /* @__PURE__ */ jsx(MetricCard, { label: "Turns", children: run.num_turns }),
976
+ /* @__PURE__ */ jsx(MetricCard, { label: "Duration", children: run.duration_ms > 0 ? `${(run.duration_ms / 1e3).toFixed(1)}s` : "\u2014" }),
977
+ /* @__PURE__ */ jsxs(MetricCard, { label: "Tokens", children: [
978
+ (run.total_input_tokens + run.total_output_tokens).toLocaleString(),
979
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground mt-0.5 font-normal", children: [
980
+ run.total_input_tokens.toLocaleString(),
981
+ " in / ",
982
+ run.total_output_tokens.toLocaleString(),
983
+ " out"
984
+ ] })
985
+ ] })
986
+ ] }),
987
+ run.error_messages && run.error_messages.length > 0 && /* @__PURE__ */ jsxs(Card, { children: [
988
+ /* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsx(CardTitle, { className: "text-base text-destructive", children: "Errors" }) }),
989
+ /* @__PURE__ */ jsxs(CardContent, { className: "space-y-2", children: [
990
+ run.error_type && /* @__PURE__ */ jsx(Badge, { variant: "destructive", children: run.error_type }),
991
+ 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))
992
+ ] })
993
+ ] }),
994
+ /* @__PURE__ */ jsx(TranscriptViewer, { transcript: transcript || [], prompt: run.prompt }),
995
+ /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs("details", { children: [
996
+ /* @__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: [
997
+ /* @__PURE__ */ jsx("span", { className: "text-base font-semibold", children: "Metadata" }),
998
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "\\u25BC" })
999
+ ] }),
1000
+ /* @__PURE__ */ jsx("div", { className: "px-6 pb-6", children: /* @__PURE__ */ jsxs("dl", { className: "grid grid-cols-2 gap-x-8 gap-y-2 text-sm", children: [
1001
+ /* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: "Run ID" }),
1002
+ /* @__PURE__ */ jsx("dd", { className: "font-mono", children: run.id }),
1003
+ /* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: "Agent ID" }),
1004
+ /* @__PURE__ */ jsx("dd", { className: "font-mono", children: run.agent_id }),
1005
+ /* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: "Company ID" }),
1006
+ /* @__PURE__ */ jsx("dd", { className: "font-mono", children: run.tenant_id }),
1007
+ /* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: "Sandbox ID" }),
1008
+ /* @__PURE__ */ jsx("dd", { className: "font-mono", children: run.sandbox_id || "\u2014" }),
1009
+ /* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: "Started" }),
1010
+ /* @__PURE__ */ jsx("dd", { children: /* @__PURE__ */ jsx(LocalDate, { value: run.started_at }) }),
1011
+ /* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: "Completed" }),
1012
+ /* @__PURE__ */ jsx("dd", { children: /* @__PURE__ */ jsx(LocalDate, { value: run.completed_at }) }),
1013
+ /* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: "Created" }),
1014
+ /* @__PURE__ */ jsx("dd", { children: /* @__PURE__ */ jsx(LocalDate, { value: run.created_at }) })
1015
+ ] }) })
1016
+ ] }) })
1017
+ ] });
1018
+ }
1019
+ function CancelRunButton({ runId, onCancelled }) {
1020
+ const [open, setOpen] = useState(false);
1021
+ const [cancelling, setCancelling] = useState(false);
1022
+ const client = useAgentPlaneClient();
1023
+ async function handleConfirm() {
1024
+ setCancelling(true);
1025
+ try {
1026
+ await client.runs.cancel(runId);
1027
+ setOpen(false);
1028
+ onCancelled();
1029
+ } catch {
1030
+ } finally {
1031
+ setCancelling(false);
1032
+ }
1033
+ }
1034
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1035
+ /* @__PURE__ */ jsx(
1036
+ Button,
1037
+ {
1038
+ variant: "destructive",
1039
+ size: "sm",
1040
+ onClick: () => setOpen(true),
1041
+ children: "Stop Run"
1042
+ }
1043
+ ),
1044
+ /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-w-sm", children: [
1045
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
1046
+ /* @__PURE__ */ jsx(DialogTitle, { children: "Stop this run?" }),
1047
+ /* @__PURE__ */ jsx(DialogDescription, { children: "This will terminate the sandbox immediately." })
1048
+ ] }),
1049
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
1050
+ /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: () => setOpen(false), disabled: cancelling, children: "Cancel" }),
1051
+ /* @__PURE__ */ jsx(
1052
+ Button,
1053
+ {
1054
+ variant: "destructive",
1055
+ size: "sm",
1056
+ onClick: handleConfirm,
1057
+ disabled: cancelling,
1058
+ children: cancelling ? "Stopping\u2026" : "Stop Run"
1059
+ }
1060
+ )
1061
+ ] })
1062
+ ] }) })
1063
+ ] });
1064
+ }
1065
+ function McpServerListPage({ initialData }) {
1066
+ const { mutate } = useSWRConfig();
1067
+ const client = useAgentPlaneClient();
1068
+ const { data: servers, error, isLoading } = useApi(
1069
+ "mcp-servers",
1070
+ (c) => c.customConnectors.listServers(),
1071
+ initialData ? { fallbackData: initialData } : void 0
1072
+ );
1073
+ const [showAdd, setShowAdd] = useState(false);
1074
+ const [adding, setAdding] = useState(false);
1075
+ const [newServer, setNewServer] = useState({ name: "", slug: "", description: "", base_url: "", mcp_endpoint_path: "/mcp" });
1076
+ const [deleteTarget, setDeleteTarget] = useState(null);
1077
+ const [deleting, setDeleting] = useState(false);
1078
+ const [deleteError, setDeleteError] = useState("");
1079
+ async function handleAdd() {
1080
+ setAdding(true);
1081
+ try {
1082
+ await client.customConnectors.createServer(newServer);
1083
+ setShowAdd(false);
1084
+ setNewServer({ name: "", slug: "", description: "", base_url: "", mcp_endpoint_path: "/mcp" });
1085
+ mutate("mcp-servers");
1086
+ } finally {
1087
+ setAdding(false);
1088
+ }
1089
+ }
1090
+ async function handleDelete() {
1091
+ if (!deleteTarget) return;
1092
+ setDeleting(true);
1093
+ setDeleteError("");
1094
+ try {
1095
+ await client.customConnectors.deleteServer(deleteTarget.id);
1096
+ setDeleteTarget(null);
1097
+ mutate("mcp-servers");
1098
+ } catch (err) {
1099
+ setDeleteError(err instanceof Error ? err.message : "Unknown error");
1100
+ } finally {
1101
+ setDeleting(false);
1102
+ }
1103
+ }
1104
+ if (error) {
1105
+ return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-[40vh]", children: /* @__PURE__ */ jsxs("p", { className: "text-destructive", children: [
1106
+ "Failed to load MCP servers: ",
1107
+ error.message
1108
+ ] }) });
1109
+ }
1110
+ if (isLoading || !servers) {
1111
+ return /* @__PURE__ */ jsx(Skeleton, { className: "h-96 rounded-lg" });
1112
+ }
1113
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
1114
+ /* @__PURE__ */ jsx("div", { className: "flex items-center", children: /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: () => setShowAdd(!showAdd), children: showAdd ? "Cancel" : "Register Connector" }) }),
1115
+ showAdd && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-border p-4 space-y-3", children: [
1116
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
1117
+ /* @__PURE__ */ jsx(Input, { placeholder: "Name", value: newServer.name, onChange: (e) => setNewServer({ ...newServer, name: e.target.value }) }),
1118
+ /* @__PURE__ */ jsx(Input, { placeholder: "Slug", value: newServer.slug, onChange: (e) => setNewServer({ ...newServer, slug: e.target.value }) }),
1119
+ /* @__PURE__ */ jsx(Input, { placeholder: "Description", value: newServer.description, onChange: (e) => setNewServer({ ...newServer, description: e.target.value }) }),
1120
+ /* @__PURE__ */ jsx(Input, { placeholder: "Base URL", value: newServer.base_url, onChange: (e) => setNewServer({ ...newServer, base_url: e.target.value }) }),
1121
+ /* @__PURE__ */ jsx(Input, { placeholder: "MCP Endpoint Path", value: newServer.mcp_endpoint_path, onChange: (e) => setNewServer({ ...newServer, mcp_endpoint_path: e.target.value }) })
1122
+ ] }),
1123
+ /* @__PURE__ */ jsx(Button, { size: "sm", onClick: handleAdd, disabled: adding || !newServer.name || !newServer.base_url, children: adding ? "Adding..." : "Add Server" })
1124
+ ] }),
1125
+ /* @__PURE__ */ jsxs(AdminTable, { children: [
1126
+ /* @__PURE__ */ jsxs(AdminTableHead, { children: [
1127
+ /* @__PURE__ */ jsx(Th, { children: "Name" }),
1128
+ /* @__PURE__ */ jsx(Th, { children: "Slug" }),
1129
+ /* @__PURE__ */ jsx(Th, { children: "Base URL" }),
1130
+ /* @__PURE__ */ jsx(Th, { children: "OAuth" }),
1131
+ /* @__PURE__ */ jsx(Th, { align: "right", children: "Connections" }),
1132
+ /* @__PURE__ */ jsx(Th, { align: "right", children: "Active" }),
1133
+ /* @__PURE__ */ jsx(Th, { children: "Created" }),
1134
+ /* @__PURE__ */ jsx(Th, { align: "right" })
1135
+ ] }),
1136
+ /* @__PURE__ */ jsxs("tbody", { children: [
1137
+ servers.map((s) => /* @__PURE__ */ jsxs(AdminTableRow, { children: [
1138
+ /* @__PURE__ */ jsx("td", { className: "p-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1139
+ s.logo_url && /* @__PURE__ */ jsx("img", { src: s.logo_url, alt: "", className: "w-5 h-5 rounded-sm object-contain" }),
1140
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: s.name })
1141
+ ] }) }),
1142
+ /* @__PURE__ */ jsx("td", { className: "p-3 font-mono text-xs text-muted-foreground", children: s.slug }),
1143
+ /* @__PURE__ */ jsx("td", { className: "p-3 font-mono text-xs text-muted-foreground truncate max-w-xs", title: s.base_url, children: s.base_url }),
1144
+ /* @__PURE__ */ jsx("td", { className: "p-3", children: /* @__PURE__ */ jsx(Badge, { variant: s.client_id ? "default" : "secondary", children: s.client_id ? "Registered" : "No DCR" }) }),
1145
+ /* @__PURE__ */ jsx("td", { className: "p-3 text-right", children: s.connection_count }),
1146
+ /* @__PURE__ */ jsx("td", { className: "p-3 text-right text-green-500", children: s.active_count }),
1147
+ /* @__PURE__ */ jsx("td", { className: "p-3 text-muted-foreground text-xs", children: new Date(s.created_at).toLocaleDateString() }),
1148
+ /* @__PURE__ */ jsx("td", { className: "p-3 text-right", children: /* @__PURE__ */ jsx(
1149
+ Button,
1150
+ {
1151
+ variant: "destructive",
1152
+ size: "sm",
1153
+ disabled: s.connection_count > 0,
1154
+ onClick: () => setDeleteTarget(s),
1155
+ children: "Delete"
1156
+ }
1157
+ ) })
1158
+ ] }, s.id)),
1159
+ servers.length === 0 && /* @__PURE__ */ jsx(EmptyRow, { colSpan: 8, children: 'No custom connectors registered. Click "Register Connector" to add one.' })
1160
+ ] })
1161
+ ] }),
1162
+ /* @__PURE__ */ jsxs(
1163
+ ConfirmDialog,
1164
+ {
1165
+ open: !!deleteTarget,
1166
+ onOpenChange: (open) => {
1167
+ if (!open) {
1168
+ setDeleteTarget(null);
1169
+ setDeleteError("");
1170
+ }
1171
+ },
1172
+ title: "Delete Server",
1173
+ confirmLabel: "Delete",
1174
+ loadingLabel: "Deleting...",
1175
+ loading: deleting,
1176
+ error: deleteError,
1177
+ onConfirm: handleDelete,
1178
+ children: [
1179
+ "Delete MCP server ",
1180
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: deleteTarget?.name }),
1181
+ "? This cannot be undone."
1182
+ ]
1183
+ }
1184
+ )
1185
+ ] });
1186
+ }
1187
+ function PluginMarketplaceListPage({ initialData }) {
1188
+ const { mutate } = useSWRConfig();
1189
+ const client = useAgentPlaneClient();
1190
+ const { LinkComponent, basePath } = useNavigation();
1191
+ const { data: marketplaces, error, isLoading } = useApi(
1192
+ "plugin-marketplaces",
1193
+ (c) => c.pluginMarketplaces.list(),
1194
+ initialData ? { fallbackData: initialData } : void 0
1195
+ );
1196
+ const [showAdd, setShowAdd] = useState(false);
1197
+ const [adding, setAdding] = useState(false);
1198
+ const [newMarketplace, setNewMarketplace] = useState({ name: "", github_repo: "" });
1199
+ const [deleteTarget, setDeleteTarget] = useState(null);
1200
+ const [deleting, setDeleting] = useState(false);
1201
+ const [deleteError, setDeleteError] = useState("");
1202
+ async function handleAdd() {
1203
+ setAdding(true);
1204
+ try {
1205
+ await client.pluginMarketplaces.create(newMarketplace);
1206
+ setShowAdd(false);
1207
+ setNewMarketplace({ name: "", github_repo: "" });
1208
+ mutate("plugin-marketplaces");
1209
+ } finally {
1210
+ setAdding(false);
1211
+ }
1212
+ }
1213
+ async function handleDelete() {
1214
+ if (!deleteTarget) return;
1215
+ setDeleting(true);
1216
+ setDeleteError("");
1217
+ try {
1218
+ await client.pluginMarketplaces.delete(deleteTarget.id);
1219
+ setDeleteTarget(null);
1220
+ mutate("plugin-marketplaces");
1221
+ } catch (err) {
1222
+ setDeleteError(err instanceof Error ? err.message : "Unknown error");
1223
+ } finally {
1224
+ setDeleting(false);
1225
+ }
1226
+ }
1227
+ if (error) {
1228
+ return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-[40vh]", children: /* @__PURE__ */ jsxs("p", { className: "text-destructive", children: [
1229
+ "Failed to load marketplaces: ",
1230
+ error.message
1231
+ ] }) });
1232
+ }
1233
+ if (isLoading || !marketplaces) {
1234
+ return /* @__PURE__ */ jsx(Skeleton, { className: "h-96 rounded-lg" });
1235
+ }
1236
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
1237
+ /* @__PURE__ */ jsx("div", { className: "flex items-center", children: /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: () => setShowAdd(!showAdd), children: showAdd ? "Cancel" : "Add Marketplace" }) }),
1238
+ showAdd && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-border p-4 space-y-3", children: [
1239
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
1240
+ /* @__PURE__ */ jsx(Input, { placeholder: "Name", value: newMarketplace.name, onChange: (e) => setNewMarketplace({ ...newMarketplace, name: e.target.value }) }),
1241
+ /* @__PURE__ */ jsx(Input, { placeholder: "GitHub Repo (owner/repo)", value: newMarketplace.github_repo, onChange: (e) => setNewMarketplace({ ...newMarketplace, github_repo: e.target.value }) })
1242
+ ] }),
1243
+ /* @__PURE__ */ jsx(Button, { size: "sm", onClick: handleAdd, disabled: adding || !newMarketplace.name || !newMarketplace.github_repo, children: adding ? "Adding..." : "Add Marketplace" })
1244
+ ] }),
1245
+ /* @__PURE__ */ jsxs(AdminTable, { children: [
1246
+ /* @__PURE__ */ jsxs(AdminTableHead, { children: [
1247
+ /* @__PURE__ */ jsx(Th, { children: "Name" }),
1248
+ /* @__PURE__ */ jsx(Th, { children: "GitHub Repo" }),
1249
+ /* @__PURE__ */ jsx(Th, { align: "right", children: "Agents Using" }),
1250
+ /* @__PURE__ */ jsx(Th, { children: "Added" }),
1251
+ /* @__PURE__ */ jsx(Th, { align: "right" })
1252
+ ] }),
1253
+ /* @__PURE__ */ jsxs("tbody", { children: [
1254
+ marketplaces.map((m) => /* @__PURE__ */ jsxs(AdminTableRow, { children: [
1255
+ /* @__PURE__ */ jsxs("td", { className: "p-3 font-medium", children: [
1256
+ /* @__PURE__ */ jsx(
1257
+ LinkComponent,
1258
+ {
1259
+ href: `${basePath}/plugin-marketplaces/${m.id}`,
1260
+ className: "text-primary hover:underline",
1261
+ children: m.name
1262
+ }
1263
+ ),
1264
+ m.is_owned && /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "ml-2 text-xs", children: "Owned" })
1265
+ ] }),
1266
+ /* @__PURE__ */ jsx("td", { className: "p-3", children: /* @__PURE__ */ jsx(
1267
+ "a",
1268
+ {
1269
+ href: `https://github.com/${m.github_repo}`,
1270
+ target: "_blank",
1271
+ rel: "noopener noreferrer",
1272
+ className: "font-mono text-xs text-primary hover:underline",
1273
+ children: m.github_repo
1274
+ }
1275
+ ) }),
1276
+ /* @__PURE__ */ jsx("td", { className: "p-3 text-right", children: /* @__PURE__ */ jsx(Badge, { variant: m.agent_count > 0 ? "default" : "secondary", children: m.agent_count }) }),
1277
+ /* @__PURE__ */ jsx("td", { className: "p-3 text-muted-foreground text-xs", children: new Date(m.created_at).toLocaleDateString() }),
1278
+ /* @__PURE__ */ jsx("td", { className: "p-3 text-right", children: /* @__PURE__ */ jsx(
1279
+ Button,
1280
+ {
1281
+ variant: "destructive",
1282
+ size: "sm",
1283
+ disabled: m.agent_count > 0,
1284
+ onClick: () => setDeleteTarget(m),
1285
+ children: "Delete"
1286
+ }
1287
+ ) })
1288
+ ] }, m.id)),
1289
+ marketplaces.length === 0 && /* @__PURE__ */ jsx(EmptyRow, { colSpan: 5, children: 'No plugin marketplaces registered. Click "Add Marketplace" to add one.' })
1290
+ ] })
1291
+ ] }),
1292
+ /* @__PURE__ */ jsxs(
1293
+ ConfirmDialog,
1294
+ {
1295
+ open: !!deleteTarget,
1296
+ onOpenChange: (open) => {
1297
+ if (!open) {
1298
+ setDeleteTarget(null);
1299
+ setDeleteError("");
1300
+ }
1301
+ },
1302
+ title: "Delete Marketplace",
1303
+ confirmLabel: "Delete",
1304
+ loadingLabel: "Deleting...",
1305
+ loading: deleting,
1306
+ error: deleteError,
1307
+ onConfirm: handleDelete,
1308
+ children: [
1309
+ "Delete marketplace ",
1310
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: deleteTarget?.name }),
1311
+ "? This cannot be undone."
1312
+ ]
1313
+ }
1314
+ )
1315
+ ] });
1316
+ }
1317
+ function PluginMarketplaceDetailPage({ marketplaceId, initialData, initialPlugins }) {
1318
+ const { mutate } = useSWRConfig();
1319
+ const client = useAgentPlaneClient();
1320
+ const { LinkComponent, basePath } = useNavigation();
1321
+ const { data: marketplace, error, isLoading } = useApi(
1322
+ `marketplace-${marketplaceId}`,
1323
+ (c) => c.pluginMarketplaces.get(marketplaceId),
1324
+ initialData ? { fallbackData: initialData } : void 0
1325
+ );
1326
+ const { data: pluginsResult } = useApi(
1327
+ `marketplace-${marketplaceId}-plugins`,
1328
+ (c) => c.pluginMarketplaces.listPlugins(marketplaceId),
1329
+ initialPlugins ? { fallbackData: initialPlugins } : void 0
1330
+ );
1331
+ const [tokenInput, setTokenInput] = useState("");
1332
+ const [savingToken, setSavingToken] = useState(false);
1333
+ async function handleSaveToken() {
1334
+ setSavingToken(true);
1335
+ try {
1336
+ await client.pluginMarketplaces.updateToken(marketplaceId, { github_token: tokenInput });
1337
+ setTokenInput("");
1338
+ mutate(`marketplace-${marketplaceId}`);
1339
+ mutate(`marketplace-${marketplaceId}-plugins`);
1340
+ } finally {
1341
+ setSavingToken(false);
1342
+ }
1343
+ }
1344
+ if (error) {
1345
+ return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-[40vh]", children: /* @__PURE__ */ jsxs("p", { className: "text-destructive", children: [
1346
+ "Failed to load marketplace: ",
1347
+ error.message
1348
+ ] }) });
1349
+ }
1350
+ if (isLoading || !marketplace) {
1351
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
1352
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-12 w-64" }),
1353
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-32 rounded-lg" }),
1354
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-64 rounded-lg" })
1355
+ ] });
1356
+ }
1357
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
1358
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
1359
+ marketplace.has_token && /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: "Owned" }),
1360
+ /* @__PURE__ */ jsx(
1361
+ "a",
1362
+ {
1363
+ href: `https://github.com/${marketplace.github_repo}`,
1364
+ target: "_blank",
1365
+ rel: "noopener noreferrer",
1366
+ className: "font-mono text-xs text-primary hover:underline",
1367
+ children: marketplace.github_repo
1368
+ }
1369
+ )
1370
+ ] }),
1371
+ /* @__PURE__ */ jsxs(Card, { children: [
1372
+ /* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: "GitHub Token" }) }),
1373
+ /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
1374
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: marketplace.has_token ? "A GitHub token is configured. You can update it below." : "Add a GitHub personal access token to enable write access and private repo support." }),
1375
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
1376
+ /* @__PURE__ */ jsx(
1377
+ Input,
1378
+ {
1379
+ type: "password",
1380
+ placeholder: "ghp_...",
1381
+ value: tokenInput,
1382
+ onChange: (e) => setTokenInput(e.target.value),
1383
+ className: "flex-1"
1384
+ }
1385
+ ),
1386
+ /* @__PURE__ */ jsx(Button, { size: "sm", onClick: handleSaveToken, disabled: savingToken || !tokenInput, children: savingToken ? "Saving..." : marketplace.has_token ? "Update Token" : "Save Token" })
1387
+ ] })
1388
+ ] }) })
1389
+ ] }),
1390
+ /* @__PURE__ */ jsxs("div", { children: [
1391
+ /* @__PURE__ */ jsx(SectionHeader, { title: "Plugins" }),
1392
+ !pluginsResult ? /* @__PURE__ */ jsx(Skeleton, { className: "h-48 rounded-lg" }) : !pluginsResult.ok ? /* @__PURE__ */ jsxs("p", { className: "text-sm text-destructive", children: [
1393
+ "Failed to load plugins: ",
1394
+ pluginsResult.message
1395
+ ] }) : pluginsResult.data.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "No plugins found in this marketplace." }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4", children: pluginsResult.data.map((plugin) => /* @__PURE__ */ jsx(
1396
+ LinkComponent,
1397
+ {
1398
+ href: `${basePath}/plugin-marketplaces/${marketplaceId}/plugins/${plugin.name}`,
1399
+ children: /* @__PURE__ */ jsxs(Card, { className: "hover:border-primary/50 transition-colors cursor-pointer h-full", children: [
1400
+ /* @__PURE__ */ jsx(CardHeader, { className: "pb-2", children: /* @__PURE__ */ jsxs(CardTitle, { className: "text-sm font-medium flex items-center justify-between", children: [
1401
+ plugin.displayName,
1402
+ plugin.version && /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground font-normal", children: [
1403
+ "v",
1404
+ plugin.version
1405
+ ] })
1406
+ ] }) }),
1407
+ /* @__PURE__ */ jsxs(CardContent, { children: [
1408
+ plugin.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mb-3", children: plugin.description }),
1409
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5", children: [
1410
+ plugin.hasAgents && /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-xs", children: "Agents" }),
1411
+ plugin.hasSkills && /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-xs", children: "Skills" }),
1412
+ plugin.hasMcpJson && /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-xs", children: "MCP" })
1413
+ ] })
1414
+ ] })
1415
+ ] })
1416
+ },
1417
+ plugin.name
1418
+ )) })
1419
+ ] })
1420
+ ] });
1421
+ }
1422
+ var TIMEZONES = typeof Intl !== "undefined" && Intl.supportedValuesOf ? Intl.supportedValuesOf("timeZone") : ["UTC"];
1423
+ function SettingsPage({ initialData, hideDangerZone }) {
1424
+ const { mutate } = useSWRConfig();
1425
+ const { data: tenantData, error: tenantError, isLoading: tenantLoading } = useApi(
1426
+ "settings-tenant",
1427
+ (c) => c.tenants.getMe(),
1428
+ initialData ? { fallbackData: initialData.tenant } : void 0
1429
+ );
1430
+ const { data: apiKeysData, error: apiKeysError, isLoading: apiKeysLoading } = useApi(
1431
+ "settings-keys",
1432
+ (c) => c.keys.list ? c.keys.list() : Promise.resolve([]),
1433
+ initialData ? { fallbackData: initialData.api_keys } : void 0
1434
+ );
1435
+ const data = tenantData && apiKeysData ? { tenant: tenantData, api_keys: apiKeysData } : void 0;
1436
+ const error = tenantError || apiKeysError;
1437
+ const isLoading = tenantLoading || apiKeysLoading;
1438
+ if (error) {
1439
+ return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-[40vh]", children: /* @__PURE__ */ jsxs("p", { className: "text-destructive", children: [
1440
+ "Failed to load settings: ",
1441
+ error.message
1442
+ ] }) });
1443
+ }
1444
+ if (isLoading || !data) {
1445
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
1446
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-48 rounded-lg" }),
1447
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-64 rounded-lg" })
1448
+ ] });
1449
+ }
1450
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
1451
+ /* @__PURE__ */ jsx(CompanyForm, { tenant: data.tenant, onSaved: () => {
1452
+ mutate("settings-tenant");
1453
+ mutate("settings-keys");
1454
+ } }),
1455
+ /* @__PURE__ */ jsx(ApiKeysSection, { initialKeys: data.api_keys, onChanged: () => {
1456
+ mutate("settings-tenant");
1457
+ mutate("settings-keys");
1458
+ } }),
1459
+ !hideDangerZone && /* @__PURE__ */ jsx(DangerZone, { tenantId: data.tenant.id, tenantName: data.tenant.name })
1460
+ ] });
1461
+ }
1462
+ function CompanyForm({ tenant, onSaved }) {
1463
+ const client = useAgentPlaneClient();
1464
+ const fileInputRef = useRef(null);
1465
+ const [name, setName] = useState(tenant.name);
1466
+ const [budget, setBudget] = useState(tenant.monthly_budget_usd.toString());
1467
+ const [timezone, setTimezone] = useState(tenant.timezone);
1468
+ const [logoUrl, setLogoUrl] = useState(tenant.logo_url ?? "");
1469
+ const [saving, setSaving] = useState(false);
1470
+ const isDirty = name !== tenant.name || budget !== tenant.monthly_budget_usd.toString() || timezone !== tenant.timezone || (logoUrl || "") !== (tenant.logo_url ?? "");
1471
+ async function handleSave() {
1472
+ setSaving(true);
1473
+ try {
1474
+ await client.tenants.updateMe({
1475
+ name,
1476
+ monthly_budget_usd: parseFloat(budget),
1477
+ timezone,
1478
+ logo_url: logoUrl || null
1479
+ });
1480
+ onSaved();
1481
+ } finally {
1482
+ setSaving(false);
1483
+ }
1484
+ }
1485
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
1486
+ /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-muted-foreground/25 p-5", children: [
1487
+ /* @__PURE__ */ jsx(SectionHeader, { title: "Logo" }),
1488
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-5", children: [
1489
+ logoUrl ? /* @__PURE__ */ jsx("img", { src: logoUrl, alt: name, className: "w-16 h-16 rounded-xl object-cover border border-border", referrerPolicy: "no-referrer" }) : /* @__PURE__ */ jsx("div", { className: "w-16 h-16 rounded-xl bg-muted flex items-center justify-center text-xl font-bold text-muted-foreground", children: name.split(/\s+/).map((w) => w[0]).join("").toUpperCase().slice(0, 2) || "?" }),
1490
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1491
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Upload a logo for your company. Recommended size: 256x256px." }),
1492
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1493
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "outline", onClick: () => fileInputRef.current?.click(), children: "Upload image" }),
1494
+ logoUrl && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "outline", onClick: () => setLogoUrl(""), children: "Remove" }),
1495
+ /* @__PURE__ */ jsx(
1496
+ "input",
1497
+ {
1498
+ ref: fileInputRef,
1499
+ type: "file",
1500
+ accept: "image/*",
1501
+ className: "hidden",
1502
+ onChange: (e) => {
1503
+ const file = e.target.files?.[0];
1504
+ if (!file) return;
1505
+ const reader = new FileReader();
1506
+ reader.onload = () => setLogoUrl(reader.result);
1507
+ reader.readAsDataURL(file);
1508
+ e.target.value = "";
1509
+ }
1510
+ }
1511
+ )
1512
+ ] })
1513
+ ] })
1514
+ ] })
1515
+ ] }),
1516
+ /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-muted-foreground/25 p-5", children: [
1517
+ /* @__PURE__ */ jsx(SectionHeader, { title: "Company Details" }),
1518
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4", children: [
1519
+ /* @__PURE__ */ jsx(FormField, { label: "Name", children: /* @__PURE__ */ jsx(Input, { value: name, onChange: (e) => setName(e.target.value) }) }),
1520
+ /* @__PURE__ */ jsx(FormField, { label: "Slug", children: /* @__PURE__ */ jsx(Input, { value: tenant.slug, readOnly: true, disabled: true, className: "opacity-60" }) }),
1521
+ /* @__PURE__ */ jsx(FormField, { label: "Status", children: /* @__PURE__ */ jsx("div", { className: "flex items-center h-9", children: /* @__PURE__ */ jsx(Badge, { variant: tenant.status === "active" ? "default" : "destructive", children: tenant.status }) }) }),
1522
+ /* @__PURE__ */ jsx(FormField, { label: "Timezone", children: /* @__PURE__ */ jsx(Select, { value: timezone, onChange: (e) => setTimezone(e.target.value), children: TIMEZONES.map((tz) => /* @__PURE__ */ jsx("option", { value: tz, children: tz.replace(/_/g, " ") }, tz)) }) }),
1523
+ /* @__PURE__ */ jsx(FormField, { label: "Monthly Budget (USD)", children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
1524
+ /* @__PURE__ */ jsx("span", { className: "absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground text-sm", children: "$" }),
1525
+ /* @__PURE__ */ jsx(
1526
+ Input,
1527
+ {
1528
+ type: "number",
1529
+ step: "0.01",
1530
+ value: budget,
1531
+ onChange: (e) => setBudget(e.target.value),
1532
+ className: "pl-7"
1533
+ }
1534
+ )
1535
+ ] }) })
1536
+ ] })
1537
+ ] }),
1538
+ /* @__PURE__ */ jsx("div", { className: "flex items-center", children: /* @__PURE__ */ jsx(Button, { onClick: handleSave, disabled: saving || !isDirty, size: "sm", children: saving ? "Saving..." : "Save Changes" }) })
1539
+ ] });
1540
+ }
1541
+ function ApiKeysSection({ initialKeys, onChanged }) {
1542
+ const client = useAgentPlaneClient();
1543
+ const [creating, setCreating] = useState(false);
1544
+ const [newKeyName, setNewKeyName] = useState("default");
1545
+ const [showCreate, setShowCreate] = useState(false);
1546
+ const [rawKey, setRawKey] = useState(null);
1547
+ const [revokeTarget, setRevokeTarget] = useState(null);
1548
+ const [revoking, setRevoking] = useState(false);
1549
+ const [revokeError, setRevokeError] = useState("");
1550
+ async function handleCreate() {
1551
+ setCreating(true);
1552
+ try {
1553
+ const result = await client.keys.create({ name: newKeyName });
1554
+ setRawKey(result.key);
1555
+ setShowCreate(false);
1556
+ setNewKeyName("default");
1557
+ onChanged();
1558
+ } finally {
1559
+ setCreating(false);
1560
+ }
1561
+ }
1562
+ async function handleRevoke() {
1563
+ if (!revokeTarget) return;
1564
+ setRevoking(true);
1565
+ setRevokeError("");
1566
+ try {
1567
+ await client.keys.revoke(revokeTarget.id);
1568
+ setRevokeTarget(null);
1569
+ onChanged();
1570
+ } catch (err) {
1571
+ setRevokeError(err instanceof Error ? err.message : "Unknown error");
1572
+ } finally {
1573
+ setRevoking(false);
1574
+ }
1575
+ }
1576
+ const activeKeys = initialKeys.filter((k) => !k.revoked_at);
1577
+ const revokedKeys = initialKeys.filter((k) => k.revoked_at);
1578
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-muted-foreground/25 p-5", children: [
1579
+ /* @__PURE__ */ jsx(SectionHeader, { title: "API Keys", children: /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: () => setShowCreate(!showCreate), children: showCreate ? "Cancel" : "+ New Key" }) }),
1580
+ rawKey && /* @__PURE__ */ jsxs("div", { className: "mb-4 p-3 rounded-lg border border-yellow-500/50 bg-yellow-500/10", children: [
1581
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium mb-1", children: "New API key created \u2014 copy it now, it won't be shown again:" }),
1582
+ /* @__PURE__ */ jsx("code", { className: "block text-xs font-mono bg-black/20 p-2 rounded break-all select-all", children: rawKey }),
1583
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "outline", className: "mt-2", onClick: () => setRawKey(null), children: "Dismiss" })
1584
+ ] }),
1585
+ showCreate && /* @__PURE__ */ jsxs("div", { className: "mb-4 flex gap-2 items-end", children: [
1586
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
1587
+ /* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: "Key Name" }),
1588
+ /* @__PURE__ */ jsx(
1589
+ Input,
1590
+ {
1591
+ value: newKeyName,
1592
+ onChange: (e) => setNewKeyName(e.target.value),
1593
+ placeholder: "default",
1594
+ className: "w-64"
1595
+ }
1596
+ )
1597
+ ] }),
1598
+ /* @__PURE__ */ jsx(Button, { size: "sm", onClick: handleCreate, disabled: creating, children: creating ? "Creating..." : "Create" })
1599
+ ] }),
1600
+ /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-border", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
1601
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b border-border bg-muted/50", children: [
1602
+ /* @__PURE__ */ jsx("th", { className: "text-left p-3 font-medium", children: "Name" }),
1603
+ /* @__PURE__ */ jsx("th", { className: "text-left p-3 font-medium", children: "Prefix" }),
1604
+ /* @__PURE__ */ jsx("th", { className: "text-left p-3 font-medium", children: "Status" }),
1605
+ /* @__PURE__ */ jsx("th", { className: "text-left p-3 font-medium", children: "Last Used" }),
1606
+ /* @__PURE__ */ jsx("th", { className: "text-left p-3 font-medium", children: "Created" }),
1607
+ /* @__PURE__ */ jsx("th", { className: "text-right p-3 font-medium" })
1608
+ ] }) }),
1609
+ /* @__PURE__ */ jsxs("tbody", { children: [
1610
+ activeKeys.map((k) => /* @__PURE__ */ jsxs("tr", { className: "border-b border-border hover:bg-muted/30", children: [
1611
+ /* @__PURE__ */ jsx("td", { className: "p-3 font-medium", children: k.name }),
1612
+ /* @__PURE__ */ jsxs("td", { className: "p-3 font-mono text-xs text-muted-foreground", children: [
1613
+ k.key_prefix,
1614
+ "..."
1615
+ ] }),
1616
+ /* @__PURE__ */ jsx("td", { className: "p-3", children: /* @__PURE__ */ jsx(Badge, { variant: "default", children: "active" }) }),
1617
+ /* @__PURE__ */ jsx("td", { className: "p-3 text-muted-foreground text-xs", children: k.last_used_at ? new Date(k.last_used_at).toLocaleString() : "never" }),
1618
+ /* @__PURE__ */ jsx("td", { className: "p-3 text-muted-foreground text-xs", children: new Date(k.created_at).toLocaleDateString() }),
1619
+ /* @__PURE__ */ jsx("td", { className: "p-3 text-right", children: /* @__PURE__ */ jsx(
1620
+ Button,
1621
+ {
1622
+ size: "sm",
1623
+ variant: "destructive",
1624
+ onClick: () => setRevokeTarget(k),
1625
+ children: "Revoke"
1626
+ }
1627
+ ) })
1628
+ ] }, k.id)),
1629
+ revokedKeys.map((k) => /* @__PURE__ */ jsxs("tr", { className: "border-b border-border opacity-50", children: [
1630
+ /* @__PURE__ */ jsx("td", { className: "p-3", children: k.name }),
1631
+ /* @__PURE__ */ jsxs("td", { className: "p-3 font-mono text-xs text-muted-foreground", children: [
1632
+ k.key_prefix,
1633
+ "..."
1634
+ ] }),
1635
+ /* @__PURE__ */ jsx("td", { className: "p-3", children: /* @__PURE__ */ jsx(Badge, { variant: "destructive", children: "revoked" }) }),
1636
+ /* @__PURE__ */ jsx("td", { className: "p-3 text-muted-foreground text-xs", children: k.last_used_at ? new Date(k.last_used_at).toLocaleString() : "never" }),
1637
+ /* @__PURE__ */ jsx("td", { className: "p-3 text-muted-foreground text-xs", children: new Date(k.created_at).toLocaleDateString() }),
1638
+ /* @__PURE__ */ jsx("td", { className: "p-3" })
1639
+ ] }, k.id)),
1640
+ initialKeys.length === 0 && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: 6, className: "p-6 text-center text-muted-foreground", children: "No API keys" }) })
1641
+ ] })
1642
+ ] }) }),
1643
+ /* @__PURE__ */ jsxs(
1644
+ ConfirmDialog,
1645
+ {
1646
+ open: !!revokeTarget,
1647
+ onOpenChange: (open) => {
1648
+ if (!open) setRevokeTarget(null);
1649
+ },
1650
+ title: "Revoke API Key",
1651
+ confirmLabel: "Revoke",
1652
+ loadingLabel: "Revoking...",
1653
+ loading: revoking,
1654
+ error: revokeError,
1655
+ onConfirm: handleRevoke,
1656
+ children: [
1657
+ "Revoke API key ",
1658
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: revokeTarget?.name }),
1659
+ " (",
1660
+ revokeTarget?.key_prefix,
1661
+ "...)? This cannot be undone."
1662
+ ]
1663
+ }
1664
+ )
1665
+ ] });
1666
+ }
1667
+ function DangerZone({ tenantId, tenantName }) {
1668
+ const client = useAgentPlaneClient();
1669
+ const { onNavigate, basePath } = useNavigation();
1670
+ const [open, setOpen] = useState(false);
1671
+ const [deleting, setDeleting] = useState(false);
1672
+ const [error, setError] = useState("");
1673
+ async function handleDelete() {
1674
+ setDeleting(true);
1675
+ setError("");
1676
+ try {
1677
+ await client.tenants.deleteMe();
1678
+ setOpen(false);
1679
+ onNavigate(basePath || "/");
1680
+ } catch (err) {
1681
+ setError(err instanceof Error ? err.message : "Unknown error");
1682
+ } finally {
1683
+ setDeleting(false);
1684
+ }
1685
+ }
1686
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-destructive/30 p-5", children: [
1687
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1688
+ /* @__PURE__ */ jsxs("div", { children: [
1689
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-destructive", children: "Danger Zone" }),
1690
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Permanently delete this company and all its agents, runs, sessions, and API keys." })
1691
+ ] }),
1692
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "destructive", onClick: () => setOpen(true), children: "Delete Company" })
1693
+ ] }),
1694
+ /* @__PURE__ */ jsxs(
1695
+ ConfirmDialog,
1696
+ {
1697
+ open,
1698
+ onOpenChange: (v) => {
1699
+ if (!v) {
1700
+ setOpen(false);
1701
+ setError("");
1702
+ }
1703
+ },
1704
+ title: "Delete Company",
1705
+ confirmLabel: "Delete Company",
1706
+ loadingLabel: "Deleting...",
1707
+ loading: deleting,
1708
+ error,
1709
+ onConfirm: handleDelete,
1710
+ children: [
1711
+ "This action ",
1712
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: "cannot be undone" }),
1713
+ ". All agents, runs, sessions, and API keys for ",
1714
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: tenantName }),
1715
+ " will be permanently deleted."
1716
+ ]
1717
+ }
1718
+ )
1719
+ ] });
1720
+ }
1721
+ function formatContextWindow(tokens) {
1722
+ if (tokens == null) return "\u2014";
1723
+ if (tokens >= 1e6) return `${(tokens / 1e6).toFixed(tokens % 1e6 === 0 ? 0 : 1)}M`;
1724
+ if (tokens >= 1e3) return `${Math.round(tokens / 1e3)}K`;
1725
+ return tokens.toString();
1726
+ }
1727
+ function formatPrice(perMillion) {
1728
+ if (perMillion == null) return "\u2014";
1729
+ if (perMillion === 0) return "Free";
1730
+ if (perMillion < 1e-3) return `<$0.01/M`;
1731
+ if (perMillion < 0.01) return `$${perMillion.toFixed(3)}/M`;
1732
+ if (perMillion < 1) return `$${perMillion.toFixed(2)}/M`;
1733
+ return `$${perMillion % 1 === 0 ? perMillion : perMillion.toFixed(2)}/M`;
1734
+ }
1735
+ var TAG_LABELS = {
1736
+ "tool-use": "T",
1737
+ reasoning: "R",
1738
+ vision: "V",
1739
+ "file-input": "F",
1740
+ "implicit-caching": "C",
1741
+ "image-generation": "I"
1742
+ };
1743
+ var TAG_TITLES = {
1744
+ "tool-use": "Tool Use",
1745
+ reasoning: "Reasoning",
1746
+ vision: "Vision",
1747
+ "file-input": "File Input",
1748
+ "implicit-caching": "Implicit Caching",
1749
+ "image-generation": "Image Generation"
1750
+ };
1751
+ function ModelSelector({ value, onChange, disabled }) {
1752
+ const client = useAgentPlaneClient();
1753
+ const [open, setOpen] = useState(false);
1754
+ const [models, setModels] = useState(null);
1755
+ const [loading, setLoading] = useState(false);
1756
+ const [error, setError] = useState(false);
1757
+ const [search, setSearch] = useState("");
1758
+ const [providerFilter, setProviderFilter] = useState("all");
1759
+ const hasFetchedRef = useRef(false);
1760
+ useEffect(() => {
1761
+ if (!open || hasFetchedRef.current) return;
1762
+ hasFetchedRef.current = true;
1763
+ let cancelled = false;
1764
+ setLoading(true);
1765
+ setError(false);
1766
+ client.models.list().then((data) => {
1767
+ if (!cancelled) {
1768
+ setModels(data);
1769
+ setLoading(false);
1770
+ }
1771
+ }).catch(() => {
1772
+ if (!cancelled) {
1773
+ setError(true);
1774
+ setLoading(false);
1775
+ }
1776
+ });
1777
+ return () => {
1778
+ cancelled = true;
1779
+ };
1780
+ }, [open, client]);
1781
+ const providers = useMemo(() => {
1782
+ if (!models) return [];
1783
+ const set = new Set(models.map((m) => m.provider));
1784
+ return Array.from(set).sort();
1785
+ }, [models]);
1786
+ const grouped = useMemo(() => {
1787
+ if (!models) return {};
1788
+ let filtered = models;
1789
+ if (providerFilter !== "all") {
1790
+ filtered = filtered.filter((m) => m.provider === providerFilter);
1791
+ }
1792
+ const groups = {};
1793
+ for (const m of filtered) {
1794
+ const key = m.provider;
1795
+ if (!groups[key]) groups[key] = [];
1796
+ groups[key].push(m);
1797
+ }
1798
+ return groups;
1799
+ }, [models, providerFilter]);
1800
+ const selectedModel = models?.find((m) => m.id === value);
1801
+ const displayName = selectedModel?.name || value || "Select model...";
1802
+ const searchMatchesAny = useMemo(() => {
1803
+ if (!search || !models) return true;
1804
+ const lower = search.toLowerCase();
1805
+ return models.some(
1806
+ (m) => m.id.toLowerCase().includes(lower) || m.name.toLowerCase().includes(lower) || m.provider.toLowerCase().includes(lower)
1807
+ );
1808
+ }, [search, models]);
1809
+ function handleSelect(modelId) {
1810
+ onChange(modelId);
1811
+ setOpen(false);
1812
+ setSearch("");
1813
+ }
1814
+ function handleOpenChange(newOpen) {
1815
+ if (disabled) return;
1816
+ setOpen(newOpen);
1817
+ if (!newOpen) {
1818
+ setSearch("");
1819
+ }
1820
+ }
1821
+ return /* @__PURE__ */ jsxs(Popover.Root, { open, onOpenChange: handleOpenChange, children: [
1822
+ /* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
1823
+ "button",
1824
+ {
1825
+ type: "button",
1826
+ disabled,
1827
+ className: "flex h-9 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors hover:bg-accent disabled:opacity-50 disabled:cursor-not-allowed",
1828
+ children: [
1829
+ /* @__PURE__ */ jsx("span", { className: "truncate text-left", children: displayName }),
1830
+ selectedModel === void 0 && value && models && /* @__PURE__ */ jsx("span", { className: "ml-1 rounded bg-yellow-500/20 px-1 py-0.5 text-[10px] text-yellow-400", children: "custom" }),
1831
+ /* @__PURE__ */ jsx(
1832
+ "svg",
1833
+ {
1834
+ className: "ml-2 h-4 w-4 shrink-0 text-muted-foreground",
1835
+ fill: "none",
1836
+ viewBox: "0 0 24 24",
1837
+ stroke: "currentColor",
1838
+ strokeWidth: 2,
1839
+ children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" })
1840
+ }
1841
+ )
1842
+ ]
1843
+ }
1844
+ ) }),
1845
+ /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(
1846
+ Popover.Content,
1847
+ {
1848
+ className: "z-50 w-[600px] rounded-lg border border-muted-foreground/25 bg-card text-card-foreground shadow-xl",
1849
+ sideOffset: 4,
1850
+ align: "start",
1851
+ onOpenAutoFocus: (e) => e.preventDefault(),
1852
+ children: /* @__PURE__ */ jsxs(
1853
+ Command,
1854
+ {
1855
+ filter: (value2, search2) => {
1856
+ const model = models?.find((m) => m.id === value2);
1857
+ if (!model) return 0;
1858
+ const lower = search2.toLowerCase();
1859
+ if (model.id.toLowerCase().includes(lower)) return 1;
1860
+ if (model.name.toLowerCase().includes(lower)) return 1;
1861
+ if (model.provider.toLowerCase().includes(lower)) return 0.5;
1862
+ return 0;
1863
+ },
1864
+ children: [
1865
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b border-muted-foreground/25 px-3", children: [
1866
+ /* @__PURE__ */ jsx("svg", { className: "h-4 w-4 shrink-0 text-muted-foreground", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }),
1867
+ /* @__PURE__ */ jsx(
1868
+ Command.Input,
1869
+ {
1870
+ value: search,
1871
+ onValueChange: setSearch,
1872
+ placeholder: "Search model...",
1873
+ className: "flex h-10 w-full bg-transparent text-sm outline-none placeholder:text-muted-foreground"
1874
+ }
1875
+ ),
1876
+ providers.length > 0 && /* @__PURE__ */ jsxs(
1877
+ "select",
1878
+ {
1879
+ value: providerFilter,
1880
+ onChange: (e) => setProviderFilter(e.target.value),
1881
+ className: "h-7 rounded border border-muted-foreground/25 bg-card px-2 text-xs text-muted-foreground outline-none [&_option]:bg-card [&_option]:text-foreground",
1882
+ children: [
1883
+ /* @__PURE__ */ jsx("option", { value: "all", children: "All Providers" }),
1884
+ providers.map((p) => /* @__PURE__ */ jsx("option", { value: p, children: p.charAt(0).toUpperCase() + p.slice(1) }, p))
1885
+ ]
1886
+ }
1887
+ )
1888
+ ] }),
1889
+ !loading && models && /* @__PURE__ */ jsxs("div", { className: "mx-1 grid grid-cols-[1fr_60px_70px_70px_80px] items-center border-b border-muted-foreground/15 px-3 py-1.5 text-[10px] font-medium uppercase tracking-wider text-muted-foreground", children: [
1890
+ /* @__PURE__ */ jsx("span", { children: "Model" }),
1891
+ /* @__PURE__ */ jsx("span", { className: "text-right", children: "Context" }),
1892
+ /* @__PURE__ */ jsx("span", { className: "text-right", children: "Input" }),
1893
+ /* @__PURE__ */ jsx("span", { className: "text-right", children: "Output" }),
1894
+ /* @__PURE__ */ jsx("span", { className: "text-right pr-1", children: "Tags" })
1895
+ ] }),
1896
+ /* @__PURE__ */ jsxs(Command.List, { className: "max-h-[340px] overflow-y-auto", children: [
1897
+ loading && /* @__PURE__ */ jsx("div", { className: "space-y-1 p-3", children: [1, 2, 3, 4, 5].map((i) => /* @__PURE__ */ jsx("div", { className: "h-8 animate-pulse rounded bg-muted-foreground/10" }, i)) }),
1898
+ error && /* @__PURE__ */ jsx("div", { className: "px-4 py-2 text-xs text-yellow-400", children: "Failed to load models from gateway. Showing cached data." }),
1899
+ /* @__PURE__ */ jsx(Command.Empty, { className: "px-4 py-4 text-center text-sm text-muted-foreground", children: "No models found." }),
1900
+ Object.entries(grouped).map(([provider, providerModels]) => /* @__PURE__ */ jsx(
1901
+ Command.Group,
1902
+ {
1903
+ heading: provider.charAt(0).toUpperCase() + provider.slice(1),
1904
+ className: "[&_[cmdk-group-heading]]:px-4 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-semibold [&_[cmdk-group-heading]]:text-muted-foreground",
1905
+ children: providerModels.map((m) => /* @__PURE__ */ jsxs(
1906
+ Command.Item,
1907
+ {
1908
+ value: m.id,
1909
+ onSelect: () => handleSelect(m.id),
1910
+ className: "mx-1 grid cursor-pointer grid-cols-[1fr_60px_70px_70px_80px] items-center rounded-md px-3 py-1.5 text-sm data-[selected=true]:bg-accent",
1911
+ children: [
1912
+ /* @__PURE__ */ jsxs("span", { className: "truncate", children: [
1913
+ m.name || m.id,
1914
+ m.id === value && /* @__PURE__ */ jsx("svg", { className: "ml-1 inline h-3.5 w-3.5 text-primary", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 13l4 4L19 7" }) })
1915
+ ] }),
1916
+ /* @__PURE__ */ jsx("span", { className: "text-right text-xs text-muted-foreground", children: formatContextWindow(m.context_window) }),
1917
+ /* @__PURE__ */ jsx("span", { className: "text-right text-xs text-muted-foreground", children: formatPrice(m.input_price_per_million) }),
1918
+ /* @__PURE__ */ jsx("span", { className: "text-right text-xs text-muted-foreground", children: formatPrice(m.output_price_per_million) }),
1919
+ /* @__PURE__ */ jsx("span", { className: "flex justify-end gap-0.5 pr-1", children: (m.capabilities ?? []).filter((t) => TAG_LABELS[t]).map((t) => /* @__PURE__ */ jsx(
1920
+ "span",
1921
+ {
1922
+ title: TAG_TITLES[t] || t,
1923
+ className: "inline-flex h-4 w-4 items-center justify-center rounded bg-muted-foreground/15 text-[9px] font-bold text-muted-foreground",
1924
+ children: TAG_LABELS[t]
1925
+ },
1926
+ t
1927
+ )) })
1928
+ ]
1929
+ },
1930
+ m.id
1931
+ ))
1932
+ },
1933
+ provider
1934
+ )),
1935
+ search && !searchMatchesAny && /* @__PURE__ */ jsxs(
1936
+ Command.Item,
1937
+ {
1938
+ value: `custom:${search}`,
1939
+ onSelect: () => handleSelect(search),
1940
+ className: "mt-1 flex cursor-pointer items-center gap-2 rounded-md border-t border-muted-foreground/10 px-3 py-2 text-sm data-[selected=true]:bg-accent",
1941
+ children: [
1942
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Use custom model:" }),
1943
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-xs", children: search })
1944
+ ]
1945
+ }
1946
+ )
1947
+ ] })
1948
+ ]
1949
+ }
1950
+ )
1951
+ }
1952
+ ) })
1953
+ ] });
1954
+ }
1955
+ function AddAgentDialog({ onCreated }) {
1956
+ const client = useAgentPlaneClient();
1957
+ const [open, setOpen] = useState(false);
1958
+ const [saving, setSaving] = useState(false);
1959
+ const [error, setError] = useState("");
1960
+ const [form, setForm] = useState({
1961
+ name: "",
1962
+ description: "",
1963
+ model: "claude-sonnet-4-6",
1964
+ runner: "",
1965
+ permission_mode: "bypassPermissions",
1966
+ max_turns: "100",
1967
+ max_budget_usd: "1.00",
1968
+ max_runtime_minutes: "10"
1969
+ });
1970
+ function resetForm() {
1971
+ setForm({
1972
+ name: "",
1973
+ description: "",
1974
+ model: "claude-sonnet-4-6",
1975
+ runner: "",
1976
+ permission_mode: "bypassPermissions",
1977
+ max_turns: "100",
1978
+ max_budget_usd: "1.00",
1979
+ max_runtime_minutes: "10"
1980
+ });
1981
+ setError("");
1982
+ }
1983
+ async function handleSubmit(e) {
1984
+ e.preventDefault();
1985
+ setSaving(true);
1986
+ setError("");
1987
+ try {
1988
+ await client.agents.create({
1989
+ name: form.name,
1990
+ description: form.description || null,
1991
+ model: form.model,
1992
+ runner: form.runner || null,
1993
+ permission_mode: form.permission_mode,
1994
+ max_turns: parseInt(form.max_turns),
1995
+ max_budget_usd: parseFloat(form.max_budget_usd),
1996
+ max_runtime_seconds: parseInt(form.max_runtime_minutes) * 60
1997
+ });
1998
+ setOpen(false);
1999
+ resetForm();
2000
+ onCreated();
2001
+ } catch (err) {
2002
+ setError(err?.message ?? "Failed to create agent");
2003
+ } finally {
2004
+ setSaving(false);
2005
+ }
2006
+ }
2007
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2008
+ /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: () => setOpen(true), children: "+ New Agent" }),
2009
+ /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (v) => {
2010
+ setOpen(v);
2011
+ if (!v) resetForm();
2012
+ }, children: /* @__PURE__ */ jsx(DialogContent, { className: "max-w-md", children: /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, children: [
2013
+ /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: "Add Agent" }) }),
2014
+ /* @__PURE__ */ jsxs(DialogBody, { className: "space-y-3", children: [
2015
+ /* @__PURE__ */ jsx(FormField, { label: "Name", children: /* @__PURE__ */ jsx(
2016
+ Input,
2017
+ {
2018
+ value: form.name,
2019
+ onChange: (e) => setForm((f) => ({ ...f, name: e.target.value })),
2020
+ placeholder: "my-agent",
2021
+ required: true
2022
+ }
2023
+ ) }),
2024
+ /* @__PURE__ */ jsx(FormField, { label: "Description", children: /* @__PURE__ */ jsx(
2025
+ Input,
2026
+ {
2027
+ value: form.description,
2028
+ onChange: (e) => setForm((f) => ({ ...f, description: e.target.value })),
2029
+ placeholder: "What does this agent do?"
2030
+ }
2031
+ ) }),
2032
+ /* @__PURE__ */ jsx(FormField, { label: "Model", children: /* @__PURE__ */ jsx(
2033
+ ModelSelector,
2034
+ {
2035
+ value: form.model,
2036
+ onChange: (modelId) => setForm((f) => ({
2037
+ ...f,
2038
+ model: modelId,
2039
+ runner: supportsClaudeRunner(modelId) ? f.runner : "vercel-ai-sdk",
2040
+ permission_mode: supportsClaudeRunner(modelId) ? f.permission_mode : "bypassPermissions"
2041
+ }))
2042
+ }
2043
+ ) }),
2044
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
2045
+ /* @__PURE__ */ jsx(FormField, { label: "Runner", children: supportsClaudeRunner(form.model) ? /* @__PURE__ */ jsxs(
2046
+ Select,
2047
+ {
2048
+ value: form.runner || "claude-agent-sdk",
2049
+ onChange: (e) => setForm((f) => ({ ...f, runner: e.target.value === "claude-agent-sdk" ? "" : e.target.value })),
2050
+ children: [
2051
+ /* @__PURE__ */ jsx("option", { value: "claude-agent-sdk", children: "Claude Agent SDK" }),
2052
+ /* @__PURE__ */ jsx("option", { value: "vercel-ai-sdk", children: "Vercel AI SDK" })
2053
+ ]
2054
+ }
2055
+ ) : /* @__PURE__ */ jsx(Select, { value: "vercel-ai-sdk", disabled: true, children: /* @__PURE__ */ jsx("option", { value: "vercel-ai-sdk", children: "Vercel AI SDK" }) }) }),
2056
+ /* @__PURE__ */ jsx(FormField, { label: "Permission Mode", children: /* @__PURE__ */ jsxs(
2057
+ Select,
2058
+ {
2059
+ value: form.permission_mode,
2060
+ onChange: (e) => setForm((f) => ({ ...f, permission_mode: e.target.value })),
2061
+ disabled: !supportsClaudeRunner(form.model) || form.runner === "vercel-ai-sdk",
2062
+ children: [
2063
+ /* @__PURE__ */ jsx("option", { value: "default", children: "default" }),
2064
+ /* @__PURE__ */ jsx("option", { value: "acceptEdits", children: "acceptEdits" }),
2065
+ /* @__PURE__ */ jsx("option", { value: "bypassPermissions", children: "bypassPermissions" }),
2066
+ /* @__PURE__ */ jsx("option", { value: "plan", children: "plan" })
2067
+ ]
2068
+ }
2069
+ ) })
2070
+ ] }),
2071
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-3", children: [
2072
+ /* @__PURE__ */ jsx(FormField, { label: "Max Turns", children: /* @__PURE__ */ jsx(
2073
+ Input,
2074
+ {
2075
+ type: "number",
2076
+ min: "1",
2077
+ max: "1000",
2078
+ value: form.max_turns,
2079
+ onChange: (e) => setForm((f) => ({ ...f, max_turns: e.target.value })),
2080
+ required: true
2081
+ }
2082
+ ) }),
2083
+ /* @__PURE__ */ jsx(FormField, { label: "Max Budget", children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
2084
+ /* @__PURE__ */ jsx("span", { className: "absolute left-3 top-1/2 -translate-y-1/2 text-sm text-muted-foreground", children: "$" }),
2085
+ /* @__PURE__ */ jsx(
2086
+ Input,
2087
+ {
2088
+ type: "number",
2089
+ step: "0.01",
2090
+ min: "0.01",
2091
+ max: "100",
2092
+ value: form.max_budget_usd,
2093
+ onChange: (e) => setForm((f) => ({ ...f, max_budget_usd: e.target.value })),
2094
+ className: "pl-6",
2095
+ required: true
2096
+ }
2097
+ )
2098
+ ] }) }),
2099
+ /* @__PURE__ */ jsx(FormField, { label: "Max Runtime", children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
2100
+ /* @__PURE__ */ jsx(
2101
+ Input,
2102
+ {
2103
+ type: "number",
2104
+ min: "1",
2105
+ max: "60",
2106
+ value: form.max_runtime_minutes,
2107
+ onChange: (e) => setForm((f) => ({ ...f, max_runtime_minutes: e.target.value })),
2108
+ className: "pr-10",
2109
+ required: true
2110
+ }
2111
+ ),
2112
+ /* @__PURE__ */ jsx("span", { className: "absolute right-3 top-1/2 -translate-y-1/2 text-sm text-muted-foreground", children: "min" })
2113
+ ] }) })
2114
+ ] }),
2115
+ /* @__PURE__ */ jsx(FormError, { error })
2116
+ ] }),
2117
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
2118
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => {
2119
+ setOpen(false);
2120
+ resetForm();
2121
+ }, children: "Cancel" }),
2122
+ /* @__PURE__ */ jsx(Button, { type: "submit", size: "sm", disabled: saving, children: saving ? "Creating..." : "Create Agent" })
2123
+ ] })
2124
+ ] }) }) })
2125
+ ] });
2126
+ }
2127
+ function DeleteAgentButton({ agentId, agentName, onDeleted }) {
2128
+ const client = useAgentPlaneClient();
2129
+ const [open, setOpen] = useState(false);
2130
+ const [deleting, setDeleting] = useState(false);
2131
+ const [error, setError] = useState("");
2132
+ async function handleDelete() {
2133
+ setDeleting(true);
2134
+ setError("");
2135
+ try {
2136
+ await client.agents.delete(agentId);
2137
+ setOpen(false);
2138
+ onDeleted();
2139
+ } catch (err) {
2140
+ setError(err?.message ?? "Failed to delete agent");
2141
+ } finally {
2142
+ setDeleting(false);
2143
+ }
2144
+ }
2145
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2146
+ /* @__PURE__ */ jsx(
2147
+ Button,
2148
+ {
2149
+ size: "sm",
2150
+ variant: "ghost",
2151
+ className: "text-muted-foreground hover:text-destructive text-xs",
2152
+ onClick: () => setOpen(true),
2153
+ children: "Delete"
2154
+ }
2155
+ ),
2156
+ /* @__PURE__ */ jsxs(
2157
+ ConfirmDialog,
2158
+ {
2159
+ open,
2160
+ onOpenChange: setOpen,
2161
+ title: "Delete Agent",
2162
+ confirmLabel: "Delete",
2163
+ loadingLabel: "Deleting...",
2164
+ loading: deleting,
2165
+ error,
2166
+ onConfirm: handleDelete,
2167
+ children: [
2168
+ "Delete ",
2169
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: agentName }),
2170
+ "? This will also remove all associated runs and connections."
2171
+ ]
2172
+ }
2173
+ )
2174
+ ] });
2175
+ }
2176
+ function AgentListPage() {
2177
+ const { LinkComponent, basePath } = useNavigation();
2178
+ const { mutate } = useSWRConfig();
2179
+ const { data, error, isLoading } = useApi(
2180
+ "agents",
2181
+ (client) => client.agents.list()
2182
+ );
2183
+ const agents = data?.data ?? [];
2184
+ const invalidate = useCallback(() => {
2185
+ mutate("agents");
2186
+ }, [mutate]);
2187
+ if (isLoading) {
2188
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
2189
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-32" }),
2190
+ /* @__PURE__ */ jsx("div", { className: "space-y-2", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-12 w-full" }, i)) })
2191
+ ] });
2192
+ }
2193
+ if (error) {
2194
+ return /* @__PURE__ */ jsxs("div", { className: "text-destructive text-sm py-12 text-center", children: [
2195
+ "Failed to load agents: ",
2196
+ error.message
2197
+ ] });
2198
+ }
2199
+ return /* @__PURE__ */ jsxs("div", { children: [
2200
+ /* @__PURE__ */ jsx("div", { className: "flex items-center mb-6", children: /* @__PURE__ */ jsx(AddAgentDialog, { onCreated: invalidate }) }),
2201
+ /* @__PURE__ */ jsxs(AdminTable, { children: [
2202
+ /* @__PURE__ */ jsxs(AdminTableHead, { children: [
2203
+ /* @__PURE__ */ jsx(Th, { children: "Name" }),
2204
+ /* @__PURE__ */ jsx(Th, { children: "Description" }),
2205
+ /* @__PURE__ */ jsx(Th, { children: "Model" }),
2206
+ /* @__PURE__ */ jsx(Th, { children: "Connectors" }),
2207
+ /* @__PURE__ */ jsx(Th, { align: "right", children: "Skills" }),
2208
+ /* @__PURE__ */ jsx(Th, { align: "right", children: "Plugins" }),
2209
+ /* @__PURE__ */ jsx(Th, { align: "right" })
2210
+ ] }),
2211
+ /* @__PURE__ */ jsxs("tbody", { children: [
2212
+ agents.map((a) => /* @__PURE__ */ jsxs(AdminTableRow, { children: [
2213
+ /* @__PURE__ */ jsx("td", { className: "p-3 font-medium", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2214
+ /* @__PURE__ */ jsx(LinkComponent, { href: `${basePath}/agents/${a.id}`, className: "text-primary hover:underline", children: a.name }),
2215
+ a.a2a_enabled && /* @__PURE__ */ jsx(Badge, { className: "text-[10px] px-1.5 py-0 bg-indigo-500/10 text-indigo-400 border-indigo-500/20", children: "A2A" })
2216
+ ] }) }),
2217
+ /* @__PURE__ */ jsx("td", { className: "p-3 text-muted-foreground text-xs max-w-xs truncate", title: a.description ?? void 0, children: a.description ?? "\u2014" }),
2218
+ /* @__PURE__ */ jsx("td", { className: "p-3 font-mono text-xs text-muted-foreground", children: a.model }),
2219
+ /* @__PURE__ */ jsx("td", { className: "p-3", children: a.composio_toolkits.length > 0 ? /* @__PURE__ */ jsx("div", { className: "flex gap-1 flex-wrap", children: a.composio_toolkits.map((t) => /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-xs", children: t }, t)) }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-xs", children: "\u2014" }) }),
2220
+ /* @__PURE__ */ jsx("td", { className: "p-3 text-right", children: (a.skills ?? []).length }),
2221
+ /* @__PURE__ */ jsx("td", { className: "p-3 text-right", children: (a.plugins ?? []).length }),
2222
+ /* @__PURE__ */ jsx("td", { className: "p-3 text-right", children: /* @__PURE__ */ jsx(DeleteAgentButton, { agentId: a.id, agentName: a.name, onDeleted: invalidate }) })
2223
+ ] }, a.id)),
2224
+ agents.length === 0 && /* @__PURE__ */ jsx(EmptyRow, { colSpan: 7, children: "No agents found" })
2225
+ ] })
2226
+ ] })
2227
+ ] });
2228
+ }
2229
+ function AgentEditForm({ agent, onSaved }) {
2230
+ const client = useAgentPlaneClient();
2231
+ const [name, setName] = useState(agent.name);
2232
+ const [description, setDescription] = useState(agent.description ?? "");
2233
+ const [model, setModel] = useState(agent.model);
2234
+ const [runner, setRunner] = useState(agent.runner ?? "");
2235
+ const [permissionMode, setPermissionMode] = useState(agent.permission_mode);
2236
+ const [maxTurns, setMaxTurns] = useState(agent.max_turns.toString());
2237
+ const [maxBudget, setMaxBudget] = useState(agent.max_budget_usd.toString());
2238
+ const [maxRuntime, setMaxRuntime] = useState(Math.floor(agent.max_runtime_seconds / 60).toString());
2239
+ const [saving, setSaving] = useState(false);
2240
+ const isDirty = name !== agent.name || description !== (agent.description ?? "") || model !== agent.model || runner !== (agent.runner ?? "") || permissionMode !== agent.permission_mode || maxTurns !== agent.max_turns.toString() || maxBudget !== agent.max_budget_usd.toString() || maxRuntime !== Math.floor(agent.max_runtime_seconds / 60).toString();
2241
+ const [error, setError] = useState("");
2242
+ async function handleSave() {
2243
+ setSaving(true);
2244
+ setError("");
2245
+ try {
2246
+ await client.agents.update(agent.id, {
2247
+ name,
2248
+ description: description || null,
2249
+ model,
2250
+ runner: runner || null,
2251
+ permission_mode: permissionMode,
2252
+ max_turns: parseInt(maxTurns) || agent.max_turns,
2253
+ max_budget_usd: parseFloat(maxBudget) || agent.max_budget_usd,
2254
+ max_runtime_seconds: (parseInt(maxRuntime) || Math.floor(agent.max_runtime_seconds / 60)) * 60
2255
+ });
2256
+ onSaved?.();
2257
+ } catch (err) {
2258
+ setError(err?.message ?? "Failed to save");
2259
+ } finally {
2260
+ setSaving(false);
2261
+ }
2262
+ }
2263
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-muted-foreground/25 p-5", children: [
2264
+ /* @__PURE__ */ jsxs(SectionHeader, { title: "Details", children: [
2265
+ error && /* @__PURE__ */ jsx("span", { className: "text-sm text-destructive mr-2", children: error }),
2266
+ /* @__PURE__ */ jsx(Button, { onClick: handleSave, disabled: saving || !isDirty, size: "sm", children: saving ? "Saving..." : "Save Changes" })
2267
+ ] }),
2268
+ /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
2269
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-12 gap-4", children: [
2270
+ /* @__PURE__ */ jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsx(FormField, { label: "Name", children: /* @__PURE__ */ jsx(Input, { value: name, onChange: (e) => setName(e.target.value), disabled: saving }) }) }),
2271
+ /* @__PURE__ */ jsx("div", { className: "col-span-4", children: /* @__PURE__ */ jsx(FormField, { label: "Description", children: /* @__PURE__ */ jsx(
2272
+ Input,
2273
+ {
2274
+ value: description,
2275
+ onChange: (e) => setDescription(e.target.value),
2276
+ placeholder: "What does this agent do?",
2277
+ disabled: saving
2278
+ }
2279
+ ) }) }),
2280
+ /* @__PURE__ */ jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsx(FormField, { label: "Model", children: /* @__PURE__ */ jsx(
2281
+ ModelSelector,
2282
+ {
2283
+ value: model,
2284
+ disabled: saving,
2285
+ onChange: (modelId) => {
2286
+ setModel(modelId);
2287
+ if (!supportsClaudeRunner(modelId)) {
2288
+ setRunner("vercel-ai-sdk");
2289
+ setPermissionMode("bypassPermissions");
2290
+ }
2291
+ }
2292
+ }
2293
+ ) }) }),
2294
+ /* @__PURE__ */ jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsx(FormField, { label: "Runner", children: supportsClaudeRunner(model) ? /* @__PURE__ */ jsxs(Select, { value: runner || "claude-agent-sdk", onChange: (e) => setRunner(e.target.value === "claude-agent-sdk" ? "" : e.target.value), disabled: saving, children: [
2295
+ /* @__PURE__ */ jsx("option", { value: "claude-agent-sdk", children: "Claude SDK" }),
2296
+ /* @__PURE__ */ jsx("option", { value: "vercel-ai-sdk", children: "AI SDK" })
2297
+ ] }) : /* @__PURE__ */ jsx(Select, { value: "vercel-ai-sdk", disabled: true, children: /* @__PURE__ */ jsx("option", { value: "vercel-ai-sdk", children: "AI SDK" }) }) }) })
2298
+ ] }),
2299
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-12 gap-4", children: [
2300
+ /* @__PURE__ */ jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsx(FormField, { label: "Max Turns", children: /* @__PURE__ */ jsx(Input, { type: "number", min: "1", max: "1000", value: maxTurns, onChange: (e) => setMaxTurns(e.target.value), disabled: saving }) }) }),
2301
+ /* @__PURE__ */ jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsx(FormField, { label: "Max Budget", children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
2302
+ /* @__PURE__ */ jsx("span", { className: "absolute left-3 top-1/2 -translate-y-1/2 text-sm text-muted-foreground", children: "$" }),
2303
+ /* @__PURE__ */ jsx(Input, { type: "number", step: "0.01", min: "0.01", max: "100", value: maxBudget, onChange: (e) => setMaxBudget(e.target.value), className: "pl-6", disabled: saving })
2304
+ ] }) }) }),
2305
+ /* @__PURE__ */ jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsx(FormField, { label: "Max Runtime", children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
2306
+ /* @__PURE__ */ jsx(Input, { type: "number", min: "1", max: "60", value: maxRuntime, onChange: (e) => setMaxRuntime(e.target.value), className: "pr-10", disabled: saving }),
2307
+ /* @__PURE__ */ jsx("span", { className: "absolute right-3 top-1/2 -translate-y-1/2 text-sm text-muted-foreground", children: "min" })
2308
+ ] }) }) }),
2309
+ supportsClaudeRunner(model) && (runner === "" || runner === "claude-agent-sdk") && /* @__PURE__ */ jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsx(FormField, { label: "Permission Mode", children: /* @__PURE__ */ jsxs(Select, { value: permissionMode, onChange: (e) => setPermissionMode(e.target.value), disabled: saving, children: [
2310
+ /* @__PURE__ */ jsx("option", { value: "default", children: "default" }),
2311
+ /* @__PURE__ */ jsx("option", { value: "acceptEdits", children: "acceptEdits" }),
2312
+ /* @__PURE__ */ jsx("option", { value: "bypassPermissions", children: "bypassPermissions" }),
2313
+ /* @__PURE__ */ jsx("option", { value: "plan", children: "plan" })
2314
+ ] }) }) })
2315
+ ] })
2316
+ ] }) })
2317
+ ] });
2318
+ }
2319
+ function ToolkitMultiselect({ value, onChange }) {
2320
+ const client = useAgentPlaneClient();
2321
+ const [open, setOpen] = useState(false);
2322
+ const [search, setSearch] = useState("");
2323
+ const [toolkits, setToolkits] = useState([]);
2324
+ const [loading, setLoading] = useState(true);
2325
+ const containerRef = useRef(null);
2326
+ const searchRef = useRef(null);
2327
+ useEffect(() => {
2328
+ client.composio.toolkits().then((data) => setToolkits(data)).catch(() => {
2329
+ }).finally(() => setLoading(false));
2330
+ }, [client]);
2331
+ useEffect(() => {
2332
+ function handleClick(e) {
2333
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
2334
+ setOpen(false);
2335
+ setSearch("");
2336
+ }
2337
+ }
2338
+ document.addEventListener("mousedown", handleClick);
2339
+ return () => document.removeEventListener("mousedown", handleClick);
2340
+ }, []);
2341
+ useEffect(() => {
2342
+ searchRef.current?.focus();
2343
+ }, []);
2344
+ const filtered = toolkits.filter((t) => {
2345
+ const q = search.toLowerCase();
2346
+ return t.slug.includes(q) || t.name.toLowerCase().includes(q);
2347
+ });
2348
+ function toggle(slug) {
2349
+ onChange(value.includes(slug) ? value.filter((s) => s !== slug) : [...value, slug]);
2350
+ setSearch("");
2351
+ searchRef.current?.focus();
2352
+ }
2353
+ function remove(slug) {
2354
+ onChange(value.filter((s) => s !== slug));
2355
+ }
2356
+ const selectedToolkits = value.map((slug) => toolkits.find((t) => t.slug === slug) ?? { slug, name: slug, logo: "" }).filter(Boolean);
2357
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative", children: [
2358
+ /* @__PURE__ */ jsxs(
2359
+ "div",
2360
+ {
2361
+ className: "rounded-md border border-input bg-transparent cursor-text",
2362
+ onClick: () => {
2363
+ setOpen(true);
2364
+ searchRef.current?.focus();
2365
+ },
2366
+ children: [
2367
+ selectedToolkits.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5 px-3 pt-2 pb-1", children: selectedToolkits.map((t) => /* @__PURE__ */ jsxs(
2368
+ "span",
2369
+ {
2370
+ className: "inline-flex items-center gap-1 rounded-md bg-secondary text-secondary-foreground text-xs px-2 py-0.5",
2371
+ children: [
2372
+ t.logo && /* @__PURE__ */ jsx("img", { src: t.logo, alt: "", className: "w-3.5 h-3.5 rounded-sm object-contain" }),
2373
+ t.name,
2374
+ /* @__PURE__ */ jsx(
2375
+ "button",
2376
+ {
2377
+ type: "button",
2378
+ onClick: (e) => {
2379
+ e.stopPropagation();
2380
+ remove(t.slug);
2381
+ },
2382
+ className: "text-muted-foreground hover:text-foreground ml-0.5",
2383
+ children: "\xD7"
2384
+ }
2385
+ )
2386
+ ]
2387
+ },
2388
+ t.slug
2389
+ )) }),
2390
+ /* @__PURE__ */ jsx("div", { className: "px-3 py-2", children: /* @__PURE__ */ jsx(
2391
+ "input",
2392
+ {
2393
+ ref: searchRef,
2394
+ value: search,
2395
+ onChange: (e) => setSearch(e.target.value),
2396
+ onFocus: () => setOpen(true),
2397
+ placeholder: "Search toolkits...",
2398
+ className: "w-full bg-transparent text-sm outline-none placeholder:text-muted-foreground"
2399
+ }
2400
+ ) })
2401
+ ]
2402
+ }
2403
+ ),
2404
+ open && search.length > 0 && /* @__PURE__ */ jsx("div", { className: "absolute z-50 mt-1 w-full rounded-md border border-border bg-popover shadow-md", children: /* @__PURE__ */ jsxs("div", { className: "max-h-64 overflow-y-auto", children: [
2405
+ loading && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground p-3", children: "Loading..." }),
2406
+ !loading && filtered.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground p-3", children: "No toolkits found" }),
2407
+ filtered.map((t) => {
2408
+ const selected = value.includes(t.slug);
2409
+ return /* @__PURE__ */ jsxs(
2410
+ "button",
2411
+ {
2412
+ type: "button",
2413
+ onClick: () => toggle(t.slug),
2414
+ className: `w-full flex items-center gap-2.5 px-3 py-2 text-sm text-left hover:bg-accent ${selected ? "bg-accent/50" : ""}`,
2415
+ children: [
2416
+ /* @__PURE__ */ jsx("span", { className: `w-4 h-4 flex-shrink-0 rounded-sm border flex items-center justify-center text-xs ${selected ? "bg-primary border-primary text-primary-foreground" : "border-input"}`, children: selected && "\u2713" }),
2417
+ t.logo && /* @__PURE__ */ jsx("img", { src: t.logo, alt: "", className: "w-5 h-5 rounded-sm object-contain flex-shrink-0" }),
2418
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: t.name }),
2419
+ /* @__PURE__ */ jsx("span", { className: "ml-auto text-xs text-muted-foreground font-mono flex-shrink-0", children: t.slug })
2420
+ ]
2421
+ },
2422
+ t.slug
2423
+ );
2424
+ })
2425
+ ] }) })
2426
+ ] });
2427
+ }
2428
+ function schemeBadgeVariant(scheme) {
2429
+ if (scheme === "NO_AUTH") return "secondary";
2430
+ return "outline";
2431
+ }
2432
+ function statusColor(status) {
2433
+ if (status === "ACTIVE") return "text-green-500";
2434
+ if (status === "INITIATED") return "text-yellow-500";
2435
+ if (status === "FAILED" || status === "EXPIRED" || status === "INACTIVE") return "text-destructive";
2436
+ return "text-muted-foreground";
2437
+ }
2438
+ function ToolsModal({
2439
+ toolkit,
2440
+ toolkitLogo,
2441
+ allowedTools,
2442
+ open,
2443
+ onOpenChange,
2444
+ onSave
2445
+ }) {
2446
+ const client = useAgentPlaneClient();
2447
+ const [tools, setTools] = useState([]);
2448
+ const [loading, setLoading] = useState(false);
2449
+ const [search, setSearch] = useState("");
2450
+ const [selected, setSelected] = useState(/* @__PURE__ */ new Set());
2451
+ const [saving, setSaving] = useState(false);
2452
+ const fetchTools = useCallback(async () => {
2453
+ setLoading(true);
2454
+ try {
2455
+ const data = await client.composio.tools(toolkit);
2456
+ setTools(data ?? []);
2457
+ } finally {
2458
+ setLoading(false);
2459
+ }
2460
+ }, [toolkit, client]);
2461
+ useEffect(() => {
2462
+ if (open) {
2463
+ fetchTools();
2464
+ setSearch("");
2465
+ const toolkitPrefix = toolkit.toUpperCase() + "_";
2466
+ const relevant = allowedTools.filter((t) => t.startsWith(toolkitPrefix));
2467
+ setSelected(new Set(relevant));
2468
+ }
2469
+ }, [open, toolkit, allowedTools, fetchTools]);
2470
+ const filtered = tools.filter((t) => {
2471
+ if (!search) return true;
2472
+ const q = search.toLowerCase();
2473
+ return t.name.toLowerCase().includes(q) || t.slug.toLowerCase().includes(q);
2474
+ });
2475
+ const allSelected = filtered.length > 0 && filtered.every((t) => selected.has(t.slug));
2476
+ function toggleAll() {
2477
+ setSelected((prev) => {
2478
+ const next = new Set(prev);
2479
+ if (allSelected) {
2480
+ for (const t of filtered) next.delete(t.slug);
2481
+ } else {
2482
+ for (const t of filtered) next.add(t.slug);
2483
+ }
2484
+ return next;
2485
+ });
2486
+ }
2487
+ function toggle(slug) {
2488
+ setSelected((prev) => {
2489
+ const next = new Set(prev);
2490
+ if (next.has(slug)) next.delete(slug);
2491
+ else next.add(slug);
2492
+ return next;
2493
+ });
2494
+ }
2495
+ async function handleSave() {
2496
+ setSaving(true);
2497
+ try {
2498
+ const selection = selected.size === tools.length ? [] : Array.from(selected);
2499
+ await onSave(toolkit, selection);
2500
+ } finally {
2501
+ setSaving(false);
2502
+ }
2503
+ }
2504
+ return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-w-lg", children: [
2505
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
2506
+ /* @__PURE__ */ jsxs(DialogTitle, { className: "capitalize flex items-center gap-2", children: [
2507
+ toolkitLogo && /* @__PURE__ */ jsx("img", { src: toolkitLogo, alt: "", className: "w-5 h-5 rounded-sm object-contain flex-shrink-0" }),
2508
+ toolkit,
2509
+ " Tools"
2510
+ ] }),
2511
+ !loading && /* @__PURE__ */ jsxs(DialogDescription, { children: [
2512
+ tools.length,
2513
+ " tools available"
2514
+ ] })
2515
+ ] }),
2516
+ /* @__PURE__ */ jsxs(DialogBody, { className: "space-y-3", children: [
2517
+ /* @__PURE__ */ jsx(Input, { placeholder: "Search tools...", value: search, onChange: (e) => setSearch(e.target.value), className: "h-8 text-sm" }),
2518
+ loading ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground py-4 text-center", children: "Loading tools..." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
2519
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-xs text-muted-foreground", children: [
2520
+ /* @__PURE__ */ jsx("span", { children: selected.size === 0 ? "All tools (no filter)" : `${selected.size} / ${tools.length} selected` }),
2521
+ /* @__PURE__ */ jsx("button", { type: "button", className: "text-primary hover:underline", onClick: toggleAll, children: allSelected ? "Deselect All" : "Select All" })
2522
+ ] }),
2523
+ /* @__PURE__ */ jsxs("div", { className: "max-h-72 overflow-y-auto border border-border rounded-lg divide-y divide-border", children: [
2524
+ filtered.map((t) => /* @__PURE__ */ jsxs("label", { className: "flex items-start gap-2.5 px-3 py-2.5 hover:bg-muted/50 cursor-pointer transition-colors", children: [
2525
+ /* @__PURE__ */ jsx("input", { type: "checkbox", checked: selected.has(t.slug), onChange: () => toggle(t.slug), className: "mt-0.5" }),
2526
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
2527
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium truncate", children: t.name }),
2528
+ t.description && /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground line-clamp-1", children: t.description })
2529
+ ] })
2530
+ ] }, t.slug)),
2531
+ filtered.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground py-4 text-center", children: "No tools match your search" })
2532
+ ] })
2533
+ ] })
2534
+ ] }),
2535
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
2536
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "outline", onClick: () => onOpenChange(false), children: "Cancel" }),
2537
+ /* @__PURE__ */ jsx(Button, { size: "sm", onClick: handleSave, disabled: saving || loading, children: saving ? "Saving..." : "Save" })
2538
+ ] })
2539
+ ] }) });
2540
+ }
2541
+ function McpToolsModal({
2542
+ agentId,
2543
+ mcpServerId,
2544
+ serverName,
2545
+ serverLogo,
2546
+ allowedTools,
2547
+ open,
2548
+ onOpenChange,
2549
+ onSave
2550
+ }) {
2551
+ const client = useAgentPlaneClient();
2552
+ const [tools, setTools] = useState([]);
2553
+ const [loading, setLoading] = useState(false);
2554
+ const [search, setSearch] = useState("");
2555
+ const [selected, setSelected] = useState(/* @__PURE__ */ new Set());
2556
+ const [saving, setSaving] = useState(false);
2557
+ const [error, setError] = useState("");
2558
+ const fetchTools = useCallback(async () => {
2559
+ setLoading(true);
2560
+ setError("");
2561
+ try {
2562
+ const data = await client.customConnectors.listTools(agentId, mcpServerId);
2563
+ setTools(data ?? []);
2564
+ } catch {
2565
+ setError("Failed to load tools");
2566
+ } finally {
2567
+ setLoading(false);
2568
+ }
2569
+ }, [agentId, mcpServerId, client]);
2570
+ useEffect(() => {
2571
+ if (open) {
2572
+ fetchTools();
2573
+ setSearch("");
2574
+ setSelected(new Set(allowedTools));
2575
+ }
2576
+ }, [open, allowedTools, fetchTools]);
2577
+ const filtered = tools.filter((t) => {
2578
+ if (!search) return true;
2579
+ const q = search.toLowerCase();
2580
+ return t.name.toLowerCase().includes(q) || (t.description?.toLowerCase().includes(q) ?? false);
2581
+ });
2582
+ const allSelected = filtered.length > 0 && filtered.every((t) => selected.has(t.name));
2583
+ function toggleAll() {
2584
+ setSelected((prev) => {
2585
+ const next = new Set(prev);
2586
+ if (allSelected) {
2587
+ for (const t of filtered) next.delete(t.name);
2588
+ } else {
2589
+ for (const t of filtered) next.add(t.name);
2590
+ }
2591
+ return next;
2592
+ });
2593
+ }
2594
+ function toggle(name) {
2595
+ setSelected((prev) => {
2596
+ const next = new Set(prev);
2597
+ if (next.has(name)) next.delete(name);
2598
+ else next.add(name);
2599
+ return next;
2600
+ });
2601
+ }
2602
+ async function handleSave() {
2603
+ setSaving(true);
2604
+ try {
2605
+ const selection = selected.size === tools.length ? [] : Array.from(selected);
2606
+ await onSave(selection);
2607
+ } finally {
2608
+ setSaving(false);
2609
+ }
2610
+ }
2611
+ return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-w-lg", children: [
2612
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
2613
+ /* @__PURE__ */ jsxs(DialogTitle, { className: "flex items-center gap-2", children: [
2614
+ serverLogo && /* @__PURE__ */ jsx("img", { src: serverLogo, alt: "", className: "w-5 h-5 rounded-sm object-contain flex-shrink-0" }),
2615
+ serverName,
2616
+ " Tools"
2617
+ ] }),
2618
+ !loading && !error && /* @__PURE__ */ jsxs(DialogDescription, { children: [
2619
+ tools.length,
2620
+ " tools available"
2621
+ ] })
2622
+ ] }),
2623
+ /* @__PURE__ */ jsxs(DialogBody, { className: "space-y-3", children: [
2624
+ /* @__PURE__ */ jsx(Input, { placeholder: "Search tools...", value: search, onChange: (e) => setSearch(e.target.value), className: "h-8 text-sm" }),
2625
+ loading ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground py-4 text-center", children: "Loading tools..." }) : error ? /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive py-4 text-center", children: error }) : /* @__PURE__ */ jsxs(Fragment, { children: [
2626
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-xs text-muted-foreground", children: [
2627
+ /* @__PURE__ */ jsx("span", { children: selected.size === 0 ? "All tools (no filter)" : `${selected.size} / ${tools.length} selected` }),
2628
+ /* @__PURE__ */ jsx("button", { type: "button", className: "text-primary hover:underline", onClick: toggleAll, children: allSelected ? "Deselect All" : "Select All" })
2629
+ ] }),
2630
+ /* @__PURE__ */ jsxs("div", { className: "max-h-72 overflow-y-auto border border-border rounded-lg divide-y divide-border", children: [
2631
+ filtered.map((t) => /* @__PURE__ */ jsxs("label", { className: "flex items-start gap-2.5 px-3 py-2.5 hover:bg-muted/50 cursor-pointer transition-colors", children: [
2632
+ /* @__PURE__ */ jsx("input", { type: "checkbox", checked: selected.has(t.name), onChange: () => toggle(t.name), className: "mt-0.5" }),
2633
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
2634
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium truncate", children: t.name }),
2635
+ t.description && /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground line-clamp-1", children: t.description })
2636
+ ] })
2637
+ ] }, t.name)),
2638
+ filtered.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground py-4 text-center", children: "No tools match your search" })
2639
+ ] })
2640
+ ] })
2641
+ ] }),
2642
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
2643
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "outline", onClick: () => onOpenChange(false), children: "Cancel" }),
2644
+ /* @__PURE__ */ jsx(Button, { size: "sm", onClick: handleSave, disabled: saving || loading, children: saving ? "Saving..." : "Save" })
2645
+ ] })
2646
+ ] }) });
2647
+ }
2648
+ function AgentConnectorsManager({ agentId, toolkits: initialToolkits, composioAllowedTools: initialAllowedTools, onChanged }) {
2649
+ const client = useAgentPlaneClient();
2650
+ const [localToolkits, setLocalToolkits] = useState(initialToolkits);
2651
+ const [connectors, setConnectors] = useState([]);
2652
+ const [loading, setLoading] = useState(true);
2653
+ const [showAdd, setShowAdd] = useState(false);
2654
+ const [pendingToolkits, setPendingToolkits] = useState(initialToolkits);
2655
+ const [applyingToolkits, setApplyingToolkits] = useState(false);
2656
+ const [confirmDelete, setConfirmDelete] = useState(null);
2657
+ const [deleting, setDeleting] = useState(false);
2658
+ const [apiKeys, setApiKeys] = useState({});
2659
+ const [saving, setSaving] = useState({});
2660
+ const [errors, setErrors] = useState({});
2661
+ const [allowedTools, setAllowedTools] = useState(initialAllowedTools);
2662
+ const [toolCounts, setToolCounts] = useState({});
2663
+ const [toolsModalToolkit, setToolsModalToolkit] = useState(null);
2664
+ const mcpOauthHandlerRef = useRef(null);
2665
+ const composioOauthHandlerRef = useRef(null);
2666
+ const [mcpConnections, setMcpConnections] = useState([]);
2667
+ const [mcpServers, setMcpServers] = useState([]);
2668
+ const [mcpLoading, setMcpLoading] = useState(true);
2669
+ const [mcpConnecting, setMcpConnecting] = useState(null);
2670
+ const [confirmMcpDisconnect, setConfirmMcpDisconnect] = useState(null);
2671
+ const [mcpDisconnecting, setMcpDisconnecting] = useState(false);
2672
+ const [mcpToolsModal, setMcpToolsModal] = useState(null);
2673
+ const loadComposio = useCallback(async () => {
2674
+ setLoading(true);
2675
+ try {
2676
+ const data = await client.connectors.list(agentId);
2677
+ setConnectors(data ?? []);
2678
+ } finally {
2679
+ setLoading(false);
2680
+ }
2681
+ }, [agentId, client]);
2682
+ const loadMcp = useCallback(async () => {
2683
+ setMcpLoading(true);
2684
+ try {
2685
+ const data = await client.customConnectors.list(agentId);
2686
+ setMcpConnections(data ?? []);
2687
+ } finally {
2688
+ setMcpLoading(false);
2689
+ }
2690
+ }, [agentId, client]);
2691
+ const toolkitsKey = localToolkits.join(",");
2692
+ useEffect(() => {
2693
+ loadComposio();
2694
+ }, [loadComposio, toolkitsKey]);
2695
+ useEffect(() => {
2696
+ loadMcp();
2697
+ }, [loadMcp]);
2698
+ useEffect(() => {
2699
+ return () => {
2700
+ if (mcpOauthHandlerRef.current) {
2701
+ window.removeEventListener("message", mcpOauthHandlerRef.current);
2702
+ mcpOauthHandlerRef.current = null;
2703
+ }
2704
+ if (composioOauthHandlerRef.current) {
2705
+ window.removeEventListener("message", composioOauthHandlerRef.current);
2706
+ composioOauthHandlerRef.current = null;
2707
+ }
2708
+ };
2709
+ }, []);
2710
+ useEffect(() => {
2711
+ if (localToolkits.length === 0) return;
2712
+ let cancelled = false;
2713
+ for (const slug of localToolkits) {
2714
+ if (toolCounts[slug] !== void 0) continue;
2715
+ client.composio.tools(slug).then((data) => {
2716
+ if (!cancelled) setToolCounts((prev) => ({ ...prev, [slug]: (data ?? []).length }));
2717
+ }).catch(() => {
2718
+ });
2719
+ }
2720
+ return () => {
2721
+ cancelled = true;
2722
+ };
2723
+ }, [toolkitsKey, client]);
2724
+ async function loadMcpServers() {
2725
+ try {
2726
+ const data = await client.customConnectors.listServers();
2727
+ setMcpServers(data ?? []);
2728
+ } catch {
2729
+ }
2730
+ }
2731
+ async function handleToolsSave(toolkit, selectedSlugs) {
2732
+ const prefix = toolkit.toUpperCase() + "_";
2733
+ const otherTools = allowedTools.filter((t) => !t.startsWith(prefix));
2734
+ const updated = [...otherTools, ...selectedSlugs];
2735
+ await client.agents.update(agentId, { composio_allowed_tools: updated });
2736
+ setAllowedTools(updated);
2737
+ setToolsModalToolkit(null);
2738
+ onChanged?.();
2739
+ }
2740
+ async function patchToolkits(newToolkits) {
2741
+ await client.agents.update(agentId, { composio_toolkits: newToolkits });
2742
+ setLocalToolkits(newToolkits);
2743
+ onChanged?.();
2744
+ }
2745
+ async function handleApplyAdd() {
2746
+ setApplyingToolkits(true);
2747
+ try {
2748
+ await patchToolkits(pendingToolkits);
2749
+ setShowAdd(false);
2750
+ } finally {
2751
+ setApplyingToolkits(false);
2752
+ }
2753
+ }
2754
+ async function handleConfirmDelete() {
2755
+ if (!confirmDelete) return;
2756
+ setDeleting(true);
2757
+ try {
2758
+ await patchToolkits(localToolkits.filter((t) => t !== confirmDelete.slug));
2759
+ setConfirmDelete(null);
2760
+ } finally {
2761
+ setDeleting(false);
2762
+ }
2763
+ }
2764
+ async function handleSaveKey(slug) {
2765
+ const key = apiKeys[slug];
2766
+ if (!key) return;
2767
+ setSaving((s) => ({ ...s, [slug]: true }));
2768
+ setErrors((e) => ({ ...e, [slug]: "" }));
2769
+ try {
2770
+ await client.connectors.saveApiKey(agentId, { toolkit: slug, api_key: key });
2771
+ setApiKeys((k) => ({ ...k, [slug]: "" }));
2772
+ await loadComposio();
2773
+ onChanged?.();
2774
+ } catch (err) {
2775
+ setErrors((e) => ({ ...e, [slug]: err?.message ?? "Unknown error" }));
2776
+ } finally {
2777
+ setSaving((s) => ({ ...s, [slug]: false }));
2778
+ }
2779
+ }
2780
+ async function handleMcpConnect(serverId) {
2781
+ setMcpConnecting(serverId);
2782
+ try {
2783
+ if (mcpOauthHandlerRef.current) {
2784
+ window.removeEventListener("message", mcpOauthHandlerRef.current);
2785
+ mcpOauthHandlerRef.current = null;
2786
+ }
2787
+ const popup = window.open("about:blank", "mcp-oauth", "width=600,height=700");
2788
+ const data = await client.customConnectors.initiateOauth(agentId, serverId);
2789
+ if (data.redirectUrl && popup) {
2790
+ popup.location.href = data.redirectUrl;
2791
+ const handler = (event) => {
2792
+ if (event.origin !== window.location.origin) return;
2793
+ if (event.data?.type === "agent_plane_mcp_oauth_callback") {
2794
+ popup.close();
2795
+ window.removeEventListener("message", handler);
2796
+ mcpOauthHandlerRef.current = null;
2797
+ loadMcp();
2798
+ setShowAdd(false);
2799
+ onChanged?.();
2800
+ }
2801
+ };
2802
+ mcpOauthHandlerRef.current = handler;
2803
+ window.addEventListener("message", handler);
2804
+ } else {
2805
+ popup?.close();
2806
+ }
2807
+ } finally {
2808
+ setMcpConnecting(null);
2809
+ }
2810
+ }
2811
+ async function handleMcpDisconnect() {
2812
+ if (!confirmMcpDisconnect) return;
2813
+ setMcpDisconnecting(true);
2814
+ try {
2815
+ await client.customConnectors.delete(agentId, confirmMcpDisconnect.mcp_server_id);
2816
+ setConfirmMcpDisconnect(null);
2817
+ await loadMcp();
2818
+ onChanged?.();
2819
+ } finally {
2820
+ setMcpDisconnecting(false);
2821
+ }
2822
+ }
2823
+ async function handleComposioOauth(slug) {
2824
+ try {
2825
+ if (composioOauthHandlerRef.current) {
2826
+ window.removeEventListener("message", composioOauthHandlerRef.current);
2827
+ composioOauthHandlerRef.current = null;
2828
+ }
2829
+ const popup = window.open("about:blank", "composio-oauth", "width=600,height=700");
2830
+ const data = await client.connectors.initiateOauth(agentId, slug);
2831
+ if (data.redirect_url && popup) {
2832
+ popup.location.href = data.redirect_url;
2833
+ const handler = (event) => {
2834
+ if (event.origin !== window.location.origin) return;
2835
+ if (event.data?.type === "agent_plane_composio_oauth_callback" || event.data?.type === "agent_plane_oauth_callback") {
2836
+ popup.close();
2837
+ window.removeEventListener("message", handler);
2838
+ composioOauthHandlerRef.current = null;
2839
+ loadComposio();
2840
+ onChanged?.();
2841
+ }
2842
+ };
2843
+ composioOauthHandlerRef.current = handler;
2844
+ window.addEventListener("message", handler);
2845
+ } else {
2846
+ popup?.close();
2847
+ }
2848
+ } catch {
2849
+ }
2850
+ }
2851
+ const connectedMcpServerIds = new Set(mcpConnections.map((c) => c.mcp_server_id));
2852
+ const availableMcpServers = mcpServers.filter((s) => !connectedMcpServerIds.has(s.id));
2853
+ const isAllLoading = loading || mcpLoading;
2854
+ const isEmpty = localToolkits.length === 0 && mcpConnections.length === 0;
2855
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2856
+ /* @__PURE__ */ jsxs(
2857
+ ConfirmDialog,
2858
+ {
2859
+ open: !!confirmDelete,
2860
+ onOpenChange: (open) => {
2861
+ if (!open) setConfirmDelete(null);
2862
+ },
2863
+ title: "Remove Connector",
2864
+ confirmLabel: "Remove",
2865
+ loadingLabel: "Removing...",
2866
+ loading: deleting,
2867
+ onConfirm: handleConfirmDelete,
2868
+ children: [
2869
+ "Remove ",
2870
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: confirmDelete?.name }),
2871
+ " from this agent?"
2872
+ ]
2873
+ }
2874
+ ),
2875
+ /* @__PURE__ */ jsxs(
2876
+ ConfirmDialog,
2877
+ {
2878
+ open: !!confirmMcpDisconnect,
2879
+ onOpenChange: (open) => {
2880
+ if (!open) setConfirmMcpDisconnect(null);
2881
+ },
2882
+ title: "Disconnect Connector",
2883
+ confirmLabel: "Disconnect",
2884
+ loadingLabel: "Disconnecting...",
2885
+ loading: mcpDisconnecting,
2886
+ onConfirm: handleMcpDisconnect,
2887
+ children: [
2888
+ "Disconnect ",
2889
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: confirmMcpDisconnect?.server_name }),
2890
+ " from this agent?"
2891
+ ]
2892
+ }
2893
+ ),
2894
+ /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-muted-foreground/25 p-5", children: [
2895
+ /* @__PURE__ */ jsx(SectionHeader, { title: "Connectors", children: /* @__PURE__ */ jsx(
2896
+ Button,
2897
+ {
2898
+ size: "sm",
2899
+ variant: "outline",
2900
+ onClick: () => {
2901
+ setPendingToolkits(localToolkits);
2902
+ loadMcpServers();
2903
+ setShowAdd(true);
2904
+ },
2905
+ children: "Add"
2906
+ }
2907
+ ) }),
2908
+ /* @__PURE__ */ jsxs("div", { children: [
2909
+ showAdd && /* @__PURE__ */ jsxs("div", { className: "mb-4 space-y-3", children: [
2910
+ /* @__PURE__ */ jsx(ToolkitMultiselect, { value: pendingToolkits, onChange: setPendingToolkits }),
2911
+ availableMcpServers.length > 0 && /* @__PURE__ */ jsx("div", { className: "grid grid-cols-4 gap-2", children: availableMcpServers.map((s) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 p-2 rounded border border-border", children: [
2912
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
2913
+ s.logo_url && /* @__PURE__ */ jsx("img", { src: s.logo_url, alt: "", className: "w-5 h-5 rounded-sm object-contain flex-shrink-0" }),
2914
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium truncate", children: s.name }),
2915
+ /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "text-xs flex-shrink-0 ml-auto", children: s.slug })
2916
+ ] }),
2917
+ s.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground truncate", children: s.description }),
2918
+ /* @__PURE__ */ jsx(
2919
+ Button,
2920
+ {
2921
+ size: "sm",
2922
+ variant: "outline",
2923
+ className: "h-7 text-xs mt-auto",
2924
+ disabled: mcpConnecting === s.id,
2925
+ onClick: () => handleMcpConnect(s.id),
2926
+ children: mcpConnecting === s.id ? "Connecting..." : "Connect"
2927
+ }
2928
+ )
2929
+ ] }, s.id)) }),
2930
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
2931
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: () => setShowAdd(false), children: "Cancel" }),
2932
+ /* @__PURE__ */ jsx(Button, { size: "sm", onClick: handleApplyAdd, disabled: applyingToolkits, children: applyingToolkits ? "Saving..." : "Apply" })
2933
+ ] })
2934
+ ] }),
2935
+ isAllLoading ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Loading..." }) : isEmpty ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "No connectors added. Click Add to configure connectors." }) : /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-3", children: [
2936
+ connectors.map((c) => /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-border p-3 flex flex-col gap-2", children: [
2937
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2938
+ c.logo && /* @__PURE__ */ jsx("img", { src: c.logo, alt: "", className: "w-5 h-5 rounded-sm object-contain flex-shrink-0" }),
2939
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium truncate flex-1", children: c.name }),
2940
+ /* @__PURE__ */ jsx(Badge, { variant: schemeBadgeVariant(c.authScheme ?? c.auth_scheme), className: "text-xs flex-shrink-0", children: c.authScheme ?? c.auth_scheme }),
2941
+ /* @__PURE__ */ jsx(
2942
+ "button",
2943
+ {
2944
+ type: "button",
2945
+ onClick: () => setConfirmDelete({ slug: c.slug, name: c.name }),
2946
+ className: "text-muted-foreground hover:text-destructive flex-shrink-0 ml-1 text-base leading-none",
2947
+ title: "Remove connector",
2948
+ children: "\xD7"
2949
+ }
2950
+ )
2951
+ ] }),
2952
+ (c.authScheme ?? c.auth_scheme) === "NO_AUTH" ? /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "No auth required" }) : c.connectionStatus === "ACTIVE" || c.connected ? /* @__PURE__ */ jsx("span", { className: `text-xs font-medium ${statusColor(c.connectionStatus ?? (c.connected ? "ACTIVE" : null))}`, children: "Connected" }) : c.connectionStatus ? /* @__PURE__ */ jsx("span", { className: `text-xs ${statusColor(c.connectionStatus)}`, children: c.connectionStatus.toLowerCase() }) : null,
2953
+ (() => {
2954
+ const total = toolCounts[c.slug];
2955
+ if (total === void 0) return null;
2956
+ const prefix = c.slug.toUpperCase() + "_";
2957
+ const filtered = allowedTools.filter((t) => t.startsWith(prefix));
2958
+ const hasFilter = filtered.length > 0;
2959
+ return /* @__PURE__ */ jsx(
2960
+ "button",
2961
+ {
2962
+ type: "button",
2963
+ className: "text-xs text-primary hover:underline text-left",
2964
+ onClick: () => setToolsModalToolkit(c.slug),
2965
+ children: hasFilter ? `${filtered.length} / ${total} tools` : `All tools (${total})`
2966
+ }
2967
+ );
2968
+ })(),
2969
+ (c.authScheme ?? c.auth_scheme) === "API_KEY" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 mt-auto", children: [
2970
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2971
+ /* @__PURE__ */ jsx(
2972
+ Input,
2973
+ {
2974
+ type: "password",
2975
+ placeholder: c.connected || c.connectionStatus === "ACTIVE" ? "Update API key\u2026" : "Enter API key\u2026",
2976
+ value: apiKeys[c.slug] ?? "",
2977
+ onChange: (e) => setApiKeys((k) => ({ ...k, [c.slug]: e.target.value })),
2978
+ className: "h-7 text-xs flex-1 min-w-0"
2979
+ }
2980
+ ),
2981
+ /* @__PURE__ */ jsx(
2982
+ Button,
2983
+ {
2984
+ size: "sm",
2985
+ variant: "outline",
2986
+ className: "h-7 text-xs flex-shrink-0",
2987
+ disabled: !apiKeys[c.slug] || saving[c.slug],
2988
+ onClick: () => handleSaveKey(c.slug),
2989
+ children: saving[c.slug] ? "Saving\u2026" : "Save"
2990
+ }
2991
+ )
2992
+ ] }),
2993
+ /* @__PURE__ */ jsx(FormError, { error: errors[c.slug] })
2994
+ ] }),
2995
+ ((c.authScheme ?? c.auth_scheme) === "OAUTH2" || (c.authScheme ?? c.auth_scheme) === "OAUTH1") && c.connectionStatus !== "ACTIVE" && !c.connected && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "outline", className: "h-7 text-xs w-full mt-auto", onClick: () => handleComposioOauth(c.slug), children: "Connect" }),
2996
+ ((c.authScheme ?? c.auth_scheme) === "OAUTH2" || (c.authScheme ?? c.auth_scheme) === "OAUTH1") && (c.connectionStatus === "ACTIVE" || c.connected) && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", className: "h-7 text-xs text-muted-foreground w-full mt-auto", onClick: () => handleComposioOauth(c.slug), children: "Reconnect" })
2997
+ ] }, `composio-${c.slug}`)),
2998
+ mcpConnections.map((c) => /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-border p-3 flex flex-col gap-2", children: [
2999
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3000
+ c.server_logo_url && /* @__PURE__ */ jsx("img", { src: c.server_logo_url, alt: "", className: "w-5 h-5 rounded-sm object-contain flex-shrink-0" }),
3001
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium truncate flex-1", children: c.server_name }),
3002
+ /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "text-xs flex-shrink-0", children: c.server_slug }),
3003
+ /* @__PURE__ */ jsx(
3004
+ "button",
3005
+ {
3006
+ type: "button",
3007
+ onClick: () => setConfirmMcpDisconnect(c),
3008
+ className: "text-muted-foreground hover:text-destructive flex-shrink-0 ml-1 text-base leading-none",
3009
+ title: "Disconnect",
3010
+ children: "\xD7"
3011
+ }
3012
+ )
3013
+ ] }),
3014
+ c.status === "active" ? /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-green-500", children: "Connected" }) : /* @__PURE__ */ jsx("span", { className: `text-xs ${c.status === "expired" || c.status === "failed" ? "text-destructive" : "text-muted-foreground"}`, children: c.status }),
3015
+ c.status === "active" && /* @__PURE__ */ jsx(
3016
+ "button",
3017
+ {
3018
+ type: "button",
3019
+ className: "text-xs text-primary hover:underline text-left",
3020
+ onClick: () => setMcpToolsModal(c),
3021
+ children: c.allowed_tools.length > 0 ? `${c.allowed_tools.length} tools selected` : "All tools (no filter)"
3022
+ }
3023
+ ),
3024
+ (c.status === "expired" || c.status === "failed") && /* @__PURE__ */ jsx(
3025
+ Button,
3026
+ {
3027
+ size: "sm",
3028
+ variant: "ghost",
3029
+ className: "h-7 text-xs text-muted-foreground mt-auto",
3030
+ disabled: mcpConnecting === c.mcp_server_id,
3031
+ onClick: () => handleMcpConnect(c.mcp_server_id),
3032
+ children: mcpConnecting === c.mcp_server_id ? "Reconnecting..." : "Reconnect"
3033
+ }
3034
+ )
3035
+ ] }, `mcp-${c.id}`))
3036
+ ] })
3037
+ ] })
3038
+ ] }),
3039
+ toolsModalToolkit && /* @__PURE__ */ jsx(
3040
+ ToolsModal,
3041
+ {
3042
+ toolkit: toolsModalToolkit,
3043
+ toolkitLogo: connectors.find((c) => c.slug === toolsModalToolkit)?.logo,
3044
+ allowedTools,
3045
+ open: !!toolsModalToolkit,
3046
+ onOpenChange: (open) => {
3047
+ if (!open) setToolsModalToolkit(null);
3048
+ },
3049
+ onSave: handleToolsSave
3050
+ }
3051
+ ),
3052
+ mcpToolsModal && /* @__PURE__ */ jsx(
3053
+ McpToolsModal,
3054
+ {
3055
+ agentId,
3056
+ mcpServerId: mcpToolsModal.mcp_server_id,
3057
+ serverName: mcpToolsModal.server_name,
3058
+ serverLogo: mcpToolsModal.server_logo_url,
3059
+ allowedTools: mcpToolsModal.allowed_tools,
3060
+ open: !!mcpToolsModal,
3061
+ onOpenChange: (open) => {
3062
+ if (!open) setMcpToolsModal(null);
3063
+ },
3064
+ onSave: async (selectedTools) => {
3065
+ await client.customConnectors.updateAllowedTools(agentId, mcpToolsModal.mcp_server_id, selectedTools);
3066
+ setMcpToolsModal(null);
3067
+ await loadMcp();
3068
+ }
3069
+ }
3070
+ )
3071
+ ] });
3072
+ }
3073
+ function AgentSkillManager({ agentId, initialSkills, onSaved }) {
3074
+ const client = useAgentPlaneClient();
3075
+ const initialFiles = useMemo(
3076
+ () => initialSkills.flatMap(
3077
+ (s) => s.files.map((f) => ({
3078
+ path: s.folder === "(root)" ? f.path : `${s.folder}/${f.path}`,
3079
+ content: f.content
3080
+ }))
3081
+ ),
3082
+ [initialSkills]
3083
+ );
3084
+ const [files, setFiles] = useState(initialFiles);
3085
+ const [selectedPath, setSelectedPath] = useState(files[0]?.path ?? null);
3086
+ const [saving, setSaving] = useState(false);
3087
+ const [addingFile, setAddingFile] = useState(false);
3088
+ const [newFileName, setNewFileName] = useState("");
3089
+ const isDirty = useMemo(
3090
+ () => JSON.stringify(files) !== JSON.stringify(initialFiles),
3091
+ [files, initialFiles]
3092
+ );
3093
+ const selectedFile = files.find((f) => f.path === selectedPath);
3094
+ function updateFileContent(path, content) {
3095
+ setFiles((prev) => prev.map((f) => f.path === path ? { ...f, content } : f));
3096
+ }
3097
+ function addFile() {
3098
+ const name = newFileName.trim();
3099
+ if (!name || files.some((f) => f.path === name)) return;
3100
+ const content = name.endsWith(".md") ? "---\nname: New Skill\ndescription: Describe when this skill should be triggered\n---\n\n# Instructions\n\nDescribe what this skill does...\n" : "";
3101
+ setFiles((prev) => [...prev, { path: name, content }]);
3102
+ setSelectedPath(name);
3103
+ setAddingFile(false);
3104
+ setNewFileName("");
3105
+ }
3106
+ function removeFile(path) {
3107
+ setFiles((prev) => prev.filter((f) => f.path !== path));
3108
+ if (selectedPath === path) {
3109
+ setSelectedPath(files.find((f) => f.path !== path)?.path ?? null);
3110
+ }
3111
+ }
3112
+ const handleSave = useCallback(async () => {
3113
+ setSaving(true);
3114
+ try {
3115
+ const folderMap = /* @__PURE__ */ new Map();
3116
+ for (const file of files) {
3117
+ const slashIdx = file.path.lastIndexOf("/");
3118
+ if (slashIdx === -1) {
3119
+ const existing = folderMap.get("(root)") ?? [];
3120
+ existing.push({ path: file.path, content: file.content });
3121
+ folderMap.set("(root)", existing);
3122
+ } else {
3123
+ const folder = file.path.slice(0, slashIdx);
3124
+ const fileName = file.path.slice(slashIdx + 1);
3125
+ const existing = folderMap.get(folder) ?? [];
3126
+ existing.push({ path: fileName, content: file.content });
3127
+ folderMap.set(folder, existing);
3128
+ }
3129
+ }
3130
+ const skills = Array.from(folderMap.entries()).map(([folder, files2]) => ({ folder, files: files2 }));
3131
+ await client.agents.update(agentId, { skills });
3132
+ onSaved?.();
3133
+ } finally {
3134
+ setSaving(false);
3135
+ }
3136
+ }, [files, agentId, client, onSaved]);
3137
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-muted-foreground/25 p-5", children: [
3138
+ /* @__PURE__ */ jsx(SectionHeader, { title: "Skills", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3139
+ isDirty && /* @__PURE__ */ jsx(Badge, { variant: "destructive", className: "text-xs", children: "Unsaved" }),
3140
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "outline", onClick: () => setAddingFile(true), children: "Add File" }),
3141
+ /* @__PURE__ */ jsx(Button, { size: "sm", onClick: handleSave, disabled: saving || !isDirty, children: saving ? "Saving..." : "Save Skills" })
3142
+ ] }) }),
3143
+ addingFile && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-3", children: [
3144
+ /* @__PURE__ */ jsx(
3145
+ Input,
3146
+ {
3147
+ value: newFileName,
3148
+ onChange: (e) => setNewFileName(e.target.value),
3149
+ placeholder: "folder/SKILL.md",
3150
+ className: "max-w-xs text-sm",
3151
+ onKeyDown: (e) => e.key === "Enter" && addFile()
3152
+ }
3153
+ ),
3154
+ /* @__PURE__ */ jsx(Button, { size: "sm", onClick: addFile, children: "Add" }),
3155
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: () => {
3156
+ setAddingFile(false);
3157
+ setNewFileName("");
3158
+ }, children: "Cancel" })
3159
+ ] }),
3160
+ files.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: 'No skills defined. Click "Add File" to create a skill.' }) : /* @__PURE__ */ jsxs("div", { className: "flex gap-4 min-h-[300px]", children: [
3161
+ /* @__PURE__ */ jsx("div", { className: "w-48 shrink-0 border-r border-border pr-3 space-y-1", children: files.map((f) => /* @__PURE__ */ jsxs(
3162
+ "div",
3163
+ {
3164
+ className: `flex items-center justify-between group rounded px-2 py-1 text-xs cursor-pointer ${selectedPath === f.path ? "bg-accent text-accent-foreground" : "hover:bg-muted/50"}`,
3165
+ onClick: () => setSelectedPath(f.path),
3166
+ children: [
3167
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: f.path }),
3168
+ /* @__PURE__ */ jsx(
3169
+ "button",
3170
+ {
3171
+ type: "button",
3172
+ onClick: (e) => {
3173
+ e.stopPropagation();
3174
+ removeFile(f.path);
3175
+ },
3176
+ className: "opacity-0 group-hover:opacity-100 text-muted-foreground hover:text-destructive ml-1",
3177
+ children: "\xD7"
3178
+ }
3179
+ )
3180
+ ]
3181
+ },
3182
+ f.path
3183
+ )) }),
3184
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: selectedFile ? /* @__PURE__ */ jsx(
3185
+ Textarea,
3186
+ {
3187
+ value: selectedFile.content,
3188
+ onChange: (e) => updateFileContent(selectedFile.path, e.target.value),
3189
+ className: "h-full min-h-[300px] font-mono text-xs resize-y",
3190
+ placeholder: "Enter skill content..."
3191
+ }
3192
+ ) : /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Select a file to edit" }) })
3193
+ ] })
3194
+ ] });
3195
+ }
3196
+ function AgentPluginManager({ agentId, initialPlugins, onSaved }) {
3197
+ const client = useAgentPlaneClient();
3198
+ const [plugins, setPlugins] = useState(initialPlugins);
3199
+ const [saving, setSaving] = useState(false);
3200
+ const [dialogOpen, setDialogOpen] = useState(false);
3201
+ const [marketplaces, setMarketplaces] = useState([]);
3202
+ const [selectedMarketplace, setSelectedMarketplace] = useState(null);
3203
+ const [availablePlugins, setAvailablePlugins] = useState([]);
3204
+ const [loadingPlugins, setLoadingPlugins] = useState(false);
3205
+ const [marketplaceNames, setMarketplaceNames] = useState({});
3206
+ const savedSnapshot = useRef(JSON.stringify(initialPlugins));
3207
+ useEffect(() => {
3208
+ savedSnapshot.current = JSON.stringify(initialPlugins);
3209
+ setPlugins(initialPlugins);
3210
+ }, [initialPlugins]);
3211
+ const isDirty = useMemo(
3212
+ () => JSON.stringify(plugins) !== savedSnapshot.current,
3213
+ [plugins]
3214
+ );
3215
+ useEffect(() => {
3216
+ client.pluginMarketplaces.list().then((data) => {
3217
+ setMarketplaces(data);
3218
+ const names = {};
3219
+ for (const m of data) names[m.id] = m.name;
3220
+ setMarketplaceNames(names);
3221
+ }).catch(() => {
3222
+ });
3223
+ }, [client]);
3224
+ const loadPluginsForMarketplace = useCallback(async (marketplaceId) => {
3225
+ setSelectedMarketplace(marketplaceId);
3226
+ setLoadingPlugins(true);
3227
+ setAvailablePlugins([]);
3228
+ try {
3229
+ const data = await client.pluginMarketplaces.listPlugins(marketplaceId);
3230
+ setAvailablePlugins(data ?? []);
3231
+ } catch {
3232
+ } finally {
3233
+ setLoadingPlugins(false);
3234
+ }
3235
+ }, [client]);
3236
+ function isPluginEnabled(marketplaceId, pluginName) {
3237
+ return plugins.some(
3238
+ (p) => p.marketplace_id === marketplaceId && p.plugin_name === pluginName
3239
+ );
3240
+ }
3241
+ function togglePlugin(marketplaceId, pluginName) {
3242
+ if (isPluginEnabled(marketplaceId, pluginName)) {
3243
+ setPlugins(
3244
+ (prev) => prev.filter(
3245
+ (p) => !(p.marketplace_id === marketplaceId && p.plugin_name === pluginName)
3246
+ )
3247
+ );
3248
+ } else {
3249
+ setPlugins((prev) => [
3250
+ ...prev,
3251
+ { marketplace_id: marketplaceId, plugin_name: pluginName }
3252
+ ]);
3253
+ }
3254
+ }
3255
+ function removePlugin(index) {
3256
+ setPlugins((prev) => prev.filter((_, i) => i !== index));
3257
+ }
3258
+ async function handleSave() {
3259
+ setSaving(true);
3260
+ try {
3261
+ await client.agents.update(agentId, { plugins });
3262
+ savedSnapshot.current = JSON.stringify(plugins);
3263
+ onSaved?.();
3264
+ } finally {
3265
+ setSaving(false);
3266
+ }
3267
+ }
3268
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-muted-foreground/25 p-5", children: [
3269
+ /* @__PURE__ */ jsx(SectionHeader, { title: "Plugins", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
3270
+ isDirty && /* @__PURE__ */ jsx(Badge, { variant: "destructive", className: "text-xs", children: "Unsaved changes" }),
3271
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
3272
+ /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: () => setDialogOpen(true), children: "Add Plugins" }),
3273
+ /* @__PURE__ */ jsx(Button, { onClick: handleSave, disabled: saving || !isDirty, size: "sm", children: saving ? "Saving..." : "Save Plugins" })
3274
+ ] })
3275
+ ] }) }),
3276
+ /* @__PURE__ */ jsxs("div", { children: [
3277
+ plugins.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: 'No plugins enabled. Click "Add Plugins" to browse available plugins.' }) : /* @__PURE__ */ jsx("div", { className: "space-y-2", children: plugins.map((p, i) => /* @__PURE__ */ jsxs(
3278
+ "div",
3279
+ {
3280
+ className: "flex items-center justify-between rounded-md border border-border px-3 py-2",
3281
+ children: [
3282
+ /* @__PURE__ */ jsxs("div", { children: [
3283
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: p.plugin_name }),
3284
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground ml-2", children: [
3285
+ "from ",
3286
+ marketplaceNames[p.marketplace_id] ?? p.marketplace_id.slice(0, 8)
3287
+ ] })
3288
+ ] }),
3289
+ /* @__PURE__ */ jsx(
3290
+ Button,
3291
+ {
3292
+ variant: "ghost",
3293
+ size: "sm",
3294
+ className: "text-xs text-muted-foreground hover:text-destructive",
3295
+ onClick: () => removePlugin(i),
3296
+ children: "Remove"
3297
+ }
3298
+ )
3299
+ ]
3300
+ },
3301
+ `${p.marketplace_id}:${p.plugin_name}`
3302
+ )) }),
3303
+ /* @__PURE__ */ jsx(Dialog, { open: dialogOpen, onOpenChange: setDialogOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-w-lg max-h-[80vh] flex flex-col", children: [
3304
+ /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: "Add Plugins" }) }),
3305
+ /* @__PURE__ */ jsx(DialogBody, { className: "flex-1 overflow-hidden flex flex-col gap-3", children: marketplaces.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "No plugin marketplaces registered. Add one from the Plugin Marketplaces page first." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
3306
+ /* @__PURE__ */ jsx("div", { className: "flex gap-2 flex-wrap", children: marketplaces.map((m) => /* @__PURE__ */ jsx(
3307
+ Button,
3308
+ {
3309
+ size: "sm",
3310
+ variant: selectedMarketplace === m.id ? "default" : "outline",
3311
+ onClick: () => loadPluginsForMarketplace(m.id),
3312
+ children: m.name
3313
+ },
3314
+ m.id
3315
+ )) }),
3316
+ selectedMarketplace && /* @__PURE__ */ jsx("div", { className: "overflow-y-auto border border-border rounded-lg divide-y divide-border", children: loadingPlugins ? /* @__PURE__ */ jsx("p", { className: "p-4 text-sm text-muted-foreground text-center", children: "Loading plugins..." }) : availablePlugins.length === 0 ? /* @__PURE__ */ jsx("p", { className: "p-4 text-sm text-muted-foreground text-center", children: "No plugins found in this marketplace." }) : availablePlugins.map((ap) => {
3317
+ const enabled = isPluginEnabled(selectedMarketplace, ap.name);
3318
+ return /* @__PURE__ */ jsxs(
3319
+ "div",
3320
+ {
3321
+ className: "flex items-center justify-between px-3 py-2.5 hover:bg-muted/30 cursor-pointer transition-colors",
3322
+ onClick: () => togglePlugin(selectedMarketplace, ap.name),
3323
+ children: [
3324
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
3325
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3326
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: ap.displayName }),
3327
+ ap.version && /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
3328
+ "v",
3329
+ ap.version
3330
+ ] })
3331
+ ] }),
3332
+ ap.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground truncate", children: ap.description }),
3333
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-1 mt-1", children: [
3334
+ ap.hasAgents && /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-[10px] px-1 py-0", children: "Agents" }),
3335
+ ap.hasSkills && /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-[10px] px-1 py-0", children: "Skills" }),
3336
+ ap.hasMcpJson && /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-[10px] px-1 py-0", children: "MCP" })
3337
+ ] })
3338
+ ] }),
3339
+ /* @__PURE__ */ jsx("div", { className: `w-4 h-4 rounded border flex items-center justify-center shrink-0 ${enabled ? "bg-primary border-primary text-primary-foreground" : "border-muted-foreground"}`, children: enabled && /* @__PURE__ */ jsx("span", { className: "text-xs", children: "\u2713" }) })
3340
+ ]
3341
+ },
3342
+ ap.name
3343
+ );
3344
+ }) })
3345
+ ] }) }),
3346
+ /* @__PURE__ */ jsx(DialogFooter, { children: /* @__PURE__ */ jsx(Button, { size: "sm", onClick: () => setDialogOpen(false), children: "Done" }) })
3347
+ ] }) })
3348
+ ] })
3349
+ ] });
3350
+ }
3351
+ var FREQUENCIES = [
3352
+ { value: "manual", label: "Manual (no schedule)" },
3353
+ { value: "hourly", label: "Hourly" },
3354
+ { value: "daily", label: "Daily" },
3355
+ { value: "weekdays", label: "Weekdays (Mon-Fri)" },
3356
+ { value: "weekly", label: "Weekly" }
3357
+ ];
3358
+ var DAYS_OF_WEEK = [
3359
+ { value: 0, label: "Sunday" },
3360
+ { value: 1, label: "Monday" },
3361
+ { value: 2, label: "Tuesday" },
3362
+ { value: 3, label: "Wednesday" },
3363
+ { value: 4, label: "Thursday" },
3364
+ { value: 5, label: "Friday" },
3365
+ { value: 6, label: "Saturday" }
3366
+ ];
3367
+ function formatTimeForInput(time) {
3368
+ if (!time) return "09:00";
3369
+ return time.slice(0, 5);
3370
+ }
3371
+ function AgentScheduleForm({ initialSchedules, timezone }) {
3372
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-muted-foreground/25 p-5", children: [
3373
+ /* @__PURE__ */ jsx(SectionHeader, { title: "Schedules", children: /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
3374
+ "Timezone: ",
3375
+ timezone
3376
+ ] }) }),
3377
+ initialSchedules.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground py-4", children: "No schedules configured." }) : /* @__PURE__ */ jsx("div", { className: "space-y-4", children: initialSchedules.map((schedule) => /* @__PURE__ */ jsx(
3378
+ ScheduleCard,
3379
+ {
3380
+ schedule,
3381
+ timezone
3382
+ },
3383
+ schedule.id
3384
+ )) })
3385
+ ] });
3386
+ }
3387
+ function ScheduleCard({
3388
+ schedule,
3389
+ timezone
3390
+ }) {
3391
+ const [frequency, setFrequency] = useState(schedule.frequency);
3392
+ const [time, setTime] = useState(formatTimeForInput(schedule.time));
3393
+ const [dayOfWeek, setDayOfWeek] = useState(schedule.day_of_week ?? 1);
3394
+ const [prompt, setPrompt] = useState(schedule.prompt ?? "");
3395
+ const [enabled, setEnabled] = useState(schedule.enabled);
3396
+ const [name, setName] = useState(schedule.name ?? "");
3397
+ const showTimePicker = ["daily", "weekdays", "weekly"].includes(frequency);
3398
+ const showDayPicker = frequency === "weekly";
3399
+ const canEnable = frequency !== "manual";
3400
+ return /* @__PURE__ */ jsxs("div", { className: "rounded border border-muted-foreground/15 p-4 space-y-3", children: [
3401
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
3402
+ /* @__PURE__ */ jsx(
3403
+ Input,
3404
+ {
3405
+ value: name,
3406
+ onChange: (e) => setName(e.target.value),
3407
+ placeholder: "Schedule name (optional)",
3408
+ className: "max-w-xs text-sm"
3409
+ }
3410
+ ),
3411
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: canEnable && /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 cursor-pointer", children: [
3412
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: enabled ? "Enabled" : "Disabled" }),
3413
+ /* @__PURE__ */ jsx(
3414
+ "button",
3415
+ {
3416
+ type: "button",
3417
+ role: "switch",
3418
+ "aria-checked": enabled,
3419
+ onClick: () => setEnabled(!enabled),
3420
+ className: `relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring ${enabled ? "bg-primary" : "bg-muted"}`,
3421
+ children: /* @__PURE__ */ jsx(
3422
+ "span",
3423
+ {
3424
+ className: `pointer-events-none inline-block h-4 w-4 transform rounded-full bg-background shadow-lg ring-0 transition-transform ${enabled ? "translate-x-4" : "translate-x-0"}`
3425
+ }
3426
+ )
3427
+ }
3428
+ )
3429
+ ] }) })
3430
+ ] }),
3431
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
3432
+ /* @__PURE__ */ jsx(FormField, { label: "Frequency", children: /* @__PURE__ */ jsx(
3433
+ Select,
3434
+ {
3435
+ value: frequency,
3436
+ onChange: (e) => {
3437
+ const newFreq = e.target.value;
3438
+ setFrequency(newFreq);
3439
+ if (newFreq === "manual") setEnabled(false);
3440
+ else setEnabled(true);
3441
+ },
3442
+ children: FREQUENCIES.map((f) => /* @__PURE__ */ jsx("option", { value: f.value, children: f.label }, f.value))
3443
+ }
3444
+ ) }),
3445
+ showTimePicker && /* @__PURE__ */ jsx(FormField, { label: `Time (${timezone})`, children: /* @__PURE__ */ jsx(
3446
+ Input,
3447
+ {
3448
+ type: "time",
3449
+ value: time,
3450
+ onChange: (e) => setTime(e.target.value)
3451
+ }
3452
+ ) }),
3453
+ showDayPicker && /* @__PURE__ */ jsx(FormField, { label: "Day of Week", children: /* @__PURE__ */ jsx(
3454
+ Select,
3455
+ {
3456
+ value: dayOfWeek.toString(),
3457
+ onChange: (e) => setDayOfWeek(parseInt(e.target.value)),
3458
+ children: DAYS_OF_WEEK.map((d) => /* @__PURE__ */ jsx("option", { value: d.value, children: d.label }, d.value))
3459
+ }
3460
+ ) })
3461
+ ] }),
3462
+ frequency !== "manual" && /* @__PURE__ */ jsx(FormField, { label: "Prompt", children: /* @__PURE__ */ jsx(
3463
+ Textarea,
3464
+ {
3465
+ value: prompt,
3466
+ onChange: (e) => setPrompt(e.target.value),
3467
+ rows: 3,
3468
+ placeholder: "Enter the prompt to send on each scheduled run...",
3469
+ className: "resize-y min-h-[60px]"
3470
+ }
3471
+ ) }),
3472
+ (schedule.last_run_at || schedule.next_run_at) && /* @__PURE__ */ jsxs("div", { className: "flex gap-6 text-sm text-muted-foreground pt-1", children: [
3473
+ schedule.last_run_at && /* @__PURE__ */ jsxs("div", { children: [
3474
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Last run:" }),
3475
+ " ",
3476
+ /* @__PURE__ */ jsx(LocalDate, { value: schedule.last_run_at })
3477
+ ] }),
3478
+ schedule.next_run_at && /* @__PURE__ */ jsxs("div", { children: [
3479
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Next run:" }),
3480
+ " ",
3481
+ /* @__PURE__ */ jsx(LocalDate, { value: schedule.next_run_at })
3482
+ ] })
3483
+ ] })
3484
+ ] });
3485
+ }
3486
+ function AgentRuns({ agentId }) {
3487
+ const { LinkComponent, basePath } = useNavigation();
3488
+ const { data, error, isLoading } = useApi(
3489
+ `agent-runs-${agentId}`,
3490
+ (client) => client.runs.list({ agent_id: agentId, limit: 50 })
3491
+ );
3492
+ const runs = data?.data ?? [];
3493
+ if (isLoading) {
3494
+ return /* @__PURE__ */ jsx("div", { className: "space-y-2", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-full" }, i)) });
3495
+ }
3496
+ if (error) {
3497
+ return /* @__PURE__ */ jsxs("div", { className: "text-destructive text-sm py-4 text-center", children: [
3498
+ "Failed to load runs: ",
3499
+ error.message
3500
+ ] });
3501
+ }
3502
+ return /* @__PURE__ */ jsxs(AdminTable, { children: [
3503
+ /* @__PURE__ */ jsxs(AdminTableHead, { children: [
3504
+ /* @__PURE__ */ jsx(Th, { children: "Run" }),
3505
+ /* @__PURE__ */ jsx(Th, { children: "Status" }),
3506
+ /* @__PURE__ */ jsx(Th, { children: "Source" }),
3507
+ /* @__PURE__ */ jsx(Th, { className: "max-w-xs", children: "Prompt" }),
3508
+ /* @__PURE__ */ jsx(Th, { align: "right", children: "Cost" }),
3509
+ /* @__PURE__ */ jsx(Th, { align: "right", children: "Turns" }),
3510
+ /* @__PURE__ */ jsx(Th, { align: "right", children: "Duration" }),
3511
+ /* @__PURE__ */ jsx(Th, { children: "Created" })
3512
+ ] }),
3513
+ /* @__PURE__ */ jsxs("tbody", { children: [
3514
+ runs.map((r) => /* @__PURE__ */ jsxs(AdminTableRow, { children: [
3515
+ /* @__PURE__ */ jsx("td", { className: "p-3 font-mono text-xs", children: /* @__PURE__ */ jsxs(LinkComponent, { href: `${basePath}/runs/${r.id}`, className: "text-primary hover:underline", children: [
3516
+ r.id.slice(0, 8),
3517
+ "..."
3518
+ ] }) }),
3519
+ /* @__PURE__ */ jsx("td", { className: "p-3", children: /* @__PURE__ */ jsx(RunStatusBadge, { status: r.status }) }),
3520
+ /* @__PURE__ */ jsx("td", { className: "p-3", children: /* @__PURE__ */ jsx(RunSourceBadge, { triggeredBy: r.triggered_by }) }),
3521
+ /* @__PURE__ */ jsxs("td", { className: "p-3 max-w-xs truncate text-muted-foreground text-xs", title: r.prompt, children: [
3522
+ r.prompt.slice(0, 80),
3523
+ r.prompt.length > 80 ? "..." : ""
3524
+ ] }),
3525
+ /* @__PURE__ */ jsxs("td", { className: "p-3 text-right font-mono", children: [
3526
+ "$",
3527
+ r.cost_usd.toFixed(4)
3528
+ ] }),
3529
+ /* @__PURE__ */ jsx("td", { className: "p-3 text-right", children: r.num_turns }),
3530
+ /* @__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" }),
3531
+ /* @__PURE__ */ jsx("td", { className: "p-3 text-muted-foreground text-xs", children: /* @__PURE__ */ jsx(LocalDate, { value: r.created_at }) })
3532
+ ] }, r.id)),
3533
+ runs.length === 0 && /* @__PURE__ */ jsx(EmptyRow, { colSpan: 8, children: "No runs yet" })
3534
+ ] })
3535
+ ] });
3536
+ }
3537
+ function AgentA2aInfo({
3538
+ agentId,
3539
+ tenantSlug,
3540
+ agentSlug,
3541
+ baseUrl,
3542
+ initialEnabled,
3543
+ initialTags,
3544
+ onChanged
3545
+ }) {
3546
+ const client = useAgentPlaneClient();
3547
+ const [enabled, setEnabled] = useState(initialEnabled);
3548
+ const [toggling, setToggling] = useState(false);
3549
+ const [detailsOpen, setDetailsOpen] = useState(false);
3550
+ const [cardPreview, setCardPreview] = useState(null);
3551
+ const [loading, setLoading] = useState(false);
3552
+ const [tags, setTags] = useState(initialTags);
3553
+ const [tagInput, setTagInput] = useState("");
3554
+ const [savingTags, setSavingTags] = useState(false);
3555
+ const inputRef = useRef(null);
3556
+ const endpointUrl = `${baseUrl}/api/a2a/${tenantSlug}/${agentSlug}`;
3557
+ const jsonRpcUrl = `${baseUrl}/api/a2a/${tenantSlug}/${agentSlug}/jsonrpc`;
3558
+ const agentCardUrl = `${baseUrl}/api/a2a/${tenantSlug}/${agentSlug}/.well-known/agent-card.json`;
3559
+ async function toggleA2a() {
3560
+ setToggling(true);
3561
+ try {
3562
+ const next = !enabled;
3563
+ await client.agents.update(agentId, { a2a_enabled: next });
3564
+ setEnabled(next);
3565
+ onChanged?.();
3566
+ } finally {
3567
+ setToggling(false);
3568
+ }
3569
+ }
3570
+ async function saveTags(nextTags) {
3571
+ setSavingTags(true);
3572
+ try {
3573
+ await client.agents.update(agentId, { a2a_tags: nextTags });
3574
+ } finally {
3575
+ setSavingTags(false);
3576
+ }
3577
+ }
3578
+ function addTag(value) {
3579
+ const tag = value.trim();
3580
+ if (!tag || tags.includes(tag)) return;
3581
+ const next = [...tags, tag];
3582
+ setTags(next);
3583
+ setTagInput("");
3584
+ saveTags(next);
3585
+ }
3586
+ function removeTag(tag) {
3587
+ const next = tags.filter((t) => t !== tag);
3588
+ setTags(next);
3589
+ saveTags(next);
3590
+ }
3591
+ function onTagKeyDown(e) {
3592
+ if (e.key === "Enter" || e.key === ",") {
3593
+ e.preventDefault();
3594
+ addTag(tagInput);
3595
+ } else if (e.key === "Backspace" && tagInput === "" && tags.length > 0) {
3596
+ const lastTag = tags[tags.length - 1];
3597
+ if (lastTag) removeTag(lastTag);
3598
+ }
3599
+ }
3600
+ async function fetchAgentCard() {
3601
+ if (cardPreview) {
3602
+ setCardPreview(null);
3603
+ return;
3604
+ }
3605
+ setLoading(true);
3606
+ try {
3607
+ const res = await fetch(agentCardUrl);
3608
+ const data = await res.json();
3609
+ setCardPreview(JSON.stringify(data, null, 2));
3610
+ } catch {
3611
+ setCardPreview("Failed to fetch Agent Card");
3612
+ } finally {
3613
+ setLoading(false);
3614
+ }
3615
+ }
3616
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-muted-foreground/25 p-5", children: [
3617
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4", children: [
3618
+ /* @__PURE__ */ jsx(SectionHeader, { title: "A2A Protocol", className: "mb-0" }),
3619
+ /* @__PURE__ */ jsx(
3620
+ "button",
3621
+ {
3622
+ type: "button",
3623
+ role: "switch",
3624
+ "aria-checked": enabled,
3625
+ disabled: toggling,
3626
+ onClick: toggleA2a,
3627
+ className: `relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 ${enabled ? "bg-indigo-500" : "bg-muted-foreground/30"}`,
3628
+ children: /* @__PURE__ */ jsx(
3629
+ "span",
3630
+ {
3631
+ className: `pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow-lg ring-0 transition duration-200 ease-in-out ${enabled ? "translate-x-5" : "translate-x-0"}`
3632
+ }
3633
+ )
3634
+ }
3635
+ )
3636
+ ] }),
3637
+ enabled && /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
3638
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4 items-start", children: [
3639
+ /* @__PURE__ */ jsxs("div", { children: [
3640
+ /* @__PURE__ */ jsx("label", { className: "text-xs text-muted-foreground mb-1 block", children: "URL" }),
3641
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3642
+ /* @__PURE__ */ jsx("code", { className: "flex-1 rounded bg-muted px-3 py-1.5 text-xs font-mono text-foreground break-all", children: endpointUrl }),
3643
+ /* @__PURE__ */ jsx(CopyButton, { text: endpointUrl })
3644
+ ] })
3645
+ ] }),
3646
+ /* @__PURE__ */ jsxs("div", { children: [
3647
+ /* @__PURE__ */ jsx("label", { className: "text-xs text-muted-foreground mb-1 block", children: "Tags" }),
3648
+ /* @__PURE__ */ jsxs(
3649
+ "div",
3650
+ {
3651
+ className: "flex flex-wrap items-center gap-1.5 rounded border border-input bg-muted px-2 py-1.5 min-h-[32px] cursor-text",
3652
+ onClick: () => inputRef.current?.focus(),
3653
+ children: [
3654
+ tags.map((tag) => /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 rounded bg-indigo-500/20 text-indigo-300 text-xs px-2 py-0.5", children: [
3655
+ tag,
3656
+ /* @__PURE__ */ jsx(
3657
+ "button",
3658
+ {
3659
+ type: "button",
3660
+ onClick: (e) => {
3661
+ e.stopPropagation();
3662
+ removeTag(tag);
3663
+ },
3664
+ className: "hover:text-white leading-none",
3665
+ disabled: savingTags,
3666
+ children: "\xD7"
3667
+ }
3668
+ )
3669
+ ] }, tag)),
3670
+ /* @__PURE__ */ jsx(
3671
+ "input",
3672
+ {
3673
+ ref: inputRef,
3674
+ value: tagInput,
3675
+ onChange: (e) => setTagInput(e.target.value),
3676
+ onKeyDown: onTagKeyDown,
3677
+ onBlur: () => tagInput.trim() && addTag(tagInput),
3678
+ placeholder: tags.length === 0 ? "Add tags\u2026" : "",
3679
+ className: "flex-1 min-w-[80px] bg-transparent text-xs outline-none placeholder:text-muted-foreground",
3680
+ disabled: savingTags
3681
+ }
3682
+ )
3683
+ ]
3684
+ }
3685
+ )
3686
+ ] })
3687
+ ] }),
3688
+ /* @__PURE__ */ jsxs(
3689
+ "button",
3690
+ {
3691
+ onClick: () => setDetailsOpen((v) => !v),
3692
+ className: "flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors",
3693
+ children: [
3694
+ /* @__PURE__ */ jsx("span", { children: detailsOpen ? "\u25BE" : "\u25B8" }),
3695
+ /* @__PURE__ */ jsx("span", { children: "Details" })
3696
+ ]
3697
+ }
3698
+ ),
3699
+ detailsOpen && /* @__PURE__ */ jsxs("div", { className: "space-y-3 pl-1", children: [
3700
+ /* @__PURE__ */ jsxs("div", { children: [
3701
+ /* @__PURE__ */ jsx("label", { className: "text-xs text-muted-foreground mb-1 block", children: "JSON-RPC Endpoint" }),
3702
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3703
+ /* @__PURE__ */ jsx("code", { className: "flex-1 rounded bg-muted px-3 py-1.5 text-xs font-mono text-foreground break-all", children: jsonRpcUrl }),
3704
+ /* @__PURE__ */ jsx(CopyButton, { text: jsonRpcUrl })
3705
+ ] })
3706
+ ] }),
3707
+ /* @__PURE__ */ jsxs("div", { children: [
3708
+ /* @__PURE__ */ jsx("label", { className: "text-xs text-muted-foreground mb-1 block", children: "Agent Card URL" }),
3709
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3710
+ /* @__PURE__ */ jsx("code", { className: "flex-1 rounded bg-muted px-3 py-1.5 text-xs font-mono text-foreground break-all", children: agentCardUrl }),
3711
+ /* @__PURE__ */ jsx(CopyButton, { text: agentCardUrl })
3712
+ ] })
3713
+ ] }),
3714
+ /* @__PURE__ */ jsxs("div", { children: [
3715
+ /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: fetchAgentCard, disabled: loading, children: loading ? "Loading..." : cardPreview ? "Hide Agent Card" : "Agent Card" }),
3716
+ cardPreview && /* @__PURE__ */ jsxs("div", { className: "mt-3 relative", children: [
3717
+ /* @__PURE__ */ jsx("pre", { className: "rounded bg-muted p-4 text-xs font-mono text-foreground overflow-x-auto max-h-96", children: cardPreview }),
3718
+ /* @__PURE__ */ jsx(CopyButton, { text: cardPreview, className: "absolute top-2 right-2" })
3719
+ ] })
3720
+ ] })
3721
+ ] })
3722
+ ] })
3723
+ ] });
3724
+ }
3725
+ function AgentDetailPage({ agentId, a2aBaseUrl, tenantSlug, timezone = "UTC" }) {
3726
+ const { LinkComponent, basePath } = useNavigation();
3727
+ const { mutate } = useSWRConfig();
3728
+ const cacheKey = `agent-${agentId}`;
3729
+ const { data: agent, error, isLoading } = useApi(
3730
+ cacheKey,
3731
+ (client) => client.agents.get(agentId)
3732
+ );
3733
+ const invalidate = useCallback(() => {
3734
+ mutate(cacheKey);
3735
+ }, [mutate, cacheKey]);
3736
+ if (isLoading) {
3737
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
3738
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-40" }),
3739
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-6 gap-4", children: [1, 2, 3, 4, 5, 6].map((i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-20" }, i)) }),
3740
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-64 w-full" })
3741
+ ] });
3742
+ }
3743
+ if (error) {
3744
+ return /* @__PURE__ */ jsx("div", { className: "text-destructive text-sm py-12 text-center", children: error.status === 404 ? "Agent not found." : `Failed to load agent: ${error.message}` });
3745
+ }
3746
+ if (!agent) {
3747
+ return /* @__PURE__ */ jsx("div", { className: "text-muted-foreground text-sm py-12 text-center", children: "Agent not found." });
3748
+ }
3749
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
3750
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-start", children: /* @__PURE__ */ jsx(
3751
+ LinkComponent,
3752
+ {
3753
+ href: `${basePath}/agents/${agentId}/playground`,
3754
+ className: buttonVariants({ variant: "outline", size: "sm" }),
3755
+ children: "Open Playground"
3756
+ }
3757
+ ) }),
3758
+ /* @__PURE__ */ jsx(
3759
+ Tabs,
3760
+ {
3761
+ tabs: [
3762
+ {
3763
+ label: "General",
3764
+ content: /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
3765
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-6 gap-4", children: [
3766
+ /* @__PURE__ */ jsx(MetricCard, { label: "Max Turns", children: agent.max_turns }),
3767
+ /* @__PURE__ */ jsx(MetricCard, { label: "Budget", children: /* @__PURE__ */ jsxs("span", { className: "font-mono", children: [
3768
+ "$",
3769
+ agent.max_budget_usd.toFixed(2)
3770
+ ] }) }),
3771
+ /* @__PURE__ */ jsx(MetricCard, { label: "Max Runtime", children: /* @__PURE__ */ jsxs("span", { className: "font-mono", children: [
3772
+ Math.floor(agent.max_runtime_seconds / 60),
3773
+ "m"
3774
+ ] }) }),
3775
+ /* @__PURE__ */ jsx(MetricCard, { label: "Skills", children: (agent.skills ?? []).length }),
3776
+ /* @__PURE__ */ jsx(MetricCard, { label: "Plugins", children: (agent.plugins ?? []).length }),
3777
+ /* @__PURE__ */ jsx(MetricCard, { label: "Model", children: /* @__PURE__ */ jsx("span", { className: "font-mono text-xs", children: agent.model }) })
3778
+ ] }),
3779
+ /* @__PURE__ */ jsx(AgentEditForm, { agent, onSaved: invalidate }),
3780
+ tenantSlug && a2aBaseUrl && /* @__PURE__ */ jsx(
3781
+ AgentA2aInfo,
3782
+ {
3783
+ agentId: agent.id,
3784
+ tenantSlug,
3785
+ agentSlug: agent.slug ?? agent.name,
3786
+ baseUrl: a2aBaseUrl,
3787
+ initialEnabled: agent.a2a_enabled,
3788
+ initialTags: agent.a2a_tags ?? [],
3789
+ onChanged: invalidate
3790
+ }
3791
+ )
3792
+ ] })
3793
+ },
3794
+ {
3795
+ label: "Connectors",
3796
+ content: /* @__PURE__ */ jsx(
3797
+ AgentConnectorsManager,
3798
+ {
3799
+ agentId: agent.id,
3800
+ toolkits: agent.composio_toolkits ?? [],
3801
+ composioAllowedTools: agent.composio_allowed_tools ?? [],
3802
+ onChanged: invalidate
3803
+ }
3804
+ )
3805
+ },
3806
+ {
3807
+ label: "Skills",
3808
+ content: /* @__PURE__ */ jsx(
3809
+ AgentSkillManager,
3810
+ {
3811
+ agentId: agent.id,
3812
+ initialSkills: agent.skills ?? [],
3813
+ onSaved: invalidate
3814
+ }
3815
+ )
3816
+ },
3817
+ {
3818
+ label: "Plugins",
3819
+ content: /* @__PURE__ */ jsx(
3820
+ AgentPluginManager,
3821
+ {
3822
+ agentId: agent.id,
3823
+ initialPlugins: agent.plugins ?? [],
3824
+ onSaved: invalidate
3825
+ }
3826
+ )
3827
+ },
3828
+ {
3829
+ label: "Schedules",
3830
+ content: /* @__PURE__ */ jsx(
3831
+ AgentScheduleForm,
3832
+ {
3833
+ initialSchedules: [],
3834
+ timezone
3835
+ }
3836
+ )
3837
+ },
3838
+ {
3839
+ label: "Runs",
3840
+ content: /* @__PURE__ */ jsx(AgentRuns, { agentId: agent.id })
3841
+ }
3842
+ ]
3843
+ }
3844
+ )
3845
+ ] });
3846
+ }
3847
+
3848
+ export { AdminTable, AdminTableHead, AdminTableRow, AgentA2aInfo, AgentConnectorsManager, AgentDetailPage, AgentEditForm, AgentListPage, AgentPlaneProvider, AgentPluginManager, AgentRuns, AgentScheduleForm, AgentSkillManager, Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, ConfirmDialog, CopyButton, DashboardPage, DetailPageHeader, Dialog, DialogBody, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, EmptyRow, FormError, FormField, Input, LocalDate, McpServerListPage, MetricCard, ModelSelector, PaginationBar, PluginMarketplaceDetailPage, PluginMarketplaceListPage, RunDetailPage, RunListPage, RunSourceBadge, RunStatusBadge, SectionHeader, Select, SettingsPage, Skeleton, Tabs, Textarea, Th, ToolkitMultiselect, TranscriptViewer, badgeVariants, buttonVariants, cn, parsePaginationParams, useAgentPlaneClient, useApi, useAuthError, useNavigation };