@invergent/agent-chat-react 1.6.1 → 1.6.3
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 +986 -160
- 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 +985 -162
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cn } from './chunk-QSC4UIVT.js';
|
|
2
2
|
import { createContext, memo, useRef, useState, useEffect, useCallback, useMemo, lazy, useContext, Suspense, Children } from 'react';
|
|
3
|
-
import { ChevronDownIcon, AlertTriangle, ChevronDown, ChevronRight, RefreshCw, ChevronRightIcon, GitBranchIcon, XIcon, ThumbsUpIcon, ThumbsDownIcon, ActivityIcon, Loader2Icon, GlobeIcon, SearchIcon, ListTodoIcon, XCircleIcon, CheckCircle2Icon, Code, CheckIcon, CopyIcon, TerminalIcon, Trash2Icon, CircleDotIcon, CircleCheckIcon, CircleXIcon, SkullIcon, ClockIcon, CheckCircleIcon, CircleIcon, CalendarClockIcon, UsersIcon, MessageSquareIcon, ZapIcon, Maximize2Icon,
|
|
3
|
+
import { ChevronDownIcon, AlertTriangle, ChevronDown, ChevronRight, RefreshCw, ChevronRightIcon, GitBranchIcon, XIcon, ThumbsUpIcon, ThumbsDownIcon, ActivityIcon, Loader2Icon, EyeIcon, GlobeIcon, SearchIcon, ListTodoIcon, XCircleIcon, CheckCircle2Icon, Code, CheckIcon, CopyIcon, TerminalIcon, Trash2Icon, CircleDotIcon, CircleCheckIcon, CircleXIcon, SkullIcon, ClockIcon, CheckCircleIcon, CircleIcon, InboxIcon, CalendarClockIcon, UsersIcon, MessageSquareIcon, ZapIcon, Maximize2Icon, FolderOpenIcon, UploadIcon, RefreshCwIcon, AlertCircleIcon, ExternalLinkIcon, ClipboardCheckIcon, ShieldAlertIcon, TimerIcon, SquareIcon, ArrowDownIcon, RotateCcwIcon, MousePointer2Icon, DownloadIcon, TrashIcon, ImageIcon, FileTextIcon, FolderIcon, FileIcon, ChevronLeftIcon, MinusIcon, PlusIcon, CornerDownLeftIcon } from 'lucide-react';
|
|
4
4
|
import { cva } from 'class-variance-authority';
|
|
5
5
|
import { Collapsible as Collapsible$1, Slot, Dialog as Dialog$1, ScrollArea as ScrollArea$1, Tooltip as Tooltip$1, Tabs as Tabs$1, Popover as Popover$1, DropdownMenu as DropdownMenu$1, HoverCard as HoverCard$1, Progress as Progress$1 } from 'radix-ui';
|
|
6
6
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
@@ -82,7 +82,9 @@ function Button({
|
|
|
82
82
|
function BrowserControlBar({
|
|
83
83
|
sessionId,
|
|
84
84
|
hasControl,
|
|
85
|
-
adapter
|
|
85
|
+
adapter,
|
|
86
|
+
onControlAcquired,
|
|
87
|
+
onControlReleased
|
|
86
88
|
}) {
|
|
87
89
|
const [pending, setPending] = useState(false);
|
|
88
90
|
const [error, setError] = useState(null);
|
|
@@ -93,8 +95,10 @@ function BrowserControlBar({
|
|
|
93
95
|
try {
|
|
94
96
|
if (hasControl) {
|
|
95
97
|
await adapter.releaseBrowserControl(sessionId);
|
|
98
|
+
onControlReleased?.();
|
|
96
99
|
} else {
|
|
97
100
|
await adapter.acquireBrowserControl(sessionId);
|
|
101
|
+
onControlAcquired?.();
|
|
98
102
|
}
|
|
99
103
|
} catch (nextError) {
|
|
100
104
|
setError(nextError.message);
|
|
@@ -333,19 +337,95 @@ function TooltipContent({
|
|
|
333
337
|
}
|
|
334
338
|
function BrowserPane({ sessionId, state, adapter }) {
|
|
335
339
|
const [fullscreenOpen, setFullscreenOpen] = useState(false);
|
|
336
|
-
const [
|
|
340
|
+
const [openFullscreenOnControl, setOpenFullscreenOnControl] = useState(false);
|
|
341
|
+
const [localControlActive, setLocalControlActive] = useState(false);
|
|
342
|
+
const [previewSnapshot, setPreviewSnapshot] = useState(null);
|
|
343
|
+
const [previewLoading, setPreviewLoading] = useState(false);
|
|
344
|
+
const [previewError, setPreviewError] = useState(null);
|
|
337
345
|
const hasLiveViewAdapter = typeof adapter.browserLiveViewUrl === "function";
|
|
346
|
+
const hasPreviewAdapter = typeof adapter.getBrowserPreviewSnapshot === "function";
|
|
338
347
|
const hasControlAdapter = typeof adapter.acquireBrowserControl === "function" && typeof adapter.releaseBrowserControl === "function";
|
|
339
348
|
const liveViewUrl = useMemo(() => {
|
|
340
349
|
if (!hasLiveViewAdapter) return "";
|
|
341
350
|
return adapter.browserLiveViewUrl(sessionId);
|
|
342
351
|
}, [adapter, hasLiveViewAdapter, sessionId]);
|
|
343
352
|
const hasLiveView = state.status !== "provisioning" && state.status !== "closed";
|
|
344
|
-
const
|
|
353
|
+
const hasUserControl = localControlActive;
|
|
354
|
+
const canUseLiveView = hasLiveView && hasUserControl && Boolean(liveViewUrl);
|
|
355
|
+
const canOpenPreview = hasLiveView && (hasPreviewAdapter || canUseLiveView);
|
|
356
|
+
const canOpenFullscreen = canOpenPreview;
|
|
357
|
+
const refreshPreview = useCallback(
|
|
358
|
+
async (signal) => {
|
|
359
|
+
if (!hasPreviewAdapter || canUseLiveView) return;
|
|
360
|
+
setPreviewLoading(true);
|
|
361
|
+
setPreviewError(null);
|
|
362
|
+
try {
|
|
363
|
+
const snapshot = await adapter.getBrowserPreviewSnapshot?.(sessionId);
|
|
364
|
+
if (signal?.aborted) return;
|
|
365
|
+
setPreviewSnapshot(snapshot ?? null);
|
|
366
|
+
if (!snapshot) {
|
|
367
|
+
setPreviewError("Browser preview is unavailable.");
|
|
368
|
+
}
|
|
369
|
+
} catch (error) {
|
|
370
|
+
if (signal?.aborted) return;
|
|
371
|
+
setPreviewSnapshot(null);
|
|
372
|
+
setPreviewError(
|
|
373
|
+
error instanceof Error ? error.message : "Browser preview failed."
|
|
374
|
+
);
|
|
375
|
+
} finally {
|
|
376
|
+
if (!signal?.aborted) setPreviewLoading(false);
|
|
377
|
+
}
|
|
378
|
+
},
|
|
379
|
+
[adapter, canUseLiveView, hasPreviewAdapter, sessionId]
|
|
380
|
+
);
|
|
345
381
|
useEffect(() => {
|
|
346
382
|
setFullscreenOpen(false);
|
|
347
|
-
|
|
383
|
+
setOpenFullscreenOnControl(false);
|
|
384
|
+
setLocalControlActive(false);
|
|
385
|
+
setPreviewSnapshot(null);
|
|
386
|
+
setPreviewLoading(false);
|
|
387
|
+
setPreviewError(null);
|
|
348
388
|
}, [sessionId]);
|
|
389
|
+
useEffect(() => {
|
|
390
|
+
if (!openFullscreenOnControl) return;
|
|
391
|
+
if (!canUseLiveView) return;
|
|
392
|
+
setFullscreenOpen(true);
|
|
393
|
+
setOpenFullscreenOnControl(false);
|
|
394
|
+
}, [canUseLiveView, openFullscreenOnControl]);
|
|
395
|
+
const handleFullscreenOpenChange = useCallback(
|
|
396
|
+
(open) => {
|
|
397
|
+
setFullscreenOpen(open);
|
|
398
|
+
if (open || !hasUserControl || !hasControlAdapter) return;
|
|
399
|
+
setLocalControlActive(false);
|
|
400
|
+
void adapter.releaseBrowserControl(sessionId).catch((error) => {
|
|
401
|
+
console.error("Failed to release browser control", error);
|
|
402
|
+
});
|
|
403
|
+
},
|
|
404
|
+
[adapter, hasControlAdapter, hasUserControl, sessionId]
|
|
405
|
+
);
|
|
406
|
+
useEffect(() => {
|
|
407
|
+
if (!hasLiveView || canUseLiveView) {
|
|
408
|
+
setPreviewLoading(false);
|
|
409
|
+
setPreviewError(null);
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
if (!hasPreviewAdapter) return;
|
|
413
|
+
const abort = new AbortController();
|
|
414
|
+
void refreshPreview(abort.signal);
|
|
415
|
+
const timer = window.setInterval(() => {
|
|
416
|
+
void refreshPreview(abort.signal);
|
|
417
|
+
}, 5e3);
|
|
418
|
+
return () => {
|
|
419
|
+
abort.abort();
|
|
420
|
+
window.clearInterval(timer);
|
|
421
|
+
};
|
|
422
|
+
}, [
|
|
423
|
+
canUseLiveView,
|
|
424
|
+
fullscreenOpen,
|
|
425
|
+
hasLiveView,
|
|
426
|
+
hasPreviewAdapter,
|
|
427
|
+
refreshPreview
|
|
428
|
+
]);
|
|
349
429
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
350
430
|
/* @__PURE__ */ jsxs(
|
|
351
431
|
"div",
|
|
@@ -375,31 +455,31 @@ function BrowserPane({ sessionId, state, adapter }) {
|
|
|
375
455
|
/* @__PURE__ */ jsx(TooltipContent, { side: "bottom", children: "Full screen" })
|
|
376
456
|
] }) }) })
|
|
377
457
|
] }),
|
|
378
|
-
/* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 bg-black", children: state.status === "provisioning" ? /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Starting browser..." }) : state.status === "closed" ? /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Browser closed." }) :
|
|
379
|
-
|
|
458
|
+
/* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 bg-black", children: state.status === "provisioning" ? /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Starting browser..." }) : state.status === "closed" ? /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Browser closed." }) : canUseLiveView ? /* @__PURE__ */ jsx(BrowserLiveView, { src: liveViewUrl }) : previewSnapshot ? /* @__PURE__ */ jsx(
|
|
459
|
+
BrowserPreviewImage,
|
|
380
460
|
{
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
onClick: () => setInlinePreviewOpen(true),
|
|
385
|
-
children: [
|
|
386
|
-
/* @__PURE__ */ jsx(PlayIcon, { className: "size-3.5", "aria-hidden": "true" }),
|
|
387
|
-
/* @__PURE__ */ jsx("span", { children: "Open live preview" })
|
|
388
|
-
]
|
|
461
|
+
src: previewSnapshot.src,
|
|
462
|
+
fit: "contain",
|
|
463
|
+
testId: "browser-preview-image"
|
|
389
464
|
}
|
|
390
|
-
) }) : /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Browser
|
|
465
|
+
) : previewLoading ? /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Loading browser preview..." }) : previewError ? /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: previewError }) : /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Browser preview is unavailable." }) }),
|
|
391
466
|
hasLiveView && hasControlAdapter && /* @__PURE__ */ jsx(
|
|
392
467
|
BrowserControlBar,
|
|
393
468
|
{
|
|
394
469
|
sessionId,
|
|
395
|
-
hasControl:
|
|
396
|
-
adapter
|
|
470
|
+
hasControl: localControlActive,
|
|
471
|
+
adapter,
|
|
472
|
+
onControlAcquired: () => {
|
|
473
|
+
setLocalControlActive(true);
|
|
474
|
+
setOpenFullscreenOnControl(true);
|
|
475
|
+
},
|
|
476
|
+
onControlReleased: () => setLocalControlActive(false)
|
|
397
477
|
}
|
|
398
478
|
)
|
|
399
479
|
]
|
|
400
480
|
}
|
|
401
481
|
),
|
|
402
|
-
/* @__PURE__ */ jsx(Dialog, { open: fullscreenOpen, onOpenChange:
|
|
482
|
+
/* @__PURE__ */ jsx(Dialog, { open: fullscreenOpen, onOpenChange: handleFullscreenOpenChange, children: /* @__PURE__ */ jsxs(
|
|
403
483
|
DialogContent,
|
|
404
484
|
{
|
|
405
485
|
"aria-describedby": void 0,
|
|
@@ -414,18 +494,41 @@ function BrowserPane({ sessionId, state, adapter }) {
|
|
|
414
494
|
" has control"
|
|
415
495
|
] })
|
|
416
496
|
] }),
|
|
417
|
-
/* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 bg-black", children:
|
|
497
|
+
/* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 bg-black", children: canUseLiveView ? /* @__PURE__ */ jsx(
|
|
418
498
|
BrowserLiveView,
|
|
419
499
|
{
|
|
420
500
|
src: liveViewUrl,
|
|
421
501
|
testId: "browser-fullscreen-iframe"
|
|
422
502
|
}
|
|
423
|
-
) : /* @__PURE__ */ jsx(
|
|
503
|
+
) : previewSnapshot ? /* @__PURE__ */ jsx(
|
|
504
|
+
BrowserPreviewImage,
|
|
505
|
+
{
|
|
506
|
+
src: previewSnapshot.src,
|
|
507
|
+
fit: "contain",
|
|
508
|
+
testId: "browser-fullscreen-preview-image"
|
|
509
|
+
}
|
|
510
|
+
) : previewLoading ? /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Loading browser preview..." }) : previewError ? /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: previewError }) : /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Browser preview is unavailable." }) })
|
|
424
511
|
]
|
|
425
512
|
}
|
|
426
513
|
) })
|
|
427
514
|
] });
|
|
428
515
|
}
|
|
516
|
+
function BrowserPreviewImage({
|
|
517
|
+
src,
|
|
518
|
+
fit,
|
|
519
|
+
testId
|
|
520
|
+
}) {
|
|
521
|
+
return /* @__PURE__ */ jsx(
|
|
522
|
+
"img",
|
|
523
|
+
{
|
|
524
|
+
"data-testid": testId,
|
|
525
|
+
src,
|
|
526
|
+
alt: "Browser preview",
|
|
527
|
+
className: `h-full w-full bg-black ${fit === "cover" ? "object-cover object-top" : "object-contain"}`,
|
|
528
|
+
draggable: false
|
|
529
|
+
}
|
|
530
|
+
);
|
|
531
|
+
}
|
|
429
532
|
var Conversation = ({ className, ...props }) => /* @__PURE__ */ jsx(
|
|
430
533
|
StickToBottom,
|
|
431
534
|
{
|
|
@@ -867,66 +970,82 @@ function TimelineSeparator({
|
|
|
867
970
|
);
|
|
868
971
|
}
|
|
869
972
|
function BrowserActivityGroup({ calls }) {
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
/* @__PURE__ */
|
|
874
|
-
"button",
|
|
875
|
-
{
|
|
876
|
-
type: "button",
|
|
877
|
-
onClick: () => setOpen((current) => !current),
|
|
878
|
-
className: "flex w-full items-center gap-2 px-3 py-2 text-left hover:bg-muted/40",
|
|
879
|
-
children: [
|
|
880
|
-
open ? /* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-3 shrink-0", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ChevronRightIcon, { className: "size-3 shrink-0", "aria-hidden": "true" }),
|
|
881
|
-
/* @__PURE__ */ jsx(ZapIcon, { className: "size-3 shrink-0 text-amber-500", "aria-hidden": "true" }),
|
|
882
|
-
/* @__PURE__ */ jsx("span", { className: "font-semibold text-foreground", children: "browser" }),
|
|
883
|
-
/* @__PURE__ */ jsxs("span", { className: "min-w-0 truncate text-muted-foreground", children: [
|
|
884
|
-
"(",
|
|
885
|
-
calls.length,
|
|
886
|
-
" ",
|
|
887
|
-
calls.length === 1 ? "action" : "actions",
|
|
888
|
-
" - latest:",
|
|
889
|
-
" ",
|
|
890
|
-
latest ? summarise(latest) : "none",
|
|
891
|
-
")"
|
|
892
|
-
] })
|
|
893
|
-
]
|
|
894
|
-
}
|
|
895
|
-
),
|
|
896
|
-
open && /* @__PURE__ */ jsx("ul", { className: "border-t border-line py-1", children: calls.map((call) => {
|
|
973
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border border-line bg-card px-3 py-2 text-xs", children: [
|
|
974
|
+
/* @__PURE__ */ jsx(ZapIcon, { className: "size-3 shrink-0 text-amber-500", "aria-hidden": "true" }),
|
|
975
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 font-semibold text-foreground", children: "Browser:" }),
|
|
976
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 truncate text-muted-foreground", children: calls.map((call, index) => {
|
|
897
977
|
const error = stringValue(parseJson(call.result).error);
|
|
898
|
-
return /* @__PURE__ */ jsxs(
|
|
899
|
-
"
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
error
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
),
|
|
911
|
-
/* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: summarise(call) }),
|
|
912
|
-
error && /* @__PURE__ */ jsx("span", { className: "ml-auto max-w-40 truncate text-destructive", children: error })
|
|
913
|
-
]
|
|
914
|
-
},
|
|
915
|
-
call.id
|
|
916
|
-
);
|
|
978
|
+
return /* @__PURE__ */ jsxs("span", { children: [
|
|
979
|
+
index > 0 && ", ",
|
|
980
|
+
/* @__PURE__ */ jsx(
|
|
981
|
+
"span",
|
|
982
|
+
{
|
|
983
|
+
className: error ? "text-destructive" : void 0,
|
|
984
|
+
"data-testid": error ? `activity-error-${call.id}` : void 0,
|
|
985
|
+
title: error || void 0,
|
|
986
|
+
children: summarize(call)
|
|
987
|
+
}
|
|
988
|
+
)
|
|
989
|
+
] }, call.id);
|
|
917
990
|
}) })
|
|
918
991
|
] });
|
|
919
992
|
}
|
|
920
|
-
function
|
|
921
|
-
const verb = call.toolName.replace(/^browser_/, "");
|
|
993
|
+
function summarize(call) {
|
|
922
994
|
const args = parseJson(call.args);
|
|
923
|
-
const
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
995
|
+
const handler = ACTION_SUMMARIES[call.toolName];
|
|
996
|
+
return handler ? handler(args) : call.toolName.replace(/^browser_/, "");
|
|
997
|
+
}
|
|
998
|
+
var ACTION_SUMMARIES = {
|
|
999
|
+
browser_navigate: (args) => `navigate to ${stringValue(args.url) || "?"}`,
|
|
1000
|
+
browser_get_state: () => "get state",
|
|
1001
|
+
browser_close: () => "close",
|
|
1002
|
+
browser_click: (args) => {
|
|
1003
|
+
const ref = stringValue(args.ref);
|
|
1004
|
+
if (ref) return `click ${ref}`;
|
|
1005
|
+
const x = asNumber(args.x);
|
|
1006
|
+
const y = asNumber(args.y);
|
|
1007
|
+
if (x !== null && y !== null) return `click (${x}, ${y})`;
|
|
1008
|
+
return "click";
|
|
1009
|
+
},
|
|
1010
|
+
browser_type: (args) => {
|
|
1011
|
+
const text = stringValue(args.text);
|
|
1012
|
+
const ref = stringValue(args.ref);
|
|
1013
|
+
const quoted = text ? `"${truncateText(text)}"` : "";
|
|
1014
|
+
if (!quoted) return ref ? `type into ${ref}` : "type";
|
|
1015
|
+
return ref ? `type ${quoted} into ${ref}` : `type ${quoted}`;
|
|
1016
|
+
},
|
|
1017
|
+
browser_press_key: (args) => {
|
|
1018
|
+
const keys = Array.isArray(args.keys) ? args.keys.map(String) : [];
|
|
1019
|
+
return keys.length ? `press ${keys.join("+")}` : "press key";
|
|
1020
|
+
},
|
|
1021
|
+
browser_scroll: (args) => {
|
|
1022
|
+
const x = asNumber(args.x);
|
|
1023
|
+
const y = asNumber(args.y);
|
|
1024
|
+
const dx = asNumber(args.delta_x) ?? 0;
|
|
1025
|
+
const dy = asNumber(args.delta_y) ?? 0;
|
|
1026
|
+
const delta = dx || dy ? `\u0394(${dx}, ${dy})` : "";
|
|
1027
|
+
const origin = x !== null && y !== null ? `at (${x}, ${y})` : "";
|
|
1028
|
+
return ["scroll", delta, origin].filter(Boolean).join(" ");
|
|
1029
|
+
},
|
|
1030
|
+
browser_drag: (args) => {
|
|
1031
|
+
const path = Array.isArray(args.path) ? args.path : [];
|
|
1032
|
+
if (path.length < 2) return "drag";
|
|
1033
|
+
const first = path[0];
|
|
1034
|
+
const last = path[path.length - 1];
|
|
1035
|
+
const point = (p) => `(${asNumber(p[0]) ?? "?"}, ${asNumber(p[1]) ?? "?"})`;
|
|
1036
|
+
return `drag ${point(first)} \u2192 ${point(last)}`;
|
|
1037
|
+
},
|
|
1038
|
+
browser_wait: (args) => {
|
|
1039
|
+
const ms = asNumber(args.ms);
|
|
1040
|
+
return ms === null ? "wait" : `wait ${ms}ms`;
|
|
1041
|
+
},
|
|
1042
|
+
browser_screenshot: (args) => args.annotate === true ? "screenshot (annotated)" : "screenshot"
|
|
1043
|
+
};
|
|
1044
|
+
function truncateText(text, max = 32) {
|
|
1045
|
+
return text.length > max ? `${text.slice(0, max - 1)}\u2026` : text;
|
|
1046
|
+
}
|
|
1047
|
+
function asNumber(value) {
|
|
1048
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
930
1049
|
}
|
|
931
1050
|
function parseJson(raw) {
|
|
932
1051
|
if (!raw) return {};
|
|
@@ -1966,6 +2085,20 @@ function WebToolBlock({ tc }) {
|
|
|
1966
2085
|
displayText && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/70 truncate text-xs", children: displayText })
|
|
1967
2086
|
] });
|
|
1968
2087
|
}
|
|
2088
|
+
function VisionAnalyzeBlock({ tc }) {
|
|
2089
|
+
let image = "";
|
|
2090
|
+
try {
|
|
2091
|
+
const args = JSON.parse(tc.args);
|
|
2092
|
+
const ref = String(args.image ?? args.image_url ?? args.image_path ?? "");
|
|
2093
|
+
image = ref.split("/").pop() ?? ref;
|
|
2094
|
+
} catch {
|
|
2095
|
+
}
|
|
2096
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm py-0.5", children: [
|
|
2097
|
+
/* @__PURE__ */ jsx(EyeIcon, { className: "size-3.5 text-muted-foreground/60 shrink-0" }),
|
|
2098
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: "Vision Analyze" }),
|
|
2099
|
+
image && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/70 truncate text-xs", children: image })
|
|
2100
|
+
] });
|
|
2101
|
+
}
|
|
1969
2102
|
function splitChangeLines(change) {
|
|
1970
2103
|
const v = change.value;
|
|
1971
2104
|
if (v === "") return [];
|
|
@@ -3903,6 +4036,8 @@ function ToolCallBlock({
|
|
|
3903
4036
|
case "web_search":
|
|
3904
4037
|
case "web_crawl":
|
|
3905
4038
|
return /* @__PURE__ */ jsx(WebToolBlock, { tc });
|
|
4039
|
+
case "vision_analyze":
|
|
4040
|
+
return /* @__PURE__ */ jsx(VisionAnalyzeBlock, { tc });
|
|
3906
4041
|
case "read_file":
|
|
3907
4042
|
return /* @__PURE__ */ jsx(ReadFileBlock, { tc, onFileSelect });
|
|
3908
4043
|
case "write_file":
|
|
@@ -5396,18 +5531,21 @@ function ChatComposerInner({
|
|
|
5396
5531
|
[menuOpen, filteredCommands, selectedIndex, handleCommandSelect, textInput]
|
|
5397
5532
|
);
|
|
5398
5533
|
const handleSubmit = useCallback(
|
|
5399
|
-
(message) => {
|
|
5534
|
+
async (message) => {
|
|
5400
5535
|
const text = message.text.trim();
|
|
5401
|
-
if (!text ||
|
|
5536
|
+
if (!text || disabled) return;
|
|
5402
5537
|
const images = [];
|
|
5403
5538
|
for (const file of message.files) {
|
|
5404
5539
|
if (file.mediaType?.startsWith("image/") && file.url) {
|
|
5405
5540
|
images.push({ data: file.url, mimeType: file.mediaType });
|
|
5406
5541
|
}
|
|
5407
5542
|
}
|
|
5408
|
-
|
|
5543
|
+
if (isRunning) {
|
|
5544
|
+
await onStop();
|
|
5545
|
+
}
|
|
5546
|
+
await onSend(text, images.length > 0 ? images : void 0);
|
|
5409
5547
|
},
|
|
5410
|
-
[onSend, isRunning, disabled]
|
|
5548
|
+
[onSend, onStop, isRunning, disabled]
|
|
5411
5549
|
);
|
|
5412
5550
|
return /* @__PURE__ */ jsxs(Popover, { open: menuOpen, onOpenChange: (open) => {
|
|
5413
5551
|
if (!open) {
|
|
@@ -7249,10 +7387,9 @@ function WorkspacePanel({
|
|
|
7249
7387
|
sessionId,
|
|
7250
7388
|
selectedPath,
|
|
7251
7389
|
onSelectedPathChange,
|
|
7252
|
-
collapsed = false,
|
|
7253
|
-
onCollapsedChange,
|
|
7254
7390
|
refreshSignal = 0,
|
|
7255
|
-
disabled = false
|
|
7391
|
+
disabled = false,
|
|
7392
|
+
fillParent = false
|
|
7256
7393
|
}) {
|
|
7257
7394
|
const fileInputRef = useRef(null);
|
|
7258
7395
|
const [entries, setEntries] = useState([]);
|
|
@@ -7453,34 +7590,17 @@ function WorkspacePanel({
|
|
|
7453
7590
|
window.removeEventListener("mouseup", onMouseUp);
|
|
7454
7591
|
};
|
|
7455
7592
|
}, []);
|
|
7456
|
-
if (collapsed) {
|
|
7457
|
-
return /* @__PURE__ */ jsx(
|
|
7458
|
-
"aside",
|
|
7459
|
-
{
|
|
7460
|
-
"data-testid": "workspace-panel",
|
|
7461
|
-
role: "button",
|
|
7462
|
-
tabIndex: 0,
|
|
7463
|
-
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",
|
|
7464
|
-
"aria-label": "Expand workspace",
|
|
7465
|
-
onClick: () => onCollapsedChange?.(false),
|
|
7466
|
-
onKeyDown: (event) => {
|
|
7467
|
-
if (event.key === "Enter" || event.key === " ") {
|
|
7468
|
-
event.preventDefault();
|
|
7469
|
-
onCollapsedChange?.(false);
|
|
7470
|
-
}
|
|
7471
|
-
},
|
|
7472
|
-
children: /* @__PURE__ */ jsx(FolderOpenIcon, { className: "size-4 text-amber-500" })
|
|
7473
|
-
}
|
|
7474
|
-
);
|
|
7475
|
-
}
|
|
7476
7593
|
return /* @__PURE__ */ jsxs(
|
|
7477
7594
|
"aside",
|
|
7478
7595
|
{
|
|
7479
7596
|
"data-testid": "workspace-panel",
|
|
7480
|
-
className:
|
|
7481
|
-
|
|
7597
|
+
className: cn(
|
|
7598
|
+
"relative z-10 flex h-full min-h-0 flex-col overflow-hidden border-l border-muted-foreground/20 bg-card",
|
|
7599
|
+
fillParent && "w-full"
|
|
7600
|
+
),
|
|
7601
|
+
style: fillParent ? void 0 : { width, minWidth: MIN_WIDTH, maxWidth: MAX_WIDTH },
|
|
7482
7602
|
children: [
|
|
7483
|
-
/* @__PURE__ */ jsx(
|
|
7603
|
+
!fillParent && /* @__PURE__ */ jsx(
|
|
7484
7604
|
"div",
|
|
7485
7605
|
{
|
|
7486
7606
|
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",
|
|
@@ -7506,19 +7626,6 @@ function WorkspacePanel({
|
|
|
7506
7626
|
/* @__PURE__ */ jsx("div", { className: "truncate text-sm font-medium text-foreground", children: rootName }),
|
|
7507
7627
|
/* @__PURE__ */ jsx("div", { className: "truncate text-xs text-faint", children: disabled ? "Workspace - Read-only" : "Workspace" })
|
|
7508
7628
|
] }),
|
|
7509
|
-
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
7510
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
7511
|
-
Button,
|
|
7512
|
-
{
|
|
7513
|
-
variant: "ghost",
|
|
7514
|
-
size: "icon-sm",
|
|
7515
|
-
onClick: () => onCollapsedChange?.(true),
|
|
7516
|
-
"aria-label": "Collapse workspace",
|
|
7517
|
-
children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: "size-4" })
|
|
7518
|
-
}
|
|
7519
|
-
) }),
|
|
7520
|
-
/* @__PURE__ */ jsx(TooltipContent, { side: "bottom", children: "Collapse" })
|
|
7521
|
-
] }),
|
|
7522
7629
|
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
7523
7630
|
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
7524
7631
|
Button,
|
|
@@ -7691,7 +7798,8 @@ var WORKSPACE_MUTATING_TOOLS = /* @__PURE__ */ new Set([
|
|
|
7691
7798
|
"terminal",
|
|
7692
7799
|
"write_file",
|
|
7693
7800
|
"patch",
|
|
7694
|
-
"execute_code"
|
|
7801
|
+
"execute_code",
|
|
7802
|
+
"browser_screenshot"
|
|
7695
7803
|
]);
|
|
7696
7804
|
var AGENT_CHAT_LISTENED_EVENTS = [
|
|
7697
7805
|
"user.message",
|
|
@@ -8617,7 +8725,6 @@ function AgentChat({
|
|
|
8617
8725
|
disabled
|
|
8618
8726
|
}) {
|
|
8619
8727
|
const [workspacePath, setWorkspacePath] = useState(null);
|
|
8620
|
-
const [workspaceCollapsed, setWorkspaceCollapsed] = useState(false);
|
|
8621
8728
|
const runtime = useAgentChatRuntime({
|
|
8622
8729
|
adapter,
|
|
8623
8730
|
agentId,
|
|
@@ -8628,6 +8735,7 @@ function AgentChat({
|
|
|
8628
8735
|
const effectiveDisabled = disabled || isReadOnlySession;
|
|
8629
8736
|
const disabledReason = isReadOnlySession ? "Scheduled run is read-only" : void 0;
|
|
8630
8737
|
const browserState = runtime.state.browser;
|
|
8738
|
+
const hasBrowserPanel = browserState !== null && sessionId;
|
|
8631
8739
|
useEffect(() => {
|
|
8632
8740
|
onMessagesChange?.(runtime.messages);
|
|
8633
8741
|
}, [onMessagesChange, runtime.messages]);
|
|
@@ -8646,52 +8754,767 @@ function AgentChat({
|
|
|
8646
8754
|
sessionId,
|
|
8647
8755
|
onFileSelect: handleFileSelect
|
|
8648
8756
|
},
|
|
8649
|
-
children: /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(
|
|
8650
|
-
|
|
8651
|
-
|
|
8757
|
+
children: /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(
|
|
8758
|
+
"section",
|
|
8759
|
+
{
|
|
8760
|
+
"data-testid": "agent-chat-layout",
|
|
8761
|
+
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",
|
|
8762
|
+
style: { direction: "ltr" },
|
|
8763
|
+
children: [
|
|
8764
|
+
/* @__PURE__ */ jsx(
|
|
8765
|
+
"div",
|
|
8766
|
+
{
|
|
8767
|
+
"data-testid": "chat-panel",
|
|
8768
|
+
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",
|
|
8769
|
+
style: hasBrowserPanel ? { right: 440 } : void 0,
|
|
8770
|
+
children: /* @__PURE__ */ jsx(
|
|
8771
|
+
ChatThread,
|
|
8772
|
+
{
|
|
8773
|
+
sessionId,
|
|
8774
|
+
messages: runtime.messages,
|
|
8775
|
+
isRunning: runtime.isRunning,
|
|
8776
|
+
isLoadingHistory: runtime.isLoadingHistory,
|
|
8777
|
+
onSend: (content, images) => runtime.send(content, images),
|
|
8778
|
+
onStop: () => runtime.stop(),
|
|
8779
|
+
onRetry: runtime.retry,
|
|
8780
|
+
onFileSelect: handleFileSelect,
|
|
8781
|
+
disabled: effectiveDisabled,
|
|
8782
|
+
disabledReason,
|
|
8783
|
+
tokenUsage: runtime.tokenUsage,
|
|
8784
|
+
retryIndicator: runtime.retryIndicator
|
|
8785
|
+
}
|
|
8786
|
+
)
|
|
8787
|
+
}
|
|
8788
|
+
),
|
|
8789
|
+
/* @__PURE__ */ jsxs(
|
|
8790
|
+
"div",
|
|
8791
|
+
{
|
|
8792
|
+
"data-testid": "right-stack",
|
|
8793
|
+
className: hasBrowserPanel ? "absolute inset-y-0 flex min-h-0 flex-col overflow-hidden" : "flex min-h-0 shrink-0 flex-col",
|
|
8794
|
+
style: hasBrowserPanel ? { right: 0, width: 440 } : void 0,
|
|
8795
|
+
children: [
|
|
8796
|
+
hasBrowserPanel && /* @__PURE__ */ jsx(
|
|
8797
|
+
"div",
|
|
8798
|
+
{
|
|
8799
|
+
"data-testid": "browser-panel",
|
|
8800
|
+
className: "h-1/2 min-h-0 w-full overflow-hidden border-b border-line",
|
|
8801
|
+
children: /* @__PURE__ */ jsx(
|
|
8802
|
+
BrowserPane,
|
|
8803
|
+
{
|
|
8804
|
+
sessionId,
|
|
8805
|
+
state: browserState,
|
|
8806
|
+
adapter
|
|
8807
|
+
}
|
|
8808
|
+
)
|
|
8809
|
+
}
|
|
8810
|
+
),
|
|
8811
|
+
/* @__PURE__ */ jsx(
|
|
8812
|
+
"div",
|
|
8813
|
+
{
|
|
8814
|
+
"data-testid": "workspace-panel-frame",
|
|
8815
|
+
className: hasBrowserPanel ? "h-1/2 min-h-0 w-full overflow-hidden" : "min-h-0",
|
|
8816
|
+
children: /* @__PURE__ */ jsx(
|
|
8817
|
+
WorkspacePanel,
|
|
8818
|
+
{
|
|
8819
|
+
adapter,
|
|
8820
|
+
sessionId,
|
|
8821
|
+
selectedPath: workspacePath,
|
|
8822
|
+
onSelectedPathChange: setWorkspacePath,
|
|
8823
|
+
refreshSignal: runtime.workspaceRefreshKey,
|
|
8824
|
+
disabled: effectiveDisabled,
|
|
8825
|
+
fillParent: Boolean(hasBrowserPanel)
|
|
8826
|
+
}
|
|
8827
|
+
)
|
|
8828
|
+
}
|
|
8829
|
+
)
|
|
8830
|
+
]
|
|
8831
|
+
}
|
|
8832
|
+
)
|
|
8833
|
+
]
|
|
8834
|
+
}
|
|
8835
|
+
) })
|
|
8836
|
+
}
|
|
8837
|
+
);
|
|
8838
|
+
}
|
|
8839
|
+
var DEFAULT_LIMIT = 50;
|
|
8840
|
+
function requireInboxAdapter(adapter) {
|
|
8841
|
+
if (!adapter.listInbox || !adapter.getInboxItem || !adapter.markInboxItemRead || !adapter.acknowledgeInboxItem || !adapter.respondGovernanceInboxItem || !adapter.openInboxStream) {
|
|
8842
|
+
throw new Error("Inbox is not supported by this adapter.");
|
|
8843
|
+
}
|
|
8844
|
+
return adapter;
|
|
8845
|
+
}
|
|
8846
|
+
var KIND_LABEL2 = {
|
|
8847
|
+
input_required: "Input needed",
|
|
8848
|
+
action_required: "Action needed",
|
|
8849
|
+
task_complete: "Task complete",
|
|
8850
|
+
governance_gate: "Approval",
|
|
8851
|
+
progress_checkin: "Progress"
|
|
8852
|
+
};
|
|
8853
|
+
function kindLabel(kind) {
|
|
8854
|
+
return KIND_LABEL2[kind] ?? kind.replace(/[_-]+/g, " ");
|
|
8855
|
+
}
|
|
8856
|
+
function statusLabel(status) {
|
|
8857
|
+
return status.split(/[_-]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
8858
|
+
}
|
|
8859
|
+
function formatRelative(value) {
|
|
8860
|
+
const date = new Date(value);
|
|
8861
|
+
if (Number.isNaN(date.getTime())) return "";
|
|
8862
|
+
return formatDistanceToNow(date, { addSuffix: true });
|
|
8863
|
+
}
|
|
8864
|
+
function kindIcon(kind) {
|
|
8865
|
+
if (kind === "input_required") return MessageSquareIcon;
|
|
8866
|
+
if (kind === "action_required") return ExternalLinkIcon;
|
|
8867
|
+
if (kind === "task_complete") return ClipboardCheckIcon;
|
|
8868
|
+
if (kind === "governance_gate") return ShieldAlertIcon;
|
|
8869
|
+
if (kind === "progress_checkin") return TimerIcon;
|
|
8870
|
+
return CircleDotIcon;
|
|
8871
|
+
}
|
|
8872
|
+
function sortItems(items) {
|
|
8873
|
+
return [...items].sort((a, b) => {
|
|
8874
|
+
if (a.createdAt !== b.createdAt) return a.createdAt > b.createdAt ? -1 : 1;
|
|
8875
|
+
return b.id - a.id;
|
|
8876
|
+
});
|
|
8877
|
+
}
|
|
8878
|
+
function QuestionInput({
|
|
8879
|
+
prompt,
|
|
8880
|
+
choices,
|
|
8881
|
+
disabled,
|
|
8882
|
+
value,
|
|
8883
|
+
onChange
|
|
8884
|
+
}) {
|
|
8885
|
+
if (choices && choices.length > 0) {
|
|
8886
|
+
return /* @__PURE__ */ jsxs(
|
|
8887
|
+
"select",
|
|
8888
|
+
{
|
|
8889
|
+
"aria-label": prompt,
|
|
8890
|
+
className: "h-9 w-full border border-line bg-background px-2 text-sm text-foreground outline-none focus:border-primary disabled:opacity-50",
|
|
8891
|
+
disabled,
|
|
8892
|
+
value,
|
|
8893
|
+
onChange: (event) => onChange(event.target.value),
|
|
8894
|
+
children: [
|
|
8895
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Select" }),
|
|
8896
|
+
choices.map((choice) => /* @__PURE__ */ jsx("option", { value: choice.label, children: choice.label }, choice.label))
|
|
8897
|
+
]
|
|
8898
|
+
}
|
|
8899
|
+
);
|
|
8900
|
+
}
|
|
8901
|
+
return /* @__PURE__ */ jsx(
|
|
8902
|
+
"input",
|
|
8903
|
+
{
|
|
8904
|
+
"aria-label": prompt,
|
|
8905
|
+
className: "h-9 w-full border border-line bg-background px-2 text-sm text-foreground outline-none focus:border-primary disabled:opacity-50",
|
|
8906
|
+
disabled,
|
|
8907
|
+
value,
|
|
8908
|
+
onChange: (event) => onChange(event.target.value)
|
|
8909
|
+
}
|
|
8910
|
+
);
|
|
8911
|
+
}
|
|
8912
|
+
function InboxDetailActions({
|
|
8913
|
+
item,
|
|
8914
|
+
adapter,
|
|
8915
|
+
onDeleted,
|
|
8916
|
+
onSessionSelect,
|
|
8917
|
+
children
|
|
8918
|
+
}) {
|
|
8919
|
+
const [deleting, setDeleting] = useState(false);
|
|
8920
|
+
async function deleteItem() {
|
|
8921
|
+
if (!adapter.deleteInboxItem) return;
|
|
8922
|
+
setDeleting(true);
|
|
8923
|
+
try {
|
|
8924
|
+
await onDeleted(item.id);
|
|
8925
|
+
} finally {
|
|
8926
|
+
setDeleting(false);
|
|
8927
|
+
}
|
|
8928
|
+
}
|
|
8929
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
|
|
8930
|
+
/* @__PURE__ */ jsxs(
|
|
8931
|
+
Button,
|
|
8932
|
+
{
|
|
8933
|
+
type: "button",
|
|
8934
|
+
size: "sm",
|
|
8935
|
+
onClick: () => onSessionSelect?.(item.sessionId),
|
|
8936
|
+
"aria-label": "Open session",
|
|
8937
|
+
children: [
|
|
8938
|
+
/* @__PURE__ */ jsx(ExternalLinkIcon, { className: "size-3.5" }),
|
|
8939
|
+
"Open session"
|
|
8940
|
+
]
|
|
8941
|
+
}
|
|
8942
|
+
),
|
|
8943
|
+
children,
|
|
8944
|
+
adapter.deleteInboxItem && /* @__PURE__ */ jsxs(
|
|
8945
|
+
Button,
|
|
8946
|
+
{
|
|
8947
|
+
type: "button",
|
|
8948
|
+
size: "sm",
|
|
8949
|
+
variant: "ghost",
|
|
8950
|
+
onClick: () => void deleteItem(),
|
|
8951
|
+
disabled: deleting,
|
|
8952
|
+
"aria-label": "Delete inbox item",
|
|
8953
|
+
title: "Delete inbox item",
|
|
8954
|
+
children: [
|
|
8955
|
+
/* @__PURE__ */ jsx(Trash2Icon, { className: "size-3.5" }),
|
|
8956
|
+
deleting ? "Deleting" : "Delete"
|
|
8957
|
+
]
|
|
8958
|
+
}
|
|
8959
|
+
)
|
|
8960
|
+
] });
|
|
8961
|
+
}
|
|
8962
|
+
function InputRequiredDetail({
|
|
8963
|
+
item,
|
|
8964
|
+
adapter,
|
|
8965
|
+
onUpdated,
|
|
8966
|
+
onDeleted,
|
|
8967
|
+
onSessionSelect
|
|
8968
|
+
}) {
|
|
8969
|
+
const questions = Array.isArray(item.payload.questions) ? item.payload.questions.map((question) => ({
|
|
8970
|
+
prompt: typeof question.prompt === "string" ? question.prompt : "",
|
|
8971
|
+
choices: Array.isArray(question.choices) ? question.choices.map((choice) => ({
|
|
8972
|
+
label: typeof choice.label === "string" ? choice.label : "",
|
|
8973
|
+
description: typeof choice.description === "string" ? choice.description : void 0
|
|
8974
|
+
})).filter((choice) => choice.label) : void 0
|
|
8975
|
+
})).filter((question) => question.prompt) : [];
|
|
8976
|
+
const [answers, setAnswers] = useState({});
|
|
8977
|
+
const [submitting, setSubmitting] = useState(false);
|
|
8978
|
+
const disabled = item.status !== "pending" || submitting;
|
|
8979
|
+
const canSubmit = questions.every((question) => answers[question.prompt]?.trim());
|
|
8980
|
+
async function submit() {
|
|
8981
|
+
const toolCallId = typeof item.payload.tool_call_id === "string" ? item.payload.tool_call_id : "";
|
|
8982
|
+
if (!toolCallId || !canSubmit) return;
|
|
8983
|
+
setSubmitting(true);
|
|
8984
|
+
try {
|
|
8985
|
+
const responses = questions.map((question) => ({
|
|
8986
|
+
question: question.prompt,
|
|
8987
|
+
answer: answers[question.prompt]?.trim() ?? "",
|
|
8988
|
+
is_other: false
|
|
8989
|
+
}));
|
|
8990
|
+
await adapter.submitClarifyResponse({
|
|
8991
|
+
sessionId: item.sessionId,
|
|
8992
|
+
toolCallId,
|
|
8993
|
+
responses
|
|
8994
|
+
});
|
|
8995
|
+
onUpdated(await adapter.getInboxItem({ itemId: item.id }));
|
|
8996
|
+
} finally {
|
|
8997
|
+
setSubmitting(false);
|
|
8998
|
+
}
|
|
8999
|
+
}
|
|
9000
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
9001
|
+
item.body && /* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap text-sm text-muted-foreground", children: item.body }),
|
|
9002
|
+
questions.map((question) => /* @__PURE__ */ jsxs("label", { className: "block space-y-1.5", children: [
|
|
9003
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: question.prompt }),
|
|
9004
|
+
/* @__PURE__ */ jsx(
|
|
9005
|
+
QuestionInput,
|
|
9006
|
+
{
|
|
9007
|
+
prompt: question.prompt,
|
|
9008
|
+
choices: question.choices,
|
|
9009
|
+
disabled,
|
|
9010
|
+
value: answers[question.prompt] ?? "",
|
|
9011
|
+
onChange: (value) => setAnswers((current) => ({
|
|
9012
|
+
...current,
|
|
9013
|
+
[question.prompt]: value
|
|
9014
|
+
}))
|
|
9015
|
+
}
|
|
9016
|
+
)
|
|
9017
|
+
] }, question.prompt)),
|
|
9018
|
+
/* @__PURE__ */ jsx(
|
|
9019
|
+
InboxDetailActions,
|
|
9020
|
+
{
|
|
9021
|
+
item,
|
|
9022
|
+
adapter,
|
|
9023
|
+
onDeleted,
|
|
9024
|
+
onSessionSelect,
|
|
9025
|
+
children: /* @__PURE__ */ jsx(
|
|
9026
|
+
Button,
|
|
8652
9027
|
{
|
|
8653
|
-
|
|
8654
|
-
|
|
8655
|
-
|
|
8656
|
-
|
|
8657
|
-
|
|
8658
|
-
|
|
8659
|
-
|
|
8660
|
-
|
|
8661
|
-
|
|
8662
|
-
|
|
8663
|
-
|
|
8664
|
-
|
|
9028
|
+
type: "button",
|
|
9029
|
+
size: "sm",
|
|
9030
|
+
onClick: () => void submit(),
|
|
9031
|
+
disabled: disabled || !canSubmit,
|
|
9032
|
+
"aria-label": "Submit inbox response",
|
|
9033
|
+
children: submitting ? "Submitting" : "Submit"
|
|
9034
|
+
}
|
|
9035
|
+
)
|
|
9036
|
+
}
|
|
9037
|
+
)
|
|
9038
|
+
] });
|
|
9039
|
+
}
|
|
9040
|
+
function AckDetail({
|
|
9041
|
+
item,
|
|
9042
|
+
adapter,
|
|
9043
|
+
onUpdated,
|
|
9044
|
+
onDeleted,
|
|
9045
|
+
onSessionSelect
|
|
9046
|
+
}) {
|
|
9047
|
+
async function acknowledge() {
|
|
9048
|
+
onUpdated(await adapter.acknowledgeInboxItem({ itemId: item.id }));
|
|
9049
|
+
}
|
|
9050
|
+
const outcome = typeof item.payload.outcome === "string" ? item.payload.outcome : "";
|
|
9051
|
+
const duration = typeof item.payload.duration_seconds === "number" ? item.payload.duration_seconds : null;
|
|
9052
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
9053
|
+
outcome && /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: outcome }),
|
|
9054
|
+
item.body && /* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap text-sm", children: item.body }),
|
|
9055
|
+
typeof item.payload.error === "string" && /* @__PURE__ */ jsx("pre", { className: "overflow-x-auto bg-destructive/10 p-3 text-xs text-destructive", children: item.payload.error }),
|
|
9056
|
+
duration !== null && /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
9057
|
+
"Duration: ",
|
|
9058
|
+
Math.round(duration / 60),
|
|
9059
|
+
" min (",
|
|
9060
|
+
duration,
|
|
9061
|
+
" s)"
|
|
9062
|
+
] }),
|
|
9063
|
+
/* @__PURE__ */ jsx(
|
|
9064
|
+
InboxDetailActions,
|
|
9065
|
+
{
|
|
9066
|
+
item,
|
|
9067
|
+
adapter,
|
|
9068
|
+
onDeleted,
|
|
9069
|
+
onSessionSelect,
|
|
9070
|
+
children: item.status === "pending" && /* @__PURE__ */ jsxs(
|
|
9071
|
+
Button,
|
|
9072
|
+
{
|
|
9073
|
+
type: "button",
|
|
9074
|
+
size: "sm",
|
|
9075
|
+
onClick: () => void acknowledge(),
|
|
9076
|
+
"aria-label": "Acknowledge inbox item",
|
|
9077
|
+
children: [
|
|
9078
|
+
/* @__PURE__ */ jsx(CheckIcon, { className: "size-3.5" }),
|
|
9079
|
+
"Acknowledge"
|
|
9080
|
+
]
|
|
8665
9081
|
}
|
|
8666
|
-
)
|
|
8667
|
-
|
|
8668
|
-
|
|
8669
|
-
|
|
9082
|
+
)
|
|
9083
|
+
}
|
|
9084
|
+
)
|
|
9085
|
+
] });
|
|
9086
|
+
}
|
|
9087
|
+
function GovernanceDetail({
|
|
9088
|
+
item,
|
|
9089
|
+
adapter,
|
|
9090
|
+
onUpdated,
|
|
9091
|
+
onDeleted,
|
|
9092
|
+
onSessionSelect
|
|
9093
|
+
}) {
|
|
9094
|
+
async function decide(decision) {
|
|
9095
|
+
onUpdated(
|
|
9096
|
+
await adapter.respondGovernanceInboxItem({ itemId: item.id, decision })
|
|
9097
|
+
);
|
|
9098
|
+
}
|
|
9099
|
+
const toolName = typeof item.payload.tool_name === "string" ? item.payload.tool_name : "tool";
|
|
9100
|
+
const args = typeof item.payload.arguments_excerpt === "string" ? item.payload.arguments_excerpt : "";
|
|
9101
|
+
const reason = typeof item.payload.deny_reason === "string" ? item.payload.deny_reason : "";
|
|
9102
|
+
const disabled = item.status !== "pending";
|
|
9103
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
9104
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
9105
|
+
/* @__PURE__ */ jsxs("div", { className: "text-base font-semibold text-foreground", children: [
|
|
9106
|
+
"Approve ",
|
|
9107
|
+
toolName,
|
|
9108
|
+
"?"
|
|
9109
|
+
] }),
|
|
9110
|
+
reason && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: reason })
|
|
9111
|
+
] }),
|
|
9112
|
+
args && /* @__PURE__ */ jsx("pre", { className: "overflow-x-auto border border-line bg-muted p-3 text-xs", children: args }),
|
|
9113
|
+
/* @__PURE__ */ jsxs(
|
|
9114
|
+
InboxDetailActions,
|
|
9115
|
+
{
|
|
9116
|
+
item,
|
|
9117
|
+
adapter,
|
|
9118
|
+
onDeleted,
|
|
9119
|
+
onSessionSelect,
|
|
9120
|
+
children: [
|
|
9121
|
+
/* @__PURE__ */ jsx(
|
|
9122
|
+
Button,
|
|
8670
9123
|
{
|
|
8671
|
-
|
|
8672
|
-
|
|
8673
|
-
|
|
9124
|
+
type: "button",
|
|
9125
|
+
size: "sm",
|
|
9126
|
+
disabled,
|
|
9127
|
+
onClick: () => void decide("approve"),
|
|
9128
|
+
children: "Approve"
|
|
8674
9129
|
}
|
|
8675
|
-
)
|
|
9130
|
+
),
|
|
8676
9131
|
/* @__PURE__ */ jsx(
|
|
8677
|
-
|
|
9132
|
+
Button,
|
|
8678
9133
|
{
|
|
8679
|
-
|
|
8680
|
-
|
|
8681
|
-
|
|
8682
|
-
|
|
8683
|
-
|
|
8684
|
-
|
|
8685
|
-
refreshSignal: runtime.workspaceRefreshKey,
|
|
8686
|
-
disabled: effectiveDisabled
|
|
9134
|
+
type: "button",
|
|
9135
|
+
size: "sm",
|
|
9136
|
+
variant: "destructive",
|
|
9137
|
+
disabled,
|
|
9138
|
+
onClick: () => void decide("reject"),
|
|
9139
|
+
children: "Reject"
|
|
8687
9140
|
}
|
|
8688
9141
|
)
|
|
8689
|
-
]
|
|
8690
|
-
|
|
9142
|
+
]
|
|
9143
|
+
}
|
|
9144
|
+
)
|
|
9145
|
+
] });
|
|
9146
|
+
}
|
|
9147
|
+
function ActionRequiredDetail({
|
|
9148
|
+
item,
|
|
9149
|
+
adapter,
|
|
9150
|
+
onUpdated,
|
|
9151
|
+
onDeleted,
|
|
9152
|
+
onSessionSelect
|
|
9153
|
+
}) {
|
|
9154
|
+
const [submitting, setSubmitting] = useState(false);
|
|
9155
|
+
const actionType = typeof item.payload.action_type === "string" ? item.payload.action_type : "";
|
|
9156
|
+
const context = typeof item.payload.context === "string" ? item.payload.context : "";
|
|
9157
|
+
const disabled = item.status !== "pending" || submitting || !adapter.respondActionRequiredInboxItem;
|
|
9158
|
+
async function complete() {
|
|
9159
|
+
if (!adapter.respondActionRequiredInboxItem) return;
|
|
9160
|
+
setSubmitting(true);
|
|
9161
|
+
try {
|
|
9162
|
+
onUpdated(await adapter.respondActionRequiredInboxItem({ itemId: item.id }));
|
|
9163
|
+
} finally {
|
|
9164
|
+
setSubmitting(false);
|
|
8691
9165
|
}
|
|
9166
|
+
}
|
|
9167
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
9168
|
+
item.body && /* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap text-sm", children: item.body }),
|
|
9169
|
+
context && context !== item.body && /* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap text-sm text-muted-foreground", children: context }),
|
|
9170
|
+
actionType && /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: actionType }),
|
|
9171
|
+
/* @__PURE__ */ jsx(
|
|
9172
|
+
InboxDetailActions,
|
|
9173
|
+
{
|
|
9174
|
+
item,
|
|
9175
|
+
adapter,
|
|
9176
|
+
onDeleted,
|
|
9177
|
+
onSessionSelect,
|
|
9178
|
+
children: /* @__PURE__ */ jsxs(
|
|
9179
|
+
Button,
|
|
9180
|
+
{
|
|
9181
|
+
type: "button",
|
|
9182
|
+
size: "sm",
|
|
9183
|
+
onClick: () => void complete(),
|
|
9184
|
+
disabled,
|
|
9185
|
+
"aria-label": "Mark action complete",
|
|
9186
|
+
children: [
|
|
9187
|
+
/* @__PURE__ */ jsx(CheckIcon, { className: "size-3.5" }),
|
|
9188
|
+
submitting ? "Marking" : "I completed this"
|
|
9189
|
+
]
|
|
9190
|
+
}
|
|
9191
|
+
)
|
|
9192
|
+
}
|
|
9193
|
+
)
|
|
9194
|
+
] });
|
|
9195
|
+
}
|
|
9196
|
+
function ProgressDetail({
|
|
9197
|
+
item,
|
|
9198
|
+
adapter,
|
|
9199
|
+
onUpdated,
|
|
9200
|
+
onDeleted,
|
|
9201
|
+
onSessionSelect
|
|
9202
|
+
}) {
|
|
9203
|
+
async function acknowledge() {
|
|
9204
|
+
onUpdated(await adapter.acknowledgeInboxItem({ itemId: item.id }));
|
|
9205
|
+
}
|
|
9206
|
+
const rows = [];
|
|
9207
|
+
for (const row of [
|
|
9208
|
+
["Iterations", item.payload.iterations],
|
|
9209
|
+
["Last tool", item.payload.last_tool],
|
|
9210
|
+
["Elapsed", item.payload.elapsed_seconds]
|
|
9211
|
+
]) {
|
|
9212
|
+
if (row[1] !== void 0 && row[1] !== null && row[1] !== "") {
|
|
9213
|
+
rows.push(row);
|
|
9214
|
+
}
|
|
9215
|
+
}
|
|
9216
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
9217
|
+
item.body && /* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap text-sm", children: item.body }),
|
|
9218
|
+
rows.length > 0 && /* @__PURE__ */ jsx("dl", { className: "space-y-1 text-xs text-muted-foreground", children: rows.map(([label, value]) => /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
9219
|
+
/* @__PURE__ */ jsx("dt", { className: "w-20 shrink-0", children: String(label) }),
|
|
9220
|
+
/* @__PURE__ */ jsx("dd", { className: "min-w-0", children: String(value) })
|
|
9221
|
+
] }, String(label))) }),
|
|
9222
|
+
/* @__PURE__ */ jsx(
|
|
9223
|
+
InboxDetailActions,
|
|
9224
|
+
{
|
|
9225
|
+
item,
|
|
9226
|
+
adapter,
|
|
9227
|
+
onDeleted,
|
|
9228
|
+
onSessionSelect,
|
|
9229
|
+
children: item.status === "pending" && /* @__PURE__ */ jsxs(
|
|
9230
|
+
Button,
|
|
9231
|
+
{
|
|
9232
|
+
type: "button",
|
|
9233
|
+
size: "sm",
|
|
9234
|
+
onClick: () => void acknowledge(),
|
|
9235
|
+
"aria-label": "Acknowledge inbox item",
|
|
9236
|
+
children: [
|
|
9237
|
+
/* @__PURE__ */ jsx(CheckIcon, { className: "size-3.5" }),
|
|
9238
|
+
"Acknowledge"
|
|
9239
|
+
]
|
|
9240
|
+
}
|
|
9241
|
+
)
|
|
9242
|
+
}
|
|
9243
|
+
)
|
|
9244
|
+
] });
|
|
9245
|
+
}
|
|
9246
|
+
function InboxDetail({
|
|
9247
|
+
item,
|
|
9248
|
+
adapter,
|
|
9249
|
+
onUpdated,
|
|
9250
|
+
onDeleted,
|
|
9251
|
+
onSessionSelect
|
|
9252
|
+
}) {
|
|
9253
|
+
const Icon = kindIcon(item.kind);
|
|
9254
|
+
return /* @__PURE__ */ jsxs("section", { className: "min-w-0 flex-1 overflow-y-auto p-6", children: [
|
|
9255
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-5 flex min-w-0 items-start gap-3", children: [
|
|
9256
|
+
/* @__PURE__ */ jsx("div", { className: "mt-0.5 flex size-8 shrink-0 items-center justify-center border border-line text-muted-foreground", children: /* @__PURE__ */ jsx(Icon, { className: "size-4" }) }),
|
|
9257
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
9258
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
9259
|
+
/* @__PURE__ */ jsx(Badge, { variant: "secondary", children: kindLabel(item.kind) }),
|
|
9260
|
+
/* @__PURE__ */ jsx(Badge, { variant: item.status === "pending" ? "default" : "secondary", children: statusLabel(item.status) })
|
|
9261
|
+
] }),
|
|
9262
|
+
/* @__PURE__ */ jsx("h2", { className: "mt-2 text-lg font-semibold leading-snug text-foreground", children: item.title })
|
|
9263
|
+
] })
|
|
9264
|
+
] }),
|
|
9265
|
+
item.kind === "input_required" ? /* @__PURE__ */ jsx(
|
|
9266
|
+
InputRequiredDetail,
|
|
9267
|
+
{
|
|
9268
|
+
item,
|
|
9269
|
+
adapter,
|
|
9270
|
+
onUpdated,
|
|
9271
|
+
onDeleted,
|
|
9272
|
+
onSessionSelect
|
|
9273
|
+
}
|
|
9274
|
+
) : item.kind === "action_required" ? /* @__PURE__ */ jsx(
|
|
9275
|
+
ActionRequiredDetail,
|
|
9276
|
+
{
|
|
9277
|
+
item,
|
|
9278
|
+
adapter,
|
|
9279
|
+
onUpdated,
|
|
9280
|
+
onDeleted,
|
|
9281
|
+
onSessionSelect
|
|
9282
|
+
}
|
|
9283
|
+
) : item.kind === "governance_gate" ? /* @__PURE__ */ jsx(
|
|
9284
|
+
GovernanceDetail,
|
|
9285
|
+
{
|
|
9286
|
+
item,
|
|
9287
|
+
adapter,
|
|
9288
|
+
onUpdated,
|
|
9289
|
+
onDeleted,
|
|
9290
|
+
onSessionSelect
|
|
9291
|
+
}
|
|
9292
|
+
) : item.kind === "progress_checkin" ? /* @__PURE__ */ jsx(
|
|
9293
|
+
ProgressDetail,
|
|
9294
|
+
{
|
|
9295
|
+
item,
|
|
9296
|
+
adapter,
|
|
9297
|
+
onUpdated,
|
|
9298
|
+
onDeleted,
|
|
9299
|
+
onSessionSelect
|
|
9300
|
+
}
|
|
9301
|
+
) : /* @__PURE__ */ jsx(
|
|
9302
|
+
AckDetail,
|
|
9303
|
+
{
|
|
9304
|
+
item,
|
|
9305
|
+
adapter,
|
|
9306
|
+
onUpdated,
|
|
9307
|
+
onDeleted,
|
|
9308
|
+
onSessionSelect
|
|
9309
|
+
}
|
|
9310
|
+
)
|
|
9311
|
+
] });
|
|
9312
|
+
}
|
|
9313
|
+
function InboxPanel({
|
|
9314
|
+
adapter,
|
|
9315
|
+
title = "Inbox",
|
|
9316
|
+
selectedId,
|
|
9317
|
+
onSelectedIdChange,
|
|
9318
|
+
onSessionSelect,
|
|
9319
|
+
hideHeader = false,
|
|
9320
|
+
limit = DEFAULT_LIMIT
|
|
9321
|
+
}) {
|
|
9322
|
+
const inboxAdapter = useMemo(() => requireInboxAdapter(adapter), [adapter]);
|
|
9323
|
+
const [items, setItems] = useState([]);
|
|
9324
|
+
const [nextCursor, setNextCursor] = useState(null);
|
|
9325
|
+
const [internalSelectedId, setInternalSelectedId] = useState(null);
|
|
9326
|
+
const [loading, setLoading] = useState(false);
|
|
9327
|
+
const [error, setError] = useState(null);
|
|
9328
|
+
const requestId = useRef(0);
|
|
9329
|
+
const selectedItemId = selectedId ?? internalSelectedId;
|
|
9330
|
+
const selectedItem = useMemo(
|
|
9331
|
+
() => items.find((item) => item.id === selectedItemId) ?? null,
|
|
9332
|
+
[items, selectedItemId]
|
|
9333
|
+
);
|
|
9334
|
+
const applyItem = useCallback((nextItem) => {
|
|
9335
|
+
setItems(
|
|
9336
|
+
(current) => sortItems(
|
|
9337
|
+
current.map((item) => item.id === nextItem.id ? nextItem : item)
|
|
9338
|
+
)
|
|
9339
|
+
);
|
|
9340
|
+
}, []);
|
|
9341
|
+
const selectItem = useCallback(
|
|
9342
|
+
async (itemId) => {
|
|
9343
|
+
if (selectedId === void 0) setInternalSelectedId(itemId);
|
|
9344
|
+
onSelectedIdChange?.(itemId);
|
|
9345
|
+
const item = await inboxAdapter.getInboxItem({ itemId });
|
|
9346
|
+
applyItem(item);
|
|
9347
|
+
if (!item.readAt) {
|
|
9348
|
+
applyItem(await inboxAdapter.markInboxItemRead({ itemId }));
|
|
9349
|
+
}
|
|
9350
|
+
},
|
|
9351
|
+
[applyItem, inboxAdapter, onSelectedIdChange, selectedId]
|
|
9352
|
+
);
|
|
9353
|
+
const load = useCallback(
|
|
9354
|
+
async (cursor) => {
|
|
9355
|
+
const id = ++requestId.current;
|
|
9356
|
+
setLoading(true);
|
|
9357
|
+
setError(null);
|
|
9358
|
+
try {
|
|
9359
|
+
const response = await inboxAdapter.listInbox({
|
|
9360
|
+
cursor: cursor ?? void 0,
|
|
9361
|
+
limit
|
|
9362
|
+
});
|
|
9363
|
+
if (id !== requestId.current) return;
|
|
9364
|
+
setItems(
|
|
9365
|
+
(current) => sortItems(cursor ? [...current, ...response.items] : response.items)
|
|
9366
|
+
);
|
|
9367
|
+
setNextCursor(response.nextCursor);
|
|
9368
|
+
} catch (err) {
|
|
9369
|
+
if (id === requestId.current) {
|
|
9370
|
+
setError(err instanceof Error ? err.message : "Failed to load inbox");
|
|
9371
|
+
}
|
|
9372
|
+
} finally {
|
|
9373
|
+
if (id === requestId.current) setLoading(false);
|
|
9374
|
+
}
|
|
9375
|
+
},
|
|
9376
|
+
[inboxAdapter, limit]
|
|
8692
9377
|
);
|
|
9378
|
+
useEffect(() => {
|
|
9379
|
+
void load(null);
|
|
9380
|
+
}, [load]);
|
|
9381
|
+
useEffect(() => {
|
|
9382
|
+
const stream = inboxAdapter.openInboxStream();
|
|
9383
|
+
stream.addEventListener("item", (event) => {
|
|
9384
|
+
const payload = JSON.parse(event.data);
|
|
9385
|
+
if (typeof payload.item_id !== "number") return;
|
|
9386
|
+
void inboxAdapter.getInboxItem({ itemId: payload.item_id }).then(applyItem);
|
|
9387
|
+
});
|
|
9388
|
+
return () => stream.close();
|
|
9389
|
+
}, [applyItem, inboxAdapter]);
|
|
9390
|
+
function updateSelectedItem(item) {
|
|
9391
|
+
applyItem(item);
|
|
9392
|
+
}
|
|
9393
|
+
const deleteItem = useCallback(
|
|
9394
|
+
async (itemId) => {
|
|
9395
|
+
if (!inboxAdapter.deleteInboxItem) return;
|
|
9396
|
+
await inboxAdapter.deleteInboxItem({ itemId });
|
|
9397
|
+
setItems((current) => current.filter((item) => item.id !== itemId));
|
|
9398
|
+
if (selectedItemId === itemId) {
|
|
9399
|
+
if (selectedId === void 0) setInternalSelectedId(null);
|
|
9400
|
+
onSelectedIdChange?.(null);
|
|
9401
|
+
}
|
|
9402
|
+
},
|
|
9403
|
+
[inboxAdapter, onSelectedIdChange, selectedId, selectedItemId]
|
|
9404
|
+
);
|
|
9405
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex h-full min-h-0 min-w-0 bg-background text-foreground", children: [
|
|
9406
|
+
/* @__PURE__ */ jsxs("aside", { className: "flex w-80 min-w-72 max-w-sm shrink-0 flex-col border-r border-line", children: [
|
|
9407
|
+
!hideHeader && /* @__PURE__ */ jsxs("div", { className: "flex min-h-14 items-center gap-2 border-b border-line px-4", children: [
|
|
9408
|
+
/* @__PURE__ */ jsx(InboxIcon, { className: "size-4 text-muted-foreground" }),
|
|
9409
|
+
/* @__PURE__ */ jsx("h1", { className: "font-semibold", children: title })
|
|
9410
|
+
] }),
|
|
9411
|
+
/* @__PURE__ */ jsxs("div", { className: "min-h-0 flex-1 overflow-y-auto", children: [
|
|
9412
|
+
error && /* @__PURE__ */ jsx("div", { className: "px-4 py-3 text-sm text-destructive", children: error }),
|
|
9413
|
+
!error && items.length === 0 && !loading && /* @__PURE__ */ jsx("div", { className: "px-4 py-8 text-sm text-muted-foreground", children: "No inbox items" }),
|
|
9414
|
+
items.map((item) => {
|
|
9415
|
+
const Icon = kindIcon(item.kind);
|
|
9416
|
+
const selected = item.id === selectedItemId;
|
|
9417
|
+
return /* @__PURE__ */ jsxs(
|
|
9418
|
+
"button",
|
|
9419
|
+
{
|
|
9420
|
+
type: "button",
|
|
9421
|
+
"aria-label": `Open inbox item ${item.title}`,
|
|
9422
|
+
className: cn(
|
|
9423
|
+
"flex w-full min-w-0 gap-3 border-l-2 px-3 py-3 text-left transition-colors hover:bg-input",
|
|
9424
|
+
selected ? "border-l-primary bg-line text-foreground" : "border-l-transparent text-subtle",
|
|
9425
|
+
!item.readAt && "font-medium text-foreground"
|
|
9426
|
+
),
|
|
9427
|
+
onClick: () => void selectItem(item.id),
|
|
9428
|
+
children: [
|
|
9429
|
+
/* @__PURE__ */ jsx(Icon, { className: "mt-0.5 size-4 shrink-0 text-muted-foreground" }),
|
|
9430
|
+
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
9431
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center justify-between gap-2", children: [
|
|
9432
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-xs uppercase tracking-wide text-muted-foreground", children: kindLabel(item.kind) }),
|
|
9433
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: formatRelative(item.createdAt) })
|
|
9434
|
+
] }),
|
|
9435
|
+
/* @__PURE__ */ jsx("span", { className: "mt-1 block truncate text-sm", children: item.title }),
|
|
9436
|
+
/* @__PURE__ */ jsx("span", { className: "mt-1 block text-xs text-muted-foreground", children: statusLabel(item.status) })
|
|
9437
|
+
] })
|
|
9438
|
+
]
|
|
9439
|
+
},
|
|
9440
|
+
item.id
|
|
9441
|
+
);
|
|
9442
|
+
}),
|
|
9443
|
+
nextCursor && /* @__PURE__ */ jsx(
|
|
9444
|
+
"button",
|
|
9445
|
+
{
|
|
9446
|
+
type: "button",
|
|
9447
|
+
className: "w-full px-3 py-3 text-sm text-muted-foreground hover:bg-input",
|
|
9448
|
+
onClick: () => void load(nextCursor),
|
|
9449
|
+
disabled: loading,
|
|
9450
|
+
children: loading ? "Loading" : "Load more"
|
|
9451
|
+
}
|
|
9452
|
+
)
|
|
9453
|
+
] })
|
|
9454
|
+
] }),
|
|
9455
|
+
selectedItem ? /* @__PURE__ */ jsx(
|
|
9456
|
+
InboxDetail,
|
|
9457
|
+
{
|
|
9458
|
+
item: selectedItem,
|
|
9459
|
+
adapter: inboxAdapter,
|
|
9460
|
+
onUpdated: updateSelectedItem,
|
|
9461
|
+
onDeleted: deleteItem,
|
|
9462
|
+
onSessionSelect
|
|
9463
|
+
}
|
|
9464
|
+
) : /* @__PURE__ */ jsx("main", { className: "flex min-w-0 flex-1 items-center justify-center px-4 text-sm text-muted-foreground", children: "Select an item" })
|
|
9465
|
+
] });
|
|
8693
9466
|
}
|
|
8694
|
-
|
|
9467
|
+
function useInboxUnreadCount(adapter) {
|
|
9468
|
+
const [unreadCount, setUnreadCount] = useState(0);
|
|
9469
|
+
const [hasLoaded, setHasLoaded] = useState(false);
|
|
9470
|
+
const [error, setError] = useState(null);
|
|
9471
|
+
useEffect(() => {
|
|
9472
|
+
let cancelled = false;
|
|
9473
|
+
let stream = null;
|
|
9474
|
+
if (!adapter.listInbox || !adapter.openInboxStream) {
|
|
9475
|
+
setError("Inbox is not supported by this adapter.");
|
|
9476
|
+
setHasLoaded(true);
|
|
9477
|
+
return () => {
|
|
9478
|
+
cancelled = true;
|
|
9479
|
+
};
|
|
9480
|
+
}
|
|
9481
|
+
adapter.listInbox({ status: "pending", limit: 200 }).then((response) => {
|
|
9482
|
+
if (cancelled) return;
|
|
9483
|
+
setUnreadCount(response.items.filter((item) => !item.readAt).length);
|
|
9484
|
+
setHasLoaded(true);
|
|
9485
|
+
}).catch((err) => {
|
|
9486
|
+
if (cancelled) return;
|
|
9487
|
+
setError(err instanceof Error ? err.message : "Failed to load inbox");
|
|
9488
|
+
setHasLoaded(true);
|
|
9489
|
+
});
|
|
9490
|
+
try {
|
|
9491
|
+
stream = adapter.openInboxStream();
|
|
9492
|
+
stream.addEventListener("snapshot", (event) => {
|
|
9493
|
+
if (cancelled) return;
|
|
9494
|
+
const payload = JSON.parse(event.data);
|
|
9495
|
+
const ids = Array.isArray(payload.unread_ids) ? payload.unread_ids : [];
|
|
9496
|
+
setUnreadCount(ids.length);
|
|
9497
|
+
setHasLoaded(true);
|
|
9498
|
+
});
|
|
9499
|
+
stream.addEventListener("item", () => {
|
|
9500
|
+
if (cancelled) return;
|
|
9501
|
+
setUnreadCount((count) => count + 1);
|
|
9502
|
+
setHasLoaded(true);
|
|
9503
|
+
});
|
|
9504
|
+
stream.onerror = () => {
|
|
9505
|
+
if (!cancelled) setError("Inbox stream disconnected.");
|
|
9506
|
+
};
|
|
9507
|
+
} catch (err) {
|
|
9508
|
+
setError(err instanceof Error ? err.message : "Failed to open inbox stream");
|
|
9509
|
+
}
|
|
9510
|
+
return () => {
|
|
9511
|
+
cancelled = true;
|
|
9512
|
+
stream?.close();
|
|
9513
|
+
};
|
|
9514
|
+
}, [adapter]);
|
|
9515
|
+
return { unreadCount, hasLoaded, error };
|
|
9516
|
+
}
|
|
9517
|
+
var DEFAULT_LIMIT2 = 50;
|
|
8695
9518
|
var DEFAULT_POLL_INTERVAL_MS = 3e4;
|
|
8696
9519
|
function fingerprint(items) {
|
|
8697
9520
|
return items.map(
|
|
@@ -8706,7 +9529,7 @@ function formatKind(value) {
|
|
|
8706
9529
|
if (!value) return "Scheduled";
|
|
8707
9530
|
return value.split(/[_-]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
8708
9531
|
}
|
|
8709
|
-
function
|
|
9532
|
+
function formatRelative2(value, label) {
|
|
8710
9533
|
if (!value) return null;
|
|
8711
9534
|
const date = new Date(value);
|
|
8712
9535
|
if (Number.isNaN(date.getTime())) return null;
|
|
@@ -8756,10 +9579,10 @@ function ScheduledWorkRow({
|
|
|
8756
9579
|
const lastSessionId = item.lastSessionId ?? null;
|
|
8757
9580
|
const meta = [
|
|
8758
9581
|
scheduleDisplay(item),
|
|
8759
|
-
|
|
8760
|
-
|
|
9582
|
+
formatRelative2(item.nextRunAt, "Next"),
|
|
9583
|
+
formatRelative2(item.lastRunAt, "Last"),
|
|
8761
9584
|
formatRuns(item.runCount),
|
|
8762
|
-
item.expiresAt ?
|
|
9585
|
+
item.expiresAt ? formatRelative2(item.expiresAt, "Expires") : null
|
|
8763
9586
|
].filter(Boolean);
|
|
8764
9587
|
const openLastRun = () => {
|
|
8765
9588
|
if (lastSessionId) onOpenLastRun(lastSessionId);
|
|
@@ -8839,7 +9662,7 @@ function ScheduledWorkPanel({
|
|
|
8839
9662
|
adapter,
|
|
8840
9663
|
agentId,
|
|
8841
9664
|
title = "Scheduled Work",
|
|
8842
|
-
limit =
|
|
9665
|
+
limit = DEFAULT_LIMIT2,
|
|
8843
9666
|
status = "active",
|
|
8844
9667
|
hideHeader = false,
|
|
8845
9668
|
pollIntervalMs = DEFAULT_POLL_INTERVAL_MS,
|
|
@@ -9359,6 +10182,6 @@ function SessionTreePanel({
|
|
|
9359
10182
|
] });
|
|
9360
10183
|
}
|
|
9361
10184
|
|
|
9362
|
-
export { AgentChat, AgentChatAdapterProvider, MessageResponse, ScheduledWorkPanel, SessionTreePanel, useAgentChatAdapterContext, useAgentChatRuntime };
|
|
10185
|
+
export { AgentChat, AgentChatAdapterProvider, InboxPanel, MessageResponse, ScheduledWorkPanel, SessionTreePanel, useAgentChatAdapterContext, useAgentChatRuntime, useInboxUnreadCount };
|
|
9363
10186
|
//# sourceMappingURL=index.js.map
|
|
9364
10187
|
//# sourceMappingURL=index.js.map
|