@invergent/agent-chat-react 1.6.0 → 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1250 -362
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +86 -1
- package/dist/index.d.ts +86 -1
- package/dist/index.js +1251 -364
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -266,7 +266,9 @@ function Button({
|
|
|
266
266
|
function BrowserControlBar({
|
|
267
267
|
sessionId,
|
|
268
268
|
hasControl,
|
|
269
|
-
adapter
|
|
269
|
+
adapter,
|
|
270
|
+
onControlAcquired,
|
|
271
|
+
onControlReleased
|
|
270
272
|
}) {
|
|
271
273
|
const [pending, setPending] = react.useState(false);
|
|
272
274
|
const [error, setError] = react.useState(null);
|
|
@@ -277,8 +279,10 @@ function BrowserControlBar({
|
|
|
277
279
|
try {
|
|
278
280
|
if (hasControl) {
|
|
279
281
|
await adapter.releaseBrowserControl(sessionId);
|
|
282
|
+
onControlReleased?.();
|
|
280
283
|
} else {
|
|
281
284
|
await adapter.acquireBrowserControl(sessionId);
|
|
285
|
+
onControlAcquired?.();
|
|
282
286
|
}
|
|
283
287
|
} catch (nextError) {
|
|
284
288
|
setError(nextError.message);
|
|
@@ -308,11 +312,14 @@ function BrowserControlBar({
|
|
|
308
312
|
error && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-w-40 truncate text-[11px] text-destructive", children: error })
|
|
309
313
|
] });
|
|
310
314
|
}
|
|
311
|
-
function BrowserLiveView({
|
|
315
|
+
function BrowserLiveView({
|
|
316
|
+
src,
|
|
317
|
+
testId = "browser-iframe"
|
|
318
|
+
}) {
|
|
312
319
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
313
320
|
"iframe",
|
|
314
321
|
{
|
|
315
|
-
"data-testid":
|
|
322
|
+
"data-testid": testId,
|
|
316
323
|
title: "Browser live view",
|
|
317
324
|
src,
|
|
318
325
|
className: "h-full w-full border-0 bg-black",
|
|
@@ -340,38 +347,379 @@ function BrowserStatusDot({
|
|
|
340
347
|
}
|
|
341
348
|
);
|
|
342
349
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
350
|
+
|
|
351
|
+
// src/components/ui/dialog.tsx
|
|
352
|
+
init_utils();
|
|
353
|
+
function Dialog({
|
|
354
|
+
...props
|
|
355
|
+
}) {
|
|
356
|
+
return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Dialog.Root, { "data-slot": "dialog", ...props });
|
|
357
|
+
}
|
|
358
|
+
function DialogPortal({
|
|
359
|
+
...props
|
|
360
|
+
}) {
|
|
361
|
+
return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Dialog.Portal, { "data-slot": "dialog-portal", ...props });
|
|
362
|
+
}
|
|
363
|
+
function DialogOverlay({
|
|
364
|
+
className,
|
|
365
|
+
...props
|
|
366
|
+
}) {
|
|
367
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
368
|
+
radixUi.Dialog.Overlay,
|
|
369
|
+
{
|
|
370
|
+
"data-slot": "dialog-overlay",
|
|
371
|
+
className: cn(
|
|
372
|
+
"fixed inset-0 isolate z-50 bg-black/20 duration-100 supports-backdrop-filter:backdrop-blur-sm data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
|
|
373
|
+
className
|
|
374
|
+
),
|
|
375
|
+
...props
|
|
376
|
+
}
|
|
347
377
|
);
|
|
348
|
-
|
|
378
|
+
}
|
|
379
|
+
function DialogContent({
|
|
380
|
+
className,
|
|
381
|
+
children,
|
|
382
|
+
showCloseButton = true,
|
|
383
|
+
...props
|
|
384
|
+
}) {
|
|
385
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(DialogPortal, { children: [
|
|
386
|
+
/* @__PURE__ */ jsxRuntime.jsx(DialogOverlay, {}),
|
|
387
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
388
|
+
radixUi.Dialog.Content,
|
|
389
|
+
{
|
|
390
|
+
"data-slot": "dialog-content",
|
|
391
|
+
className: cn(
|
|
392
|
+
"fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-6 rounded-none bg-popover p-6 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-md data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
|
393
|
+
className
|
|
394
|
+
),
|
|
395
|
+
...props,
|
|
396
|
+
children: [
|
|
397
|
+
children,
|
|
398
|
+
showCloseButton && /* @__PURE__ */ jsxRuntime.jsx(radixUi.Dialog.Close, { "data-slot": "dialog-close", asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
399
|
+
Button,
|
|
400
|
+
{
|
|
401
|
+
variant: "ghost",
|
|
402
|
+
className: "absolute top-5 right-5 bg-secondary",
|
|
403
|
+
size: "icon-sm",
|
|
404
|
+
children: [
|
|
405
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
406
|
+
lucideReact.XIcon,
|
|
407
|
+
{}
|
|
408
|
+
),
|
|
409
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Close" })
|
|
410
|
+
]
|
|
411
|
+
}
|
|
412
|
+
) })
|
|
413
|
+
]
|
|
414
|
+
}
|
|
415
|
+
)
|
|
416
|
+
] });
|
|
417
|
+
}
|
|
418
|
+
function DialogHeader({ className, ...props }) {
|
|
419
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
420
|
+
"div",
|
|
421
|
+
{
|
|
422
|
+
"data-slot": "dialog-header",
|
|
423
|
+
className: cn("flex flex-col gap-2", className),
|
|
424
|
+
...props
|
|
425
|
+
}
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
function DialogFooter({
|
|
429
|
+
className,
|
|
430
|
+
showCloseButton = false,
|
|
431
|
+
children,
|
|
432
|
+
...props
|
|
433
|
+
}) {
|
|
349
434
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
350
435
|
"div",
|
|
351
436
|
{
|
|
352
|
-
"data-
|
|
353
|
-
className:
|
|
437
|
+
"data-slot": "dialog-footer",
|
|
438
|
+
className: cn(
|
|
439
|
+
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
|
440
|
+
className
|
|
441
|
+
),
|
|
442
|
+
...props,
|
|
354
443
|
children: [
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
444
|
+
children,
|
|
445
|
+
showCloseButton && /* @__PURE__ */ jsxRuntime.jsx(radixUi.Dialog.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", children: "Close" }) })
|
|
446
|
+
]
|
|
447
|
+
}
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
function DialogTitle({
|
|
451
|
+
className,
|
|
452
|
+
...props
|
|
453
|
+
}) {
|
|
454
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
455
|
+
radixUi.Dialog.Title,
|
|
456
|
+
{
|
|
457
|
+
"data-slot": "dialog-title",
|
|
458
|
+
className: cn(
|
|
459
|
+
"font-heading text-lg leading-none font-semibold tracking-wider uppercase",
|
|
460
|
+
className
|
|
461
|
+
),
|
|
462
|
+
...props
|
|
463
|
+
}
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
function DialogDescription({
|
|
467
|
+
className,
|
|
468
|
+
...props
|
|
469
|
+
}) {
|
|
470
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
471
|
+
radixUi.Dialog.Description,
|
|
472
|
+
{
|
|
473
|
+
"data-slot": "dialog-description",
|
|
474
|
+
className: cn(
|
|
475
|
+
"mt-0.5 text-sm leading-relaxed text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
|
|
476
|
+
className
|
|
477
|
+
),
|
|
478
|
+
...props
|
|
479
|
+
}
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// src/components/ui/tooltip.tsx
|
|
484
|
+
init_utils();
|
|
485
|
+
function TooltipProvider({
|
|
486
|
+
delayDuration = 0,
|
|
487
|
+
...props
|
|
488
|
+
}) {
|
|
489
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
490
|
+
radixUi.Tooltip.Provider,
|
|
491
|
+
{
|
|
492
|
+
"data-slot": "tooltip-provider",
|
|
493
|
+
delayDuration,
|
|
494
|
+
...props
|
|
495
|
+
}
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
function Tooltip({
|
|
499
|
+
...props
|
|
500
|
+
}) {
|
|
501
|
+
return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Tooltip.Root, { "data-slot": "tooltip", ...props });
|
|
502
|
+
}
|
|
503
|
+
function TooltipTrigger({
|
|
504
|
+
...props
|
|
505
|
+
}) {
|
|
506
|
+
return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Tooltip.Trigger, { "data-slot": "tooltip-trigger", ...props });
|
|
507
|
+
}
|
|
508
|
+
function TooltipContent({
|
|
509
|
+
className,
|
|
510
|
+
sideOffset = 0,
|
|
511
|
+
children,
|
|
512
|
+
...props
|
|
513
|
+
}) {
|
|
514
|
+
return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Tooltip.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
515
|
+
radixUi.Tooltip.Content,
|
|
516
|
+
{
|
|
517
|
+
"data-slot": "tooltip-content",
|
|
518
|
+
sideOffset,
|
|
519
|
+
className: cn(
|
|
520
|
+
"z-50 inline-flex w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin) items-center gap-1.5 rounded-none bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-none data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
|
521
|
+
className
|
|
522
|
+
),
|
|
523
|
+
...props,
|
|
524
|
+
children: [
|
|
525
|
+
children,
|
|
526
|
+
/* @__PURE__ */ jsxRuntime.jsx(radixUi.Tooltip.Arrow, { className: "z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-none bg-foreground fill-foreground" })
|
|
373
527
|
]
|
|
374
528
|
}
|
|
529
|
+
) });
|
|
530
|
+
}
|
|
531
|
+
function BrowserPane({ sessionId, state, adapter }) {
|
|
532
|
+
const [fullscreenOpen, setFullscreenOpen] = react.useState(false);
|
|
533
|
+
const [openFullscreenOnControl, setOpenFullscreenOnControl] = react.useState(false);
|
|
534
|
+
const [localControlActive, setLocalControlActive] = react.useState(false);
|
|
535
|
+
const [previewSnapshot, setPreviewSnapshot] = react.useState(null);
|
|
536
|
+
const [previewLoading, setPreviewLoading] = react.useState(false);
|
|
537
|
+
const [previewError, setPreviewError] = react.useState(null);
|
|
538
|
+
const hasLiveViewAdapter = typeof adapter.browserLiveViewUrl === "function";
|
|
539
|
+
const hasPreviewAdapter = typeof adapter.getBrowserPreviewSnapshot === "function";
|
|
540
|
+
const hasControlAdapter = typeof adapter.acquireBrowserControl === "function" && typeof adapter.releaseBrowserControl === "function";
|
|
541
|
+
const liveViewUrl = react.useMemo(() => {
|
|
542
|
+
if (!hasLiveViewAdapter) return "";
|
|
543
|
+
return adapter.browserLiveViewUrl(sessionId);
|
|
544
|
+
}, [adapter, hasLiveViewAdapter, sessionId]);
|
|
545
|
+
const hasLiveView = state.status !== "provisioning" && state.status !== "closed";
|
|
546
|
+
const hasUserControl = localControlActive;
|
|
547
|
+
const canUseLiveView = hasLiveView && hasUserControl && Boolean(liveViewUrl);
|
|
548
|
+
const canOpenPreview = hasLiveView && (hasPreviewAdapter || canUseLiveView);
|
|
549
|
+
const canOpenFullscreen = canOpenPreview;
|
|
550
|
+
const refreshPreview = react.useCallback(
|
|
551
|
+
async (signal) => {
|
|
552
|
+
if (!hasPreviewAdapter || canUseLiveView) return;
|
|
553
|
+
setPreviewLoading(true);
|
|
554
|
+
setPreviewError(null);
|
|
555
|
+
try {
|
|
556
|
+
const snapshot = await adapter.getBrowserPreviewSnapshot?.(sessionId);
|
|
557
|
+
if (signal?.aborted) return;
|
|
558
|
+
setPreviewSnapshot(snapshot ?? null);
|
|
559
|
+
if (!snapshot) {
|
|
560
|
+
setPreviewError("Browser preview is unavailable.");
|
|
561
|
+
}
|
|
562
|
+
} catch (error) {
|
|
563
|
+
if (signal?.aborted) return;
|
|
564
|
+
setPreviewSnapshot(null);
|
|
565
|
+
setPreviewError(
|
|
566
|
+
error instanceof Error ? error.message : "Browser preview failed."
|
|
567
|
+
);
|
|
568
|
+
} finally {
|
|
569
|
+
if (!signal?.aborted) setPreviewLoading(false);
|
|
570
|
+
}
|
|
571
|
+
},
|
|
572
|
+
[adapter, canUseLiveView, hasPreviewAdapter, sessionId]
|
|
573
|
+
);
|
|
574
|
+
react.useEffect(() => {
|
|
575
|
+
setFullscreenOpen(false);
|
|
576
|
+
setOpenFullscreenOnControl(false);
|
|
577
|
+
setLocalControlActive(false);
|
|
578
|
+
setPreviewSnapshot(null);
|
|
579
|
+
setPreviewLoading(false);
|
|
580
|
+
setPreviewError(null);
|
|
581
|
+
}, [sessionId]);
|
|
582
|
+
react.useEffect(() => {
|
|
583
|
+
if (!openFullscreenOnControl) return;
|
|
584
|
+
if (!canUseLiveView) return;
|
|
585
|
+
setFullscreenOpen(true);
|
|
586
|
+
setOpenFullscreenOnControl(false);
|
|
587
|
+
}, [canUseLiveView, openFullscreenOnControl]);
|
|
588
|
+
const handleFullscreenOpenChange = react.useCallback(
|
|
589
|
+
(open) => {
|
|
590
|
+
setFullscreenOpen(open);
|
|
591
|
+
if (open || !hasUserControl || !hasControlAdapter) return;
|
|
592
|
+
setLocalControlActive(false);
|
|
593
|
+
void adapter.releaseBrowserControl(sessionId).catch((error) => {
|
|
594
|
+
console.error("Failed to release browser control", error);
|
|
595
|
+
});
|
|
596
|
+
},
|
|
597
|
+
[adapter, hasControlAdapter, hasUserControl, sessionId]
|
|
598
|
+
);
|
|
599
|
+
react.useEffect(() => {
|
|
600
|
+
if (!hasLiveView || canUseLiveView) {
|
|
601
|
+
setPreviewLoading(false);
|
|
602
|
+
setPreviewError(null);
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
if (!hasPreviewAdapter) return;
|
|
606
|
+
const abort = new AbortController();
|
|
607
|
+
void refreshPreview(abort.signal);
|
|
608
|
+
const timer = window.setInterval(() => {
|
|
609
|
+
void refreshPreview(abort.signal);
|
|
610
|
+
}, 5e3);
|
|
611
|
+
return () => {
|
|
612
|
+
abort.abort();
|
|
613
|
+
window.clearInterval(timer);
|
|
614
|
+
};
|
|
615
|
+
}, [
|
|
616
|
+
canUseLiveView,
|
|
617
|
+
fullscreenOpen,
|
|
618
|
+
hasLiveView,
|
|
619
|
+
hasPreviewAdapter,
|
|
620
|
+
refreshPreview
|
|
621
|
+
]);
|
|
622
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
623
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
624
|
+
"div",
|
|
625
|
+
{
|
|
626
|
+
"data-testid": "browser-pane",
|
|
627
|
+
className: "flex h-full min-h-0 flex-col bg-background",
|
|
628
|
+
children: [
|
|
629
|
+
/* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex min-h-10 items-center gap-2 border-b border-line bg-card px-3 text-xs text-foreground", children: [
|
|
630
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ZapIcon, { className: "size-3.5 text-muted-foreground", "aria-hidden": "true" }),
|
|
631
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Browser" }),
|
|
632
|
+
/* @__PURE__ */ jsxRuntime.jsx(BrowserStatusDot, { status: state.status }),
|
|
633
|
+
state.controlOwner && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0 truncate text-amber-500", children: [
|
|
634
|
+
state.controlOwner,
|
|
635
|
+
" has control"
|
|
636
|
+
] }),
|
|
637
|
+
canOpenFullscreen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-auto flex items-center", children: /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
638
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
639
|
+
"button",
|
|
640
|
+
{
|
|
641
|
+
type: "button",
|
|
642
|
+
"aria-label": "Maximize browser",
|
|
643
|
+
className: "inline-flex size-7 items-center justify-center text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
644
|
+
onClick: () => setFullscreenOpen(true),
|
|
645
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Maximize2Icon, { className: "size-3.5", "aria-hidden": "true" })
|
|
646
|
+
}
|
|
647
|
+
) }),
|
|
648
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "bottom", children: "Full screen" })
|
|
649
|
+
] }) }) })
|
|
650
|
+
] }),
|
|
651
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-0 flex-1 bg-black", children: state.status === "provisioning" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Starting browser..." }) : state.status === "closed" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Browser closed." }) : canUseLiveView ? /* @__PURE__ */ jsxRuntime.jsx(BrowserLiveView, { src: liveViewUrl }) : previewSnapshot ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
652
|
+
BrowserPreviewImage,
|
|
653
|
+
{
|
|
654
|
+
src: previewSnapshot.src,
|
|
655
|
+
fit: "contain",
|
|
656
|
+
testId: "browser-preview-image"
|
|
657
|
+
}
|
|
658
|
+
) : previewLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Loading browser preview..." }) : previewError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: previewError }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Browser preview is unavailable." }) }),
|
|
659
|
+
hasLiveView && hasControlAdapter && /* @__PURE__ */ jsxRuntime.jsx(
|
|
660
|
+
BrowserControlBar,
|
|
661
|
+
{
|
|
662
|
+
sessionId,
|
|
663
|
+
hasControl: localControlActive,
|
|
664
|
+
adapter,
|
|
665
|
+
onControlAcquired: () => {
|
|
666
|
+
setLocalControlActive(true);
|
|
667
|
+
setOpenFullscreenOnControl(true);
|
|
668
|
+
},
|
|
669
|
+
onControlReleased: () => setLocalControlActive(false)
|
|
670
|
+
}
|
|
671
|
+
)
|
|
672
|
+
]
|
|
673
|
+
}
|
|
674
|
+
),
|
|
675
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog, { open: fullscreenOpen, onOpenChange: handleFullscreenOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
676
|
+
DialogContent,
|
|
677
|
+
{
|
|
678
|
+
"aria-describedby": void 0,
|
|
679
|
+
className: "flex h-screen w-screen max-w-none flex-col gap-0 overflow-hidden rounded-none border-0 bg-background p-0 shadow-none ring-0 sm:max-w-none",
|
|
680
|
+
children: [
|
|
681
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DialogHeader, { className: "h-10 shrink-0 flex-row items-center gap-2 border-b border-line bg-card px-4 py-0", children: [
|
|
682
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ZapIcon, { className: "size-3.5 text-muted-foreground", "aria-hidden": "true" }),
|
|
683
|
+
/* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { className: "text-xs normal-case tracking-normal", children: "Browser" }),
|
|
684
|
+
/* @__PURE__ */ jsxRuntime.jsx(BrowserStatusDot, { status: state.status }),
|
|
685
|
+
state.controlOwner && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0 truncate text-xs text-amber-500", children: [
|
|
686
|
+
state.controlOwner,
|
|
687
|
+
" has control"
|
|
688
|
+
] })
|
|
689
|
+
] }),
|
|
690
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-0 flex-1 bg-black", children: canUseLiveView ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
691
|
+
BrowserLiveView,
|
|
692
|
+
{
|
|
693
|
+
src: liveViewUrl,
|
|
694
|
+
testId: "browser-fullscreen-iframe"
|
|
695
|
+
}
|
|
696
|
+
) : previewSnapshot ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
697
|
+
BrowserPreviewImage,
|
|
698
|
+
{
|
|
699
|
+
src: previewSnapshot.src,
|
|
700
|
+
fit: "contain",
|
|
701
|
+
testId: "browser-fullscreen-preview-image"
|
|
702
|
+
}
|
|
703
|
+
) : previewLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Loading browser preview..." }) : previewError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: previewError }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Browser preview is unavailable." }) })
|
|
704
|
+
]
|
|
705
|
+
}
|
|
706
|
+
) })
|
|
707
|
+
] });
|
|
708
|
+
}
|
|
709
|
+
function BrowserPreviewImage({
|
|
710
|
+
src,
|
|
711
|
+
fit,
|
|
712
|
+
testId
|
|
713
|
+
}) {
|
|
714
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
715
|
+
"img",
|
|
716
|
+
{
|
|
717
|
+
"data-testid": testId,
|
|
718
|
+
src,
|
|
719
|
+
alt: "Browser preview",
|
|
720
|
+
className: `h-full w-full bg-black ${fit === "cover" ? "object-cover object-top" : "object-contain"}`,
|
|
721
|
+
draggable: false
|
|
722
|
+
}
|
|
375
723
|
);
|
|
376
724
|
}
|
|
377
725
|
|
|
@@ -446,56 +794,7 @@ var ConversationScrollButton = ({
|
|
|
446
794
|
);
|
|
447
795
|
};
|
|
448
796
|
|
|
449
|
-
// src/components/
|
|
450
|
-
init_utils();
|
|
451
|
-
function TooltipProvider({
|
|
452
|
-
delayDuration = 0,
|
|
453
|
-
...props
|
|
454
|
-
}) {
|
|
455
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
456
|
-
radixUi.Tooltip.Provider,
|
|
457
|
-
{
|
|
458
|
-
"data-slot": "tooltip-provider",
|
|
459
|
-
delayDuration,
|
|
460
|
-
...props
|
|
461
|
-
}
|
|
462
|
-
);
|
|
463
|
-
}
|
|
464
|
-
function Tooltip({
|
|
465
|
-
...props
|
|
466
|
-
}) {
|
|
467
|
-
return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Tooltip.Root, { "data-slot": "tooltip", ...props });
|
|
468
|
-
}
|
|
469
|
-
function TooltipTrigger({
|
|
470
|
-
...props
|
|
471
|
-
}) {
|
|
472
|
-
return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Tooltip.Trigger, { "data-slot": "tooltip-trigger", ...props });
|
|
473
|
-
}
|
|
474
|
-
function TooltipContent({
|
|
475
|
-
className,
|
|
476
|
-
sideOffset = 0,
|
|
477
|
-
children,
|
|
478
|
-
...props
|
|
479
|
-
}) {
|
|
480
|
-
return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Tooltip.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
481
|
-
radixUi.Tooltip.Content,
|
|
482
|
-
{
|
|
483
|
-
"data-slot": "tooltip-content",
|
|
484
|
-
sideOffset,
|
|
485
|
-
className: cn(
|
|
486
|
-
"z-50 inline-flex w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin) items-center gap-1.5 rounded-none bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-none data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
|
487
|
-
className
|
|
488
|
-
),
|
|
489
|
-
...props,
|
|
490
|
-
children: [
|
|
491
|
-
children,
|
|
492
|
-
/* @__PURE__ */ jsxRuntime.jsx(radixUi.Tooltip.Arrow, { className: "z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-none bg-foreground fill-foreground" })
|
|
493
|
-
]
|
|
494
|
-
}
|
|
495
|
-
) });
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
// src/components/ai-elements/message.tsx
|
|
797
|
+
// src/components/ai-elements/message.tsx
|
|
499
798
|
init_utils();
|
|
500
799
|
var Message = ({ className, from, ...props }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
501
800
|
"div",
|
|
@@ -879,66 +1178,82 @@ function TimelineSeparator({
|
|
|
879
1178
|
);
|
|
880
1179
|
}
|
|
881
1180
|
function BrowserActivityGroup({ calls }) {
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
/* @__PURE__ */ jsxRuntime.
|
|
886
|
-
"button",
|
|
887
|
-
{
|
|
888
|
-
type: "button",
|
|
889
|
-
onClick: () => setOpen((current) => !current),
|
|
890
|
-
className: "flex w-full items-center gap-2 px-3 py-2 text-left hover:bg-muted/40",
|
|
891
|
-
children: [
|
|
892
|
-
open ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDownIcon, { className: "size-3 shrink-0", "aria-hidden": "true" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRightIcon, { className: "size-3 shrink-0", "aria-hidden": "true" }),
|
|
893
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ZapIcon, { className: "size-3 shrink-0 text-amber-500", "aria-hidden": "true" }),
|
|
894
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-foreground", children: "browser" }),
|
|
895
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0 truncate text-muted-foreground", children: [
|
|
896
|
-
"(",
|
|
897
|
-
calls.length,
|
|
898
|
-
" ",
|
|
899
|
-
calls.length === 1 ? "action" : "actions",
|
|
900
|
-
" - latest:",
|
|
901
|
-
" ",
|
|
902
|
-
latest ? summarise(latest) : "none",
|
|
903
|
-
")"
|
|
904
|
-
] })
|
|
905
|
-
]
|
|
906
|
-
}
|
|
907
|
-
),
|
|
908
|
-
open && /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "border-t border-line py-1", children: calls.map((call) => {
|
|
1181
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 border border-line bg-card px-3 py-2 text-xs", children: [
|
|
1182
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ZapIcon, { className: "size-3 shrink-0 text-amber-500", "aria-hidden": "true" }),
|
|
1183
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 font-semibold text-foreground", children: "Browser:" }),
|
|
1184
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate text-muted-foreground", children: calls.map((call, index) => {
|
|
909
1185
|
const error = stringValue(parseJson(call.result).error);
|
|
910
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
911
|
-
"
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
error
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
),
|
|
923
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate", children: summarise(call) }),
|
|
924
|
-
error && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto max-w-40 truncate text-destructive", children: error })
|
|
925
|
-
]
|
|
926
|
-
},
|
|
927
|
-
call.id
|
|
928
|
-
);
|
|
1186
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
1187
|
+
index > 0 && ", ",
|
|
1188
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1189
|
+
"span",
|
|
1190
|
+
{
|
|
1191
|
+
className: error ? "text-destructive" : void 0,
|
|
1192
|
+
"data-testid": error ? `activity-error-${call.id}` : void 0,
|
|
1193
|
+
title: error || void 0,
|
|
1194
|
+
children: summarize(call)
|
|
1195
|
+
}
|
|
1196
|
+
)
|
|
1197
|
+
] }, call.id);
|
|
929
1198
|
}) })
|
|
930
1199
|
] });
|
|
931
1200
|
}
|
|
932
|
-
function
|
|
933
|
-
const verb = call.toolName.replace(/^browser_/, "");
|
|
1201
|
+
function summarize(call) {
|
|
934
1202
|
const args = parseJson(call.args);
|
|
935
|
-
const
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
1203
|
+
const handler = ACTION_SUMMARIES[call.toolName];
|
|
1204
|
+
return handler ? handler(args) : call.toolName.replace(/^browser_/, "");
|
|
1205
|
+
}
|
|
1206
|
+
var ACTION_SUMMARIES = {
|
|
1207
|
+
browser_navigate: (args) => `navigate to ${stringValue(args.url) || "?"}`,
|
|
1208
|
+
browser_get_state: () => "get state",
|
|
1209
|
+
browser_close: () => "close",
|
|
1210
|
+
browser_click: (args) => {
|
|
1211
|
+
const ref = stringValue(args.ref);
|
|
1212
|
+
if (ref) return `click ${ref}`;
|
|
1213
|
+
const x = asNumber(args.x);
|
|
1214
|
+
const y = asNumber(args.y);
|
|
1215
|
+
if (x !== null && y !== null) return `click (${x}, ${y})`;
|
|
1216
|
+
return "click";
|
|
1217
|
+
},
|
|
1218
|
+
browser_type: (args) => {
|
|
1219
|
+
const text = stringValue(args.text);
|
|
1220
|
+
const ref = stringValue(args.ref);
|
|
1221
|
+
const quoted = text ? `"${truncateText(text)}"` : "";
|
|
1222
|
+
if (!quoted) return ref ? `type into ${ref}` : "type";
|
|
1223
|
+
return ref ? `type ${quoted} into ${ref}` : `type ${quoted}`;
|
|
1224
|
+
},
|
|
1225
|
+
browser_press_key: (args) => {
|
|
1226
|
+
const keys = Array.isArray(args.keys) ? args.keys.map(String) : [];
|
|
1227
|
+
return keys.length ? `press ${keys.join("+")}` : "press key";
|
|
1228
|
+
},
|
|
1229
|
+
browser_scroll: (args) => {
|
|
1230
|
+
const x = asNumber(args.x);
|
|
1231
|
+
const y = asNumber(args.y);
|
|
1232
|
+
const dx = asNumber(args.delta_x) ?? 0;
|
|
1233
|
+
const dy = asNumber(args.delta_y) ?? 0;
|
|
1234
|
+
const delta = dx || dy ? `\u0394(${dx}, ${dy})` : "";
|
|
1235
|
+
const origin = x !== null && y !== null ? `at (${x}, ${y})` : "";
|
|
1236
|
+
return ["scroll", delta, origin].filter(Boolean).join(" ");
|
|
1237
|
+
},
|
|
1238
|
+
browser_drag: (args) => {
|
|
1239
|
+
const path = Array.isArray(args.path) ? args.path : [];
|
|
1240
|
+
if (path.length < 2) return "drag";
|
|
1241
|
+
const first = path[0];
|
|
1242
|
+
const last = path[path.length - 1];
|
|
1243
|
+
const point = (p) => `(${asNumber(p[0]) ?? "?"}, ${asNumber(p[1]) ?? "?"})`;
|
|
1244
|
+
return `drag ${point(first)} \u2192 ${point(last)}`;
|
|
1245
|
+
},
|
|
1246
|
+
browser_wait: (args) => {
|
|
1247
|
+
const ms = asNumber(args.ms);
|
|
1248
|
+
return ms === null ? "wait" : `wait ${ms}ms`;
|
|
1249
|
+
},
|
|
1250
|
+
browser_screenshot: (args) => args.annotate === true ? "screenshot (annotated)" : "screenshot"
|
|
1251
|
+
};
|
|
1252
|
+
function truncateText(text, max = 32) {
|
|
1253
|
+
return text.length > max ? `${text.slice(0, max - 1)}\u2026` : text;
|
|
1254
|
+
}
|
|
1255
|
+
function asNumber(value) {
|
|
1256
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
942
1257
|
}
|
|
943
1258
|
function parseJson(raw) {
|
|
944
1259
|
if (!raw) return {};
|
|
@@ -1997,140 +2312,20 @@ function WebToolBlock({ tc }) {
|
|
|
1997
2312
|
displayText && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/70 truncate text-xs", children: displayText })
|
|
1998
2313
|
] });
|
|
1999
2314
|
}
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
}
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
})
|
|
2011
|
-
|
|
2012
|
-
}
|
|
2013
|
-
function DialogOverlay({
|
|
2014
|
-
className,
|
|
2015
|
-
...props
|
|
2016
|
-
}) {
|
|
2017
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2018
|
-
radixUi.Dialog.Overlay,
|
|
2019
|
-
{
|
|
2020
|
-
"data-slot": "dialog-overlay",
|
|
2021
|
-
className: cn(
|
|
2022
|
-
"fixed inset-0 isolate z-50 bg-black/20 duration-100 supports-backdrop-filter:backdrop-blur-sm data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
|
|
2023
|
-
className
|
|
2024
|
-
),
|
|
2025
|
-
...props
|
|
2026
|
-
}
|
|
2027
|
-
);
|
|
2028
|
-
}
|
|
2029
|
-
function DialogContent({
|
|
2030
|
-
className,
|
|
2031
|
-
children,
|
|
2032
|
-
showCloseButton = true,
|
|
2033
|
-
...props
|
|
2034
|
-
}) {
|
|
2035
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(DialogPortal, { children: [
|
|
2036
|
-
/* @__PURE__ */ jsxRuntime.jsx(DialogOverlay, {}),
|
|
2037
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2038
|
-
radixUi.Dialog.Content,
|
|
2039
|
-
{
|
|
2040
|
-
"data-slot": "dialog-content",
|
|
2041
|
-
className: cn(
|
|
2042
|
-
"fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-6 rounded-none bg-popover p-6 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-md data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
|
2043
|
-
className
|
|
2044
|
-
),
|
|
2045
|
-
...props,
|
|
2046
|
-
children: [
|
|
2047
|
-
children,
|
|
2048
|
-
showCloseButton && /* @__PURE__ */ jsxRuntime.jsx(radixUi.Dialog.Close, { "data-slot": "dialog-close", asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2049
|
-
Button,
|
|
2050
|
-
{
|
|
2051
|
-
variant: "ghost",
|
|
2052
|
-
className: "absolute top-5 right-5 bg-secondary",
|
|
2053
|
-
size: "icon-sm",
|
|
2054
|
-
children: [
|
|
2055
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2056
|
-
lucideReact.XIcon,
|
|
2057
|
-
{}
|
|
2058
|
-
),
|
|
2059
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Close" })
|
|
2060
|
-
]
|
|
2061
|
-
}
|
|
2062
|
-
) })
|
|
2063
|
-
]
|
|
2064
|
-
}
|
|
2065
|
-
)
|
|
2315
|
+
function VisionAnalyzeBlock({ tc }) {
|
|
2316
|
+
let image = "";
|
|
2317
|
+
try {
|
|
2318
|
+
const args = JSON.parse(tc.args);
|
|
2319
|
+
const ref = String(args.image ?? args.image_url ?? args.image_path ?? "");
|
|
2320
|
+
image = ref.split("/").pop() ?? ref;
|
|
2321
|
+
} catch {
|
|
2322
|
+
}
|
|
2323
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-sm py-0.5", children: [
|
|
2324
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.EyeIcon, { className: "size-3.5 text-muted-foreground/60 shrink-0" }),
|
|
2325
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: "Vision Analyze" }),
|
|
2326
|
+
image && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/70 truncate text-xs", children: image })
|
|
2066
2327
|
] });
|
|
2067
2328
|
}
|
|
2068
|
-
function DialogHeader({ className, ...props }) {
|
|
2069
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2070
|
-
"div",
|
|
2071
|
-
{
|
|
2072
|
-
"data-slot": "dialog-header",
|
|
2073
|
-
className: cn("flex flex-col gap-2", className),
|
|
2074
|
-
...props
|
|
2075
|
-
}
|
|
2076
|
-
);
|
|
2077
|
-
}
|
|
2078
|
-
function DialogFooter({
|
|
2079
|
-
className,
|
|
2080
|
-
showCloseButton = false,
|
|
2081
|
-
children,
|
|
2082
|
-
...props
|
|
2083
|
-
}) {
|
|
2084
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2085
|
-
"div",
|
|
2086
|
-
{
|
|
2087
|
-
"data-slot": "dialog-footer",
|
|
2088
|
-
className: cn(
|
|
2089
|
-
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
|
2090
|
-
className
|
|
2091
|
-
),
|
|
2092
|
-
...props,
|
|
2093
|
-
children: [
|
|
2094
|
-
children,
|
|
2095
|
-
showCloseButton && /* @__PURE__ */ jsxRuntime.jsx(radixUi.Dialog.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", children: "Close" }) })
|
|
2096
|
-
]
|
|
2097
|
-
}
|
|
2098
|
-
);
|
|
2099
|
-
}
|
|
2100
|
-
function DialogTitle({
|
|
2101
|
-
className,
|
|
2102
|
-
...props
|
|
2103
|
-
}) {
|
|
2104
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2105
|
-
radixUi.Dialog.Title,
|
|
2106
|
-
{
|
|
2107
|
-
"data-slot": "dialog-title",
|
|
2108
|
-
className: cn(
|
|
2109
|
-
"font-heading text-lg leading-none font-semibold tracking-wider uppercase",
|
|
2110
|
-
className
|
|
2111
|
-
),
|
|
2112
|
-
...props
|
|
2113
|
-
}
|
|
2114
|
-
);
|
|
2115
|
-
}
|
|
2116
|
-
function DialogDescription({
|
|
2117
|
-
className,
|
|
2118
|
-
...props
|
|
2119
|
-
}) {
|
|
2120
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2121
|
-
radixUi.Dialog.Description,
|
|
2122
|
-
{
|
|
2123
|
-
"data-slot": "dialog-description",
|
|
2124
|
-
className: cn(
|
|
2125
|
-
"mt-0.5 text-sm leading-relaxed text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
|
|
2126
|
-
className
|
|
2127
|
-
),
|
|
2128
|
-
...props
|
|
2129
|
-
}
|
|
2130
|
-
);
|
|
2131
|
-
}
|
|
2132
|
-
|
|
2133
|
-
// src/components/chat/tools/file-tools.tsx
|
|
2134
2329
|
init_utils();
|
|
2135
2330
|
|
|
2136
2331
|
// src/components/chat/diff-viewer.tsx
|
|
@@ -4097,6 +4292,8 @@ function ToolCallBlock({
|
|
|
4097
4292
|
case "web_search":
|
|
4098
4293
|
case "web_crawl":
|
|
4099
4294
|
return /* @__PURE__ */ jsxRuntime.jsx(WebToolBlock, { tc });
|
|
4295
|
+
case "vision_analyze":
|
|
4296
|
+
return /* @__PURE__ */ jsxRuntime.jsx(VisionAnalyzeBlock, { tc });
|
|
4100
4297
|
case "read_file":
|
|
4101
4298
|
return /* @__PURE__ */ jsxRuntime.jsx(ReadFileBlock, { tc, onFileSelect });
|
|
4102
4299
|
case "write_file":
|
|
@@ -5617,18 +5814,21 @@ function ChatComposerInner({
|
|
|
5617
5814
|
[menuOpen, filteredCommands, selectedIndex, handleCommandSelect, textInput]
|
|
5618
5815
|
);
|
|
5619
5816
|
const handleSubmit = react.useCallback(
|
|
5620
|
-
(message) => {
|
|
5817
|
+
async (message) => {
|
|
5621
5818
|
const text = message.text.trim();
|
|
5622
|
-
if (!text ||
|
|
5819
|
+
if (!text || disabled) return;
|
|
5623
5820
|
const images = [];
|
|
5624
5821
|
for (const file of message.files) {
|
|
5625
5822
|
if (file.mediaType?.startsWith("image/") && file.url) {
|
|
5626
5823
|
images.push({ data: file.url, mimeType: file.mediaType });
|
|
5627
5824
|
}
|
|
5628
5825
|
}
|
|
5629
|
-
|
|
5826
|
+
if (isRunning) {
|
|
5827
|
+
await onStop();
|
|
5828
|
+
}
|
|
5829
|
+
await onSend(text, images.length > 0 ? images : void 0);
|
|
5630
5830
|
},
|
|
5631
|
-
[onSend, isRunning, disabled]
|
|
5831
|
+
[onSend, onStop, isRunning, disabled]
|
|
5632
5832
|
);
|
|
5633
5833
|
return /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: menuOpen, onOpenChange: (open) => {
|
|
5634
5834
|
if (!open) {
|
|
@@ -7489,10 +7689,9 @@ function WorkspacePanel({
|
|
|
7489
7689
|
sessionId,
|
|
7490
7690
|
selectedPath,
|
|
7491
7691
|
onSelectedPathChange,
|
|
7492
|
-
collapsed = false,
|
|
7493
|
-
onCollapsedChange,
|
|
7494
7692
|
refreshSignal = 0,
|
|
7495
|
-
disabled = false
|
|
7693
|
+
disabled = false,
|
|
7694
|
+
fillParent = false
|
|
7496
7695
|
}) {
|
|
7497
7696
|
const fileInputRef = react.useRef(null);
|
|
7498
7697
|
const [entries, setEntries] = react.useState([]);
|
|
@@ -7693,34 +7892,17 @@ function WorkspacePanel({
|
|
|
7693
7892
|
window.removeEventListener("mouseup", onMouseUp);
|
|
7694
7893
|
};
|
|
7695
7894
|
}, []);
|
|
7696
|
-
if (collapsed) {
|
|
7697
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7698
|
-
"aside",
|
|
7699
|
-
{
|
|
7700
|
-
"data-testid": "workspace-panel",
|
|
7701
|
-
role: "button",
|
|
7702
|
-
tabIndex: 0,
|
|
7703
|
-
className: "relative z-10 flex min-h-0 w-10 shrink-0 cursor-pointer items-center justify-center border-l border-muted-foreground/20 bg-card text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/30",
|
|
7704
|
-
"aria-label": "Expand workspace",
|
|
7705
|
-
onClick: () => onCollapsedChange?.(false),
|
|
7706
|
-
onKeyDown: (event) => {
|
|
7707
|
-
if (event.key === "Enter" || event.key === " ") {
|
|
7708
|
-
event.preventDefault();
|
|
7709
|
-
onCollapsedChange?.(false);
|
|
7710
|
-
}
|
|
7711
|
-
},
|
|
7712
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FolderOpenIcon, { className: "size-4 text-amber-500" })
|
|
7713
|
-
}
|
|
7714
|
-
);
|
|
7715
|
-
}
|
|
7716
7895
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7717
7896
|
"aside",
|
|
7718
7897
|
{
|
|
7719
7898
|
"data-testid": "workspace-panel",
|
|
7720
|
-
className:
|
|
7721
|
-
|
|
7899
|
+
className: cn(
|
|
7900
|
+
"relative z-10 flex h-full min-h-0 flex-col overflow-hidden border-l border-muted-foreground/20 bg-card",
|
|
7901
|
+
fillParent && "w-full"
|
|
7902
|
+
),
|
|
7903
|
+
style: fillParent ? void 0 : { width, minWidth: MIN_WIDTH, maxWidth: MAX_WIDTH },
|
|
7722
7904
|
children: [
|
|
7723
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7905
|
+
!fillParent && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7724
7906
|
"div",
|
|
7725
7907
|
{
|
|
7726
7908
|
className: "absolute inset-y-0 left-0 z-20 w-1.5 cursor-col-resize transition-colors hover:bg-primary/20 active:bg-primary/30",
|
|
@@ -7746,19 +7928,6 @@ function WorkspacePanel({
|
|
|
7746
7928
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-sm font-medium text-foreground", children: rootName }),
|
|
7747
7929
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-xs text-faint", children: disabled ? "Workspace - Read-only" : "Workspace" })
|
|
7748
7930
|
] }),
|
|
7749
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
7750
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
7751
|
-
Button,
|
|
7752
|
-
{
|
|
7753
|
-
variant: "ghost",
|
|
7754
|
-
size: "icon-sm",
|
|
7755
|
-
onClick: () => onCollapsedChange?.(true),
|
|
7756
|
-
"aria-label": "Collapse workspace",
|
|
7757
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRightIcon, { className: "size-4" })
|
|
7758
|
-
}
|
|
7759
|
-
) }),
|
|
7760
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "bottom", children: "Collapse" })
|
|
7761
|
-
] }),
|
|
7762
7931
|
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
7763
7932
|
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
7764
7933
|
Button,
|
|
@@ -7931,7 +8100,8 @@ var WORKSPACE_MUTATING_TOOLS = /* @__PURE__ */ new Set([
|
|
|
7931
8100
|
"terminal",
|
|
7932
8101
|
"write_file",
|
|
7933
8102
|
"patch",
|
|
7934
|
-
"execute_code"
|
|
8103
|
+
"execute_code",
|
|
8104
|
+
"browser_screenshot"
|
|
7935
8105
|
]);
|
|
7936
8106
|
var AGENT_CHAT_LISTENED_EVENTS = [
|
|
7937
8107
|
"user.message",
|
|
@@ -8857,7 +9027,6 @@ function AgentChat({
|
|
|
8857
9027
|
disabled
|
|
8858
9028
|
}) {
|
|
8859
9029
|
const [workspacePath, setWorkspacePath] = react.useState(null);
|
|
8860
|
-
const [workspaceCollapsed, setWorkspaceCollapsed] = react.useState(false);
|
|
8861
9030
|
const runtime = useAgentChatRuntime({
|
|
8862
9031
|
adapter,
|
|
8863
9032
|
agentId,
|
|
@@ -8868,6 +9037,7 @@ function AgentChat({
|
|
|
8868
9037
|
const effectiveDisabled = disabled || isReadOnlySession;
|
|
8869
9038
|
const disabledReason = isReadOnlySession ? "Scheduled run is read-only" : void 0;
|
|
8870
9039
|
const browserState = runtime.state.browser;
|
|
9040
|
+
const hasBrowserPanel = browserState !== null && sessionId;
|
|
8871
9041
|
react.useEffect(() => {
|
|
8872
9042
|
onMessagesChange?.(runtime.messages);
|
|
8873
9043
|
}, [onMessagesChange, runtime.messages]);
|
|
@@ -8886,53 +9056,769 @@ function AgentChat({
|
|
|
8886
9056
|
sessionId,
|
|
8887
9057
|
onFileSelect: handleFileSelect
|
|
8888
9058
|
},
|
|
8889
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
|
|
8893
|
-
|
|
8894
|
-
|
|
8895
|
-
|
|
8896
|
-
|
|
8897
|
-
|
|
8898
|
-
|
|
8899
|
-
|
|
8900
|
-
|
|
8901
|
-
|
|
8902
|
-
|
|
8903
|
-
|
|
8904
|
-
|
|
8905
|
-
|
|
8906
|
-
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
8916
|
-
|
|
8917
|
-
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
|
|
8921
|
-
|
|
8922
|
-
|
|
8923
|
-
|
|
8924
|
-
|
|
8925
|
-
|
|
8926
|
-
|
|
8927
|
-
|
|
8928
|
-
|
|
8929
|
-
|
|
8930
|
-
|
|
8931
|
-
|
|
8932
|
-
|
|
8933
|
-
|
|
8934
|
-
|
|
8935
|
-
|
|
9059
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9060
|
+
"section",
|
|
9061
|
+
{
|
|
9062
|
+
"data-testid": "agent-chat-layout",
|
|
9063
|
+
className: hasBrowserPanel ? "relative min-h-0 flex-1 overflow-hidden bg-background text-sm text-foreground" : "flex min-h-0 flex-1 overflow-hidden bg-background text-sm text-foreground",
|
|
9064
|
+
style: { direction: "ltr" },
|
|
9065
|
+
children: [
|
|
9066
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9067
|
+
"div",
|
|
9068
|
+
{
|
|
9069
|
+
"data-testid": "chat-panel",
|
|
9070
|
+
className: hasBrowserPanel ? "absolute inset-y-0 left-0 flex min-h-0 min-w-0 flex-col overflow-hidden" : "flex min-w-0 flex-1 flex-col overflow-hidden",
|
|
9071
|
+
style: hasBrowserPanel ? { right: 440 } : void 0,
|
|
9072
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
9073
|
+
ChatThread,
|
|
9074
|
+
{
|
|
9075
|
+
sessionId,
|
|
9076
|
+
messages: runtime.messages,
|
|
9077
|
+
isRunning: runtime.isRunning,
|
|
9078
|
+
isLoadingHistory: runtime.isLoadingHistory,
|
|
9079
|
+
onSend: (content, images) => runtime.send(content, images),
|
|
9080
|
+
onStop: () => runtime.stop(),
|
|
9081
|
+
onRetry: runtime.retry,
|
|
9082
|
+
onFileSelect: handleFileSelect,
|
|
9083
|
+
disabled: effectiveDisabled,
|
|
9084
|
+
disabledReason,
|
|
9085
|
+
tokenUsage: runtime.tokenUsage,
|
|
9086
|
+
retryIndicator: runtime.retryIndicator
|
|
9087
|
+
}
|
|
9088
|
+
)
|
|
9089
|
+
}
|
|
9090
|
+
),
|
|
9091
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
9092
|
+
"div",
|
|
9093
|
+
{
|
|
9094
|
+
"data-testid": "right-stack",
|
|
9095
|
+
className: hasBrowserPanel ? "absolute inset-y-0 flex min-h-0 flex-col overflow-hidden" : "flex min-h-0 shrink-0 flex-col",
|
|
9096
|
+
style: hasBrowserPanel ? { right: 0, width: 440 } : void 0,
|
|
9097
|
+
children: [
|
|
9098
|
+
hasBrowserPanel && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9099
|
+
"div",
|
|
9100
|
+
{
|
|
9101
|
+
"data-testid": "browser-panel",
|
|
9102
|
+
className: "h-1/2 min-h-0 w-full overflow-hidden border-b border-line",
|
|
9103
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
9104
|
+
BrowserPane,
|
|
9105
|
+
{
|
|
9106
|
+
sessionId,
|
|
9107
|
+
state: browserState,
|
|
9108
|
+
adapter
|
|
9109
|
+
}
|
|
9110
|
+
)
|
|
9111
|
+
}
|
|
9112
|
+
),
|
|
9113
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9114
|
+
"div",
|
|
9115
|
+
{
|
|
9116
|
+
"data-testid": "workspace-panel-frame",
|
|
9117
|
+
className: hasBrowserPanel ? "h-1/2 min-h-0 w-full overflow-hidden" : "min-h-0",
|
|
9118
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
9119
|
+
WorkspacePanel,
|
|
9120
|
+
{
|
|
9121
|
+
adapter,
|
|
9122
|
+
sessionId,
|
|
9123
|
+
selectedPath: workspacePath,
|
|
9124
|
+
onSelectedPathChange: setWorkspacePath,
|
|
9125
|
+
refreshSignal: runtime.workspaceRefreshKey,
|
|
9126
|
+
disabled: effectiveDisabled,
|
|
9127
|
+
fillParent: Boolean(hasBrowserPanel)
|
|
9128
|
+
}
|
|
9129
|
+
)
|
|
9130
|
+
}
|
|
9131
|
+
)
|
|
9132
|
+
]
|
|
9133
|
+
}
|
|
9134
|
+
)
|
|
9135
|
+
]
|
|
9136
|
+
}
|
|
9137
|
+
) })
|
|
9138
|
+
}
|
|
9139
|
+
);
|
|
9140
|
+
}
|
|
9141
|
+
init_utils();
|
|
9142
|
+
var DEFAULT_LIMIT = 50;
|
|
9143
|
+
function requireInboxAdapter(adapter) {
|
|
9144
|
+
if (!adapter.listInbox || !adapter.getInboxItem || !adapter.markInboxItemRead || !adapter.acknowledgeInboxItem || !adapter.respondGovernanceInboxItem || !adapter.openInboxStream) {
|
|
9145
|
+
throw new Error("Inbox is not supported by this adapter.");
|
|
9146
|
+
}
|
|
9147
|
+
return adapter;
|
|
9148
|
+
}
|
|
9149
|
+
var KIND_LABEL2 = {
|
|
9150
|
+
input_required: "Input needed",
|
|
9151
|
+
action_required: "Action needed",
|
|
9152
|
+
task_complete: "Task complete",
|
|
9153
|
+
governance_gate: "Approval",
|
|
9154
|
+
progress_checkin: "Progress"
|
|
9155
|
+
};
|
|
9156
|
+
function kindLabel(kind) {
|
|
9157
|
+
return KIND_LABEL2[kind] ?? kind.replace(/[_-]+/g, " ");
|
|
9158
|
+
}
|
|
9159
|
+
function statusLabel(status) {
|
|
9160
|
+
return status.split(/[_-]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
9161
|
+
}
|
|
9162
|
+
function formatRelative(value) {
|
|
9163
|
+
const date = new Date(value);
|
|
9164
|
+
if (Number.isNaN(date.getTime())) return "";
|
|
9165
|
+
return dateFns.formatDistanceToNow(date, { addSuffix: true });
|
|
9166
|
+
}
|
|
9167
|
+
function kindIcon(kind) {
|
|
9168
|
+
if (kind === "input_required") return lucideReact.MessageSquareIcon;
|
|
9169
|
+
if (kind === "action_required") return lucideReact.ExternalLinkIcon;
|
|
9170
|
+
if (kind === "task_complete") return lucideReact.ClipboardCheckIcon;
|
|
9171
|
+
if (kind === "governance_gate") return lucideReact.ShieldAlertIcon;
|
|
9172
|
+
if (kind === "progress_checkin") return lucideReact.TimerIcon;
|
|
9173
|
+
return lucideReact.CircleDotIcon;
|
|
9174
|
+
}
|
|
9175
|
+
function sortItems(items) {
|
|
9176
|
+
return [...items].sort((a, b) => {
|
|
9177
|
+
if (a.createdAt !== b.createdAt) return a.createdAt > b.createdAt ? -1 : 1;
|
|
9178
|
+
return b.id - a.id;
|
|
9179
|
+
});
|
|
9180
|
+
}
|
|
9181
|
+
function QuestionInput({
|
|
9182
|
+
prompt,
|
|
9183
|
+
choices,
|
|
9184
|
+
disabled,
|
|
9185
|
+
value,
|
|
9186
|
+
onChange
|
|
9187
|
+
}) {
|
|
9188
|
+
if (choices && choices.length > 0) {
|
|
9189
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9190
|
+
"select",
|
|
9191
|
+
{
|
|
9192
|
+
"aria-label": prompt,
|
|
9193
|
+
className: "h-9 w-full border border-line bg-background px-2 text-sm text-foreground outline-none focus:border-primary disabled:opacity-50",
|
|
9194
|
+
disabled,
|
|
9195
|
+
value,
|
|
9196
|
+
onChange: (event) => onChange(event.target.value),
|
|
9197
|
+
children: [
|
|
9198
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "Select" }),
|
|
9199
|
+
choices.map((choice) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: choice.label, children: choice.label }, choice.label))
|
|
9200
|
+
]
|
|
9201
|
+
}
|
|
9202
|
+
);
|
|
9203
|
+
}
|
|
9204
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9205
|
+
"input",
|
|
9206
|
+
{
|
|
9207
|
+
"aria-label": prompt,
|
|
9208
|
+
className: "h-9 w-full border border-line bg-background px-2 text-sm text-foreground outline-none focus:border-primary disabled:opacity-50",
|
|
9209
|
+
disabled,
|
|
9210
|
+
value,
|
|
9211
|
+
onChange: (event) => onChange(event.target.value)
|
|
9212
|
+
}
|
|
9213
|
+
);
|
|
9214
|
+
}
|
|
9215
|
+
function InboxDetailActions({
|
|
9216
|
+
item,
|
|
9217
|
+
adapter,
|
|
9218
|
+
onDeleted,
|
|
9219
|
+
onSessionSelect,
|
|
9220
|
+
children
|
|
9221
|
+
}) {
|
|
9222
|
+
const [deleting, setDeleting] = react.useState(false);
|
|
9223
|
+
async function deleteItem() {
|
|
9224
|
+
if (!adapter.deleteInboxItem) return;
|
|
9225
|
+
setDeleting(true);
|
|
9226
|
+
try {
|
|
9227
|
+
await onDeleted(item.id);
|
|
9228
|
+
} finally {
|
|
9229
|
+
setDeleting(false);
|
|
9230
|
+
}
|
|
9231
|
+
}
|
|
9232
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-2", children: [
|
|
9233
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
9234
|
+
Button,
|
|
9235
|
+
{
|
|
9236
|
+
type: "button",
|
|
9237
|
+
size: "sm",
|
|
9238
|
+
onClick: () => onSessionSelect?.(item.sessionId),
|
|
9239
|
+
"aria-label": "Open session",
|
|
9240
|
+
children: [
|
|
9241
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLinkIcon, { className: "size-3.5" }),
|
|
9242
|
+
"Open session"
|
|
9243
|
+
]
|
|
9244
|
+
}
|
|
9245
|
+
),
|
|
9246
|
+
children,
|
|
9247
|
+
adapter.deleteInboxItem && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9248
|
+
Button,
|
|
9249
|
+
{
|
|
9250
|
+
type: "button",
|
|
9251
|
+
size: "sm",
|
|
9252
|
+
variant: "ghost",
|
|
9253
|
+
onClick: () => void deleteItem(),
|
|
9254
|
+
disabled: deleting,
|
|
9255
|
+
"aria-label": "Delete inbox item",
|
|
9256
|
+
title: "Delete inbox item",
|
|
9257
|
+
children: [
|
|
9258
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2Icon, { className: "size-3.5" }),
|
|
9259
|
+
deleting ? "Deleting" : "Delete"
|
|
9260
|
+
]
|
|
9261
|
+
}
|
|
9262
|
+
)
|
|
9263
|
+
] });
|
|
9264
|
+
}
|
|
9265
|
+
function InputRequiredDetail({
|
|
9266
|
+
item,
|
|
9267
|
+
adapter,
|
|
9268
|
+
onUpdated,
|
|
9269
|
+
onDeleted,
|
|
9270
|
+
onSessionSelect
|
|
9271
|
+
}) {
|
|
9272
|
+
const questions = Array.isArray(item.payload.questions) ? item.payload.questions.map((question) => ({
|
|
9273
|
+
prompt: typeof question.prompt === "string" ? question.prompt : "",
|
|
9274
|
+
choices: Array.isArray(question.choices) ? question.choices.map((choice) => ({
|
|
9275
|
+
label: typeof choice.label === "string" ? choice.label : "",
|
|
9276
|
+
description: typeof choice.description === "string" ? choice.description : void 0
|
|
9277
|
+
})).filter((choice) => choice.label) : void 0
|
|
9278
|
+
})).filter((question) => question.prompt) : [];
|
|
9279
|
+
const [answers, setAnswers] = react.useState({});
|
|
9280
|
+
const [submitting, setSubmitting] = react.useState(false);
|
|
9281
|
+
const disabled = item.status !== "pending" || submitting;
|
|
9282
|
+
const canSubmit = questions.every((question) => answers[question.prompt]?.trim());
|
|
9283
|
+
async function submit() {
|
|
9284
|
+
const toolCallId = typeof item.payload.tool_call_id === "string" ? item.payload.tool_call_id : "";
|
|
9285
|
+
if (!toolCallId || !canSubmit) return;
|
|
9286
|
+
setSubmitting(true);
|
|
9287
|
+
try {
|
|
9288
|
+
const responses = questions.map((question) => ({
|
|
9289
|
+
question: question.prompt,
|
|
9290
|
+
answer: answers[question.prompt]?.trim() ?? "",
|
|
9291
|
+
is_other: false
|
|
9292
|
+
}));
|
|
9293
|
+
await adapter.submitClarifyResponse({
|
|
9294
|
+
sessionId: item.sessionId,
|
|
9295
|
+
toolCallId,
|
|
9296
|
+
responses
|
|
9297
|
+
});
|
|
9298
|
+
onUpdated(await adapter.getInboxItem({ itemId: item.id }));
|
|
9299
|
+
} finally {
|
|
9300
|
+
setSubmitting(false);
|
|
9301
|
+
}
|
|
9302
|
+
}
|
|
9303
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
9304
|
+
item.body && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap text-sm text-muted-foreground", children: item.body }),
|
|
9305
|
+
questions.map((question) => /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block space-y-1.5", children: [
|
|
9306
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-foreground", children: question.prompt }),
|
|
9307
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9308
|
+
QuestionInput,
|
|
9309
|
+
{
|
|
9310
|
+
prompt: question.prompt,
|
|
9311
|
+
choices: question.choices,
|
|
9312
|
+
disabled,
|
|
9313
|
+
value: answers[question.prompt] ?? "",
|
|
9314
|
+
onChange: (value) => setAnswers((current) => ({
|
|
9315
|
+
...current,
|
|
9316
|
+
[question.prompt]: value
|
|
9317
|
+
}))
|
|
9318
|
+
}
|
|
9319
|
+
)
|
|
9320
|
+
] }, question.prompt)),
|
|
9321
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9322
|
+
InboxDetailActions,
|
|
9323
|
+
{
|
|
9324
|
+
item,
|
|
9325
|
+
adapter,
|
|
9326
|
+
onDeleted,
|
|
9327
|
+
onSessionSelect,
|
|
9328
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
9329
|
+
Button,
|
|
9330
|
+
{
|
|
9331
|
+
type: "button",
|
|
9332
|
+
size: "sm",
|
|
9333
|
+
onClick: () => void submit(),
|
|
9334
|
+
disabled: disabled || !canSubmit,
|
|
9335
|
+
"aria-label": "Submit inbox response",
|
|
9336
|
+
children: submitting ? "Submitting" : "Submit"
|
|
9337
|
+
}
|
|
9338
|
+
)
|
|
9339
|
+
}
|
|
9340
|
+
)
|
|
9341
|
+
] });
|
|
9342
|
+
}
|
|
9343
|
+
function AckDetail({
|
|
9344
|
+
item,
|
|
9345
|
+
adapter,
|
|
9346
|
+
onUpdated,
|
|
9347
|
+
onDeleted,
|
|
9348
|
+
onSessionSelect
|
|
9349
|
+
}) {
|
|
9350
|
+
async function acknowledge() {
|
|
9351
|
+
onUpdated(await adapter.acknowledgeInboxItem({ itemId: item.id }));
|
|
9352
|
+
}
|
|
9353
|
+
const outcome = typeof item.payload.outcome === "string" ? item.payload.outcome : "";
|
|
9354
|
+
const duration = typeof item.payload.duration_seconds === "number" ? item.payload.duration_seconds : null;
|
|
9355
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
9356
|
+
outcome && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "secondary", children: outcome }),
|
|
9357
|
+
item.body && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap text-sm", children: item.body }),
|
|
9358
|
+
typeof item.payload.error === "string" && /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "overflow-x-auto bg-destructive/10 p-3 text-xs text-destructive", children: item.payload.error }),
|
|
9359
|
+
duration !== null && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
9360
|
+
"Duration: ",
|
|
9361
|
+
Math.round(duration / 60),
|
|
9362
|
+
" min (",
|
|
9363
|
+
duration,
|
|
9364
|
+
" s)"
|
|
9365
|
+
] }),
|
|
9366
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9367
|
+
InboxDetailActions,
|
|
9368
|
+
{
|
|
9369
|
+
item,
|
|
9370
|
+
adapter,
|
|
9371
|
+
onDeleted,
|
|
9372
|
+
onSessionSelect,
|
|
9373
|
+
children: item.status === "pending" && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9374
|
+
Button,
|
|
9375
|
+
{
|
|
9376
|
+
type: "button",
|
|
9377
|
+
size: "sm",
|
|
9378
|
+
onClick: () => void acknowledge(),
|
|
9379
|
+
"aria-label": "Acknowledge inbox item",
|
|
9380
|
+
children: [
|
|
9381
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckIcon, { className: "size-3.5" }),
|
|
9382
|
+
"Acknowledge"
|
|
9383
|
+
]
|
|
9384
|
+
}
|
|
9385
|
+
)
|
|
9386
|
+
}
|
|
9387
|
+
)
|
|
9388
|
+
] });
|
|
9389
|
+
}
|
|
9390
|
+
function GovernanceDetail({
|
|
9391
|
+
item,
|
|
9392
|
+
adapter,
|
|
9393
|
+
onUpdated,
|
|
9394
|
+
onDeleted,
|
|
9395
|
+
onSessionSelect
|
|
9396
|
+
}) {
|
|
9397
|
+
async function decide(decision) {
|
|
9398
|
+
onUpdated(
|
|
9399
|
+
await adapter.respondGovernanceInboxItem({ itemId: item.id, decision })
|
|
9400
|
+
);
|
|
9401
|
+
}
|
|
9402
|
+
const toolName = typeof item.payload.tool_name === "string" ? item.payload.tool_name : "tool";
|
|
9403
|
+
const args = typeof item.payload.arguments_excerpt === "string" ? item.payload.arguments_excerpt : "";
|
|
9404
|
+
const reason = typeof item.payload.deny_reason === "string" ? item.payload.deny_reason : "";
|
|
9405
|
+
const disabled = item.status !== "pending";
|
|
9406
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
9407
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
9408
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-base font-semibold text-foreground", children: [
|
|
9409
|
+
"Approve ",
|
|
9410
|
+
toolName,
|
|
9411
|
+
"?"
|
|
9412
|
+
] }),
|
|
9413
|
+
reason && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: reason })
|
|
9414
|
+
] }),
|
|
9415
|
+
args && /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "overflow-x-auto border border-line bg-muted p-3 text-xs", children: args }),
|
|
9416
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
9417
|
+
InboxDetailActions,
|
|
9418
|
+
{
|
|
9419
|
+
item,
|
|
9420
|
+
adapter,
|
|
9421
|
+
onDeleted,
|
|
9422
|
+
onSessionSelect,
|
|
9423
|
+
children: [
|
|
9424
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9425
|
+
Button,
|
|
9426
|
+
{
|
|
9427
|
+
type: "button",
|
|
9428
|
+
size: "sm",
|
|
9429
|
+
disabled,
|
|
9430
|
+
onClick: () => void decide("approve"),
|
|
9431
|
+
children: "Approve"
|
|
9432
|
+
}
|
|
9433
|
+
),
|
|
9434
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9435
|
+
Button,
|
|
9436
|
+
{
|
|
9437
|
+
type: "button",
|
|
9438
|
+
size: "sm",
|
|
9439
|
+
variant: "destructive",
|
|
9440
|
+
disabled,
|
|
9441
|
+
onClick: () => void decide("reject"),
|
|
9442
|
+
children: "Reject"
|
|
9443
|
+
}
|
|
9444
|
+
)
|
|
9445
|
+
]
|
|
9446
|
+
}
|
|
9447
|
+
)
|
|
9448
|
+
] });
|
|
9449
|
+
}
|
|
9450
|
+
function ActionRequiredDetail({
|
|
9451
|
+
item,
|
|
9452
|
+
adapter,
|
|
9453
|
+
onUpdated,
|
|
9454
|
+
onDeleted,
|
|
9455
|
+
onSessionSelect
|
|
9456
|
+
}) {
|
|
9457
|
+
const [submitting, setSubmitting] = react.useState(false);
|
|
9458
|
+
const actionType = typeof item.payload.action_type === "string" ? item.payload.action_type : "";
|
|
9459
|
+
const context = typeof item.payload.context === "string" ? item.payload.context : "";
|
|
9460
|
+
const disabled = item.status !== "pending" || submitting || !adapter.respondActionRequiredInboxItem;
|
|
9461
|
+
async function complete() {
|
|
9462
|
+
if (!adapter.respondActionRequiredInboxItem) return;
|
|
9463
|
+
setSubmitting(true);
|
|
9464
|
+
try {
|
|
9465
|
+
onUpdated(await adapter.respondActionRequiredInboxItem({ itemId: item.id }));
|
|
9466
|
+
} finally {
|
|
9467
|
+
setSubmitting(false);
|
|
9468
|
+
}
|
|
9469
|
+
}
|
|
9470
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
9471
|
+
item.body && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap text-sm", children: item.body }),
|
|
9472
|
+
context && context !== item.body && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap text-sm text-muted-foreground", children: context }),
|
|
9473
|
+
actionType && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "secondary", children: actionType }),
|
|
9474
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9475
|
+
InboxDetailActions,
|
|
9476
|
+
{
|
|
9477
|
+
item,
|
|
9478
|
+
adapter,
|
|
9479
|
+
onDeleted,
|
|
9480
|
+
onSessionSelect,
|
|
9481
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9482
|
+
Button,
|
|
9483
|
+
{
|
|
9484
|
+
type: "button",
|
|
9485
|
+
size: "sm",
|
|
9486
|
+
onClick: () => void complete(),
|
|
9487
|
+
disabled,
|
|
9488
|
+
"aria-label": "Mark action complete",
|
|
9489
|
+
children: [
|
|
9490
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckIcon, { className: "size-3.5" }),
|
|
9491
|
+
submitting ? "Marking" : "I completed this"
|
|
9492
|
+
]
|
|
9493
|
+
}
|
|
9494
|
+
)
|
|
9495
|
+
}
|
|
9496
|
+
)
|
|
9497
|
+
] });
|
|
9498
|
+
}
|
|
9499
|
+
function ProgressDetail({
|
|
9500
|
+
item,
|
|
9501
|
+
adapter,
|
|
9502
|
+
onUpdated,
|
|
9503
|
+
onDeleted,
|
|
9504
|
+
onSessionSelect
|
|
9505
|
+
}) {
|
|
9506
|
+
async function acknowledge() {
|
|
9507
|
+
onUpdated(await adapter.acknowledgeInboxItem({ itemId: item.id }));
|
|
9508
|
+
}
|
|
9509
|
+
const rows = [];
|
|
9510
|
+
for (const row of [
|
|
9511
|
+
["Iterations", item.payload.iterations],
|
|
9512
|
+
["Last tool", item.payload.last_tool],
|
|
9513
|
+
["Elapsed", item.payload.elapsed_seconds]
|
|
9514
|
+
]) {
|
|
9515
|
+
if (row[1] !== void 0 && row[1] !== null && row[1] !== "") {
|
|
9516
|
+
rows.push(row);
|
|
9517
|
+
}
|
|
9518
|
+
}
|
|
9519
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
9520
|
+
item.body && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap text-sm", children: item.body }),
|
|
9521
|
+
rows.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("dl", { className: "space-y-1 text-xs text-muted-foreground", children: rows.map(([label, value]) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
|
|
9522
|
+
/* @__PURE__ */ jsxRuntime.jsx("dt", { className: "w-20 shrink-0", children: String(label) }),
|
|
9523
|
+
/* @__PURE__ */ jsxRuntime.jsx("dd", { className: "min-w-0", children: String(value) })
|
|
9524
|
+
] }, String(label))) }),
|
|
9525
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9526
|
+
InboxDetailActions,
|
|
9527
|
+
{
|
|
9528
|
+
item,
|
|
9529
|
+
adapter,
|
|
9530
|
+
onDeleted,
|
|
9531
|
+
onSessionSelect,
|
|
9532
|
+
children: item.status === "pending" && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9533
|
+
Button,
|
|
9534
|
+
{
|
|
9535
|
+
type: "button",
|
|
9536
|
+
size: "sm",
|
|
9537
|
+
onClick: () => void acknowledge(),
|
|
9538
|
+
"aria-label": "Acknowledge inbox item",
|
|
9539
|
+
children: [
|
|
9540
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckIcon, { className: "size-3.5" }),
|
|
9541
|
+
"Acknowledge"
|
|
9542
|
+
]
|
|
9543
|
+
}
|
|
9544
|
+
)
|
|
9545
|
+
}
|
|
9546
|
+
)
|
|
9547
|
+
] });
|
|
9548
|
+
}
|
|
9549
|
+
function InboxDetail({
|
|
9550
|
+
item,
|
|
9551
|
+
adapter,
|
|
9552
|
+
onUpdated,
|
|
9553
|
+
onDeleted,
|
|
9554
|
+
onSessionSelect
|
|
9555
|
+
}) {
|
|
9556
|
+
const Icon = kindIcon(item.kind);
|
|
9557
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "min-w-0 flex-1 overflow-y-auto p-6", children: [
|
|
9558
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-5 flex min-w-0 items-start gap-3", children: [
|
|
9559
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-0.5 flex size-8 shrink-0 items-center justify-center border border-line text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "size-4" }) }),
|
|
9560
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
9561
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
9562
|
+
/* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "secondary", children: kindLabel(item.kind) }),
|
|
9563
|
+
/* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: item.status === "pending" ? "default" : "secondary", children: statusLabel(item.status) })
|
|
9564
|
+
] }),
|
|
9565
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "mt-2 text-lg font-semibold leading-snug text-foreground", children: item.title })
|
|
9566
|
+
] })
|
|
9567
|
+
] }),
|
|
9568
|
+
item.kind === "input_required" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
9569
|
+
InputRequiredDetail,
|
|
9570
|
+
{
|
|
9571
|
+
item,
|
|
9572
|
+
adapter,
|
|
9573
|
+
onUpdated,
|
|
9574
|
+
onDeleted,
|
|
9575
|
+
onSessionSelect
|
|
9576
|
+
}
|
|
9577
|
+
) : item.kind === "action_required" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
9578
|
+
ActionRequiredDetail,
|
|
9579
|
+
{
|
|
9580
|
+
item,
|
|
9581
|
+
adapter,
|
|
9582
|
+
onUpdated,
|
|
9583
|
+
onDeleted,
|
|
9584
|
+
onSessionSelect
|
|
9585
|
+
}
|
|
9586
|
+
) : item.kind === "governance_gate" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
9587
|
+
GovernanceDetail,
|
|
9588
|
+
{
|
|
9589
|
+
item,
|
|
9590
|
+
adapter,
|
|
9591
|
+
onUpdated,
|
|
9592
|
+
onDeleted,
|
|
9593
|
+
onSessionSelect
|
|
9594
|
+
}
|
|
9595
|
+
) : item.kind === "progress_checkin" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
9596
|
+
ProgressDetail,
|
|
9597
|
+
{
|
|
9598
|
+
item,
|
|
9599
|
+
adapter,
|
|
9600
|
+
onUpdated,
|
|
9601
|
+
onDeleted,
|
|
9602
|
+
onSessionSelect
|
|
9603
|
+
}
|
|
9604
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
9605
|
+
AckDetail,
|
|
9606
|
+
{
|
|
9607
|
+
item,
|
|
9608
|
+
adapter,
|
|
9609
|
+
onUpdated,
|
|
9610
|
+
onDeleted,
|
|
9611
|
+
onSessionSelect
|
|
9612
|
+
}
|
|
9613
|
+
)
|
|
9614
|
+
] });
|
|
9615
|
+
}
|
|
9616
|
+
function InboxPanel({
|
|
9617
|
+
adapter,
|
|
9618
|
+
title = "Inbox",
|
|
9619
|
+
selectedId,
|
|
9620
|
+
onSelectedIdChange,
|
|
9621
|
+
onSessionSelect,
|
|
9622
|
+
hideHeader = false,
|
|
9623
|
+
limit = DEFAULT_LIMIT
|
|
9624
|
+
}) {
|
|
9625
|
+
const inboxAdapter = react.useMemo(() => requireInboxAdapter(adapter), [adapter]);
|
|
9626
|
+
const [items, setItems] = react.useState([]);
|
|
9627
|
+
const [nextCursor, setNextCursor] = react.useState(null);
|
|
9628
|
+
const [internalSelectedId, setInternalSelectedId] = react.useState(null);
|
|
9629
|
+
const [loading, setLoading] = react.useState(false);
|
|
9630
|
+
const [error, setError] = react.useState(null);
|
|
9631
|
+
const requestId = react.useRef(0);
|
|
9632
|
+
const selectedItemId = selectedId ?? internalSelectedId;
|
|
9633
|
+
const selectedItem = react.useMemo(
|
|
9634
|
+
() => items.find((item) => item.id === selectedItemId) ?? null,
|
|
9635
|
+
[items, selectedItemId]
|
|
9636
|
+
);
|
|
9637
|
+
const applyItem = react.useCallback((nextItem) => {
|
|
9638
|
+
setItems(
|
|
9639
|
+
(current) => sortItems(
|
|
9640
|
+
current.map((item) => item.id === nextItem.id ? nextItem : item)
|
|
9641
|
+
)
|
|
9642
|
+
);
|
|
9643
|
+
}, []);
|
|
9644
|
+
const selectItem = react.useCallback(
|
|
9645
|
+
async (itemId) => {
|
|
9646
|
+
if (selectedId === void 0) setInternalSelectedId(itemId);
|
|
9647
|
+
onSelectedIdChange?.(itemId);
|
|
9648
|
+
const item = await inboxAdapter.getInboxItem({ itemId });
|
|
9649
|
+
applyItem(item);
|
|
9650
|
+
if (!item.readAt) {
|
|
9651
|
+
applyItem(await inboxAdapter.markInboxItemRead({ itemId }));
|
|
9652
|
+
}
|
|
9653
|
+
},
|
|
9654
|
+
[applyItem, inboxAdapter, onSelectedIdChange, selectedId]
|
|
9655
|
+
);
|
|
9656
|
+
const load = react.useCallback(
|
|
9657
|
+
async (cursor) => {
|
|
9658
|
+
const id = ++requestId.current;
|
|
9659
|
+
setLoading(true);
|
|
9660
|
+
setError(null);
|
|
9661
|
+
try {
|
|
9662
|
+
const response = await inboxAdapter.listInbox({
|
|
9663
|
+
cursor: cursor ?? void 0,
|
|
9664
|
+
limit
|
|
9665
|
+
});
|
|
9666
|
+
if (id !== requestId.current) return;
|
|
9667
|
+
setItems(
|
|
9668
|
+
(current) => sortItems(cursor ? [...current, ...response.items] : response.items)
|
|
9669
|
+
);
|
|
9670
|
+
setNextCursor(response.nextCursor);
|
|
9671
|
+
} catch (err) {
|
|
9672
|
+
if (id === requestId.current) {
|
|
9673
|
+
setError(err instanceof Error ? err.message : "Failed to load inbox");
|
|
9674
|
+
}
|
|
9675
|
+
} finally {
|
|
9676
|
+
if (id === requestId.current) setLoading(false);
|
|
9677
|
+
}
|
|
9678
|
+
},
|
|
9679
|
+
[inboxAdapter, limit]
|
|
9680
|
+
);
|
|
9681
|
+
react.useEffect(() => {
|
|
9682
|
+
void load(null);
|
|
9683
|
+
}, [load]);
|
|
9684
|
+
react.useEffect(() => {
|
|
9685
|
+
const stream = inboxAdapter.openInboxStream();
|
|
9686
|
+
stream.addEventListener("item", (event) => {
|
|
9687
|
+
const payload = JSON.parse(event.data);
|
|
9688
|
+
if (typeof payload.item_id !== "number") return;
|
|
9689
|
+
void inboxAdapter.getInboxItem({ itemId: payload.item_id }).then(applyItem);
|
|
9690
|
+
});
|
|
9691
|
+
return () => stream.close();
|
|
9692
|
+
}, [applyItem, inboxAdapter]);
|
|
9693
|
+
function updateSelectedItem(item) {
|
|
9694
|
+
applyItem(item);
|
|
9695
|
+
}
|
|
9696
|
+
const deleteItem = react.useCallback(
|
|
9697
|
+
async (itemId) => {
|
|
9698
|
+
if (!inboxAdapter.deleteInboxItem) return;
|
|
9699
|
+
await inboxAdapter.deleteInboxItem({ itemId });
|
|
9700
|
+
setItems((current) => current.filter((item) => item.id !== itemId));
|
|
9701
|
+
if (selectedItemId === itemId) {
|
|
9702
|
+
if (selectedId === void 0) setInternalSelectedId(null);
|
|
9703
|
+
onSelectedIdChange?.(null);
|
|
9704
|
+
}
|
|
9705
|
+
},
|
|
9706
|
+
[inboxAdapter, onSelectedIdChange, selectedId, selectedItemId]
|
|
9707
|
+
);
|
|
9708
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full min-h-0 min-w-0 bg-background text-foreground", children: [
|
|
9709
|
+
/* @__PURE__ */ jsxRuntime.jsxs("aside", { className: "flex w-80 min-w-72 max-w-sm shrink-0 flex-col border-r border-line", children: [
|
|
9710
|
+
!hideHeader && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-14 items-center gap-2 border-b border-line px-4", children: [
|
|
9711
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.InboxIcon, { className: "size-4 text-muted-foreground" }),
|
|
9712
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "font-semibold", children: title })
|
|
9713
|
+
] }),
|
|
9714
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-h-0 flex-1 overflow-y-auto", children: [
|
|
9715
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-3 text-sm text-destructive", children: error }),
|
|
9716
|
+
!error && items.length === 0 && !loading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-8 text-sm text-muted-foreground", children: "No inbox items" }),
|
|
9717
|
+
items.map((item) => {
|
|
9718
|
+
const Icon = kindIcon(item.kind);
|
|
9719
|
+
const selected = item.id === selectedItemId;
|
|
9720
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9721
|
+
"button",
|
|
9722
|
+
{
|
|
9723
|
+
type: "button",
|
|
9724
|
+
"aria-label": `Open inbox item ${item.title}`,
|
|
9725
|
+
className: cn(
|
|
9726
|
+
"flex w-full min-w-0 gap-3 border-l-2 px-3 py-3 text-left transition-colors hover:bg-input",
|
|
9727
|
+
selected ? "border-l-primary bg-line text-foreground" : "border-l-transparent text-subtle",
|
|
9728
|
+
!item.readAt && "font-medium text-foreground"
|
|
9729
|
+
),
|
|
9730
|
+
onClick: () => void selectItem(item.id),
|
|
9731
|
+
children: [
|
|
9732
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "mt-0.5 size-4 shrink-0 text-muted-foreground" }),
|
|
9733
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
9734
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-center justify-between gap-2", children: [
|
|
9735
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-xs uppercase tracking-wide text-muted-foreground", children: kindLabel(item.kind) }),
|
|
9736
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: formatRelative(item.createdAt) })
|
|
9737
|
+
] }),
|
|
9738
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1 block truncate text-sm", children: item.title }),
|
|
9739
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1 block text-xs text-muted-foreground", children: statusLabel(item.status) })
|
|
9740
|
+
] })
|
|
9741
|
+
]
|
|
9742
|
+
},
|
|
9743
|
+
item.id
|
|
9744
|
+
);
|
|
9745
|
+
}),
|
|
9746
|
+
nextCursor && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9747
|
+
"button",
|
|
9748
|
+
{
|
|
9749
|
+
type: "button",
|
|
9750
|
+
className: "w-full px-3 py-3 text-sm text-muted-foreground hover:bg-input",
|
|
9751
|
+
onClick: () => void load(nextCursor),
|
|
9752
|
+
disabled: loading,
|
|
9753
|
+
children: loading ? "Loading" : "Load more"
|
|
9754
|
+
}
|
|
9755
|
+
)
|
|
9756
|
+
] })
|
|
9757
|
+
] }),
|
|
9758
|
+
selectedItem ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
9759
|
+
InboxDetail,
|
|
9760
|
+
{
|
|
9761
|
+
item: selectedItem,
|
|
9762
|
+
adapter: inboxAdapter,
|
|
9763
|
+
onUpdated: updateSelectedItem,
|
|
9764
|
+
onDeleted: deleteItem,
|
|
9765
|
+
onSessionSelect
|
|
9766
|
+
}
|
|
9767
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("main", { className: "flex min-w-0 flex-1 items-center justify-center px-4 text-sm text-muted-foreground", children: "Select an item" })
|
|
9768
|
+
] });
|
|
9769
|
+
}
|
|
9770
|
+
function useInboxUnreadCount(adapter) {
|
|
9771
|
+
const [unreadCount, setUnreadCount] = react.useState(0);
|
|
9772
|
+
const [hasLoaded, setHasLoaded] = react.useState(false);
|
|
9773
|
+
const [error, setError] = react.useState(null);
|
|
9774
|
+
react.useEffect(() => {
|
|
9775
|
+
let cancelled = false;
|
|
9776
|
+
let stream = null;
|
|
9777
|
+
if (!adapter.listInbox || !adapter.openInboxStream) {
|
|
9778
|
+
setError("Inbox is not supported by this adapter.");
|
|
9779
|
+
setHasLoaded(true);
|
|
9780
|
+
return () => {
|
|
9781
|
+
cancelled = true;
|
|
9782
|
+
};
|
|
9783
|
+
}
|
|
9784
|
+
adapter.listInbox({ status: "pending", limit: 200 }).then((response) => {
|
|
9785
|
+
if (cancelled) return;
|
|
9786
|
+
setUnreadCount(response.items.filter((item) => !item.readAt).length);
|
|
9787
|
+
setHasLoaded(true);
|
|
9788
|
+
}).catch((err) => {
|
|
9789
|
+
if (cancelled) return;
|
|
9790
|
+
setError(err instanceof Error ? err.message : "Failed to load inbox");
|
|
9791
|
+
setHasLoaded(true);
|
|
9792
|
+
});
|
|
9793
|
+
try {
|
|
9794
|
+
stream = adapter.openInboxStream();
|
|
9795
|
+
stream.addEventListener("snapshot", (event) => {
|
|
9796
|
+
if (cancelled) return;
|
|
9797
|
+
const payload = JSON.parse(event.data);
|
|
9798
|
+
const ids = Array.isArray(payload.unread_ids) ? payload.unread_ids : [];
|
|
9799
|
+
setUnreadCount(ids.length);
|
|
9800
|
+
setHasLoaded(true);
|
|
9801
|
+
});
|
|
9802
|
+
stream.addEventListener("item", () => {
|
|
9803
|
+
if (cancelled) return;
|
|
9804
|
+
setUnreadCount((count) => count + 1);
|
|
9805
|
+
setHasLoaded(true);
|
|
9806
|
+
});
|
|
9807
|
+
stream.onerror = () => {
|
|
9808
|
+
if (!cancelled) setError("Inbox stream disconnected.");
|
|
9809
|
+
};
|
|
9810
|
+
} catch (err) {
|
|
9811
|
+
setError(err instanceof Error ? err.message : "Failed to open inbox stream");
|
|
9812
|
+
}
|
|
9813
|
+
return () => {
|
|
9814
|
+
cancelled = true;
|
|
9815
|
+
stream?.close();
|
|
9816
|
+
};
|
|
9817
|
+
}, [adapter]);
|
|
9818
|
+
return { unreadCount, hasLoaded, error };
|
|
9819
|
+
}
|
|
9820
|
+
init_utils();
|
|
9821
|
+
var DEFAULT_LIMIT2 = 50;
|
|
8936
9822
|
var DEFAULT_POLL_INTERVAL_MS = 3e4;
|
|
8937
9823
|
function fingerprint(items) {
|
|
8938
9824
|
return items.map(
|
|
@@ -8947,7 +9833,7 @@ function formatKind(value) {
|
|
|
8947
9833
|
if (!value) return "Scheduled";
|
|
8948
9834
|
return value.split(/[_-]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
8949
9835
|
}
|
|
8950
|
-
function
|
|
9836
|
+
function formatRelative2(value, label) {
|
|
8951
9837
|
if (!value) return null;
|
|
8952
9838
|
const date = new Date(value);
|
|
8953
9839
|
if (Number.isNaN(date.getTime())) return null;
|
|
@@ -8997,10 +9883,10 @@ function ScheduledWorkRow({
|
|
|
8997
9883
|
const lastSessionId = item.lastSessionId ?? null;
|
|
8998
9884
|
const meta = [
|
|
8999
9885
|
scheduleDisplay(item),
|
|
9000
|
-
|
|
9001
|
-
|
|
9886
|
+
formatRelative2(item.nextRunAt, "Next"),
|
|
9887
|
+
formatRelative2(item.lastRunAt, "Last"),
|
|
9002
9888
|
formatRuns(item.runCount),
|
|
9003
|
-
item.expiresAt ?
|
|
9889
|
+
item.expiresAt ? formatRelative2(item.expiresAt, "Expires") : null
|
|
9004
9890
|
].filter(Boolean);
|
|
9005
9891
|
const openLastRun = () => {
|
|
9006
9892
|
if (lastSessionId) onOpenLastRun(lastSessionId);
|
|
@@ -9080,7 +9966,7 @@ function ScheduledWorkPanel({
|
|
|
9080
9966
|
adapter,
|
|
9081
9967
|
agentId,
|
|
9082
9968
|
title = "Scheduled Work",
|
|
9083
|
-
limit =
|
|
9969
|
+
limit = DEFAULT_LIMIT2,
|
|
9084
9970
|
status = "active",
|
|
9085
9971
|
hideHeader = false,
|
|
9086
9972
|
pollIntervalMs = DEFAULT_POLL_INTERVAL_MS,
|
|
@@ -9603,10 +10489,12 @@ function SessionTreePanel({
|
|
|
9603
10489
|
|
|
9604
10490
|
exports.AgentChat = AgentChat;
|
|
9605
10491
|
exports.AgentChatAdapterProvider = AgentChatAdapterProvider;
|
|
10492
|
+
exports.InboxPanel = InboxPanel;
|
|
9606
10493
|
exports.MessageResponse = MessageResponse;
|
|
9607
10494
|
exports.ScheduledWorkPanel = ScheduledWorkPanel;
|
|
9608
10495
|
exports.SessionTreePanel = SessionTreePanel;
|
|
9609
10496
|
exports.useAgentChatAdapterContext = useAgentChatAdapterContext;
|
|
9610
10497
|
exports.useAgentChatRuntime = useAgentChatRuntime;
|
|
10498
|
+
exports.useInboxUnreadCount = useInboxUnreadCount;
|
|
9611
10499
|
//# sourceMappingURL=index.cjs.map
|
|
9612
10500
|
//# sourceMappingURL=index.cjs.map
|