@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.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, FolderOpenIcon, UploadIcon, RefreshCwIcon, AlertCircleIcon, SquareIcon, ArrowDownIcon, RotateCcwIcon, MousePointer2Icon, DownloadIcon, TrashIcon, ImageIcon, FileTextIcon,
|
|
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);
|
|
@@ -124,11 +128,14 @@ function BrowserControlBar({
|
|
|
124
128
|
error && /* @__PURE__ */ jsx("span", { className: "max-w-40 truncate text-[11px] text-destructive", children: error })
|
|
125
129
|
] });
|
|
126
130
|
}
|
|
127
|
-
function BrowserLiveView({
|
|
131
|
+
function BrowserLiveView({
|
|
132
|
+
src,
|
|
133
|
+
testId = "browser-iframe"
|
|
134
|
+
}) {
|
|
128
135
|
return /* @__PURE__ */ jsx(
|
|
129
136
|
"iframe",
|
|
130
137
|
{
|
|
131
|
-
"data-testid":
|
|
138
|
+
"data-testid": testId,
|
|
132
139
|
title: "Browser live view",
|
|
133
140
|
src,
|
|
134
141
|
className: "h-full w-full border-0 bg-black",
|
|
@@ -153,38 +160,373 @@ function BrowserStatusDot({
|
|
|
153
160
|
}
|
|
154
161
|
);
|
|
155
162
|
}
|
|
156
|
-
function
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
163
|
+
function Dialog({
|
|
164
|
+
...props
|
|
165
|
+
}) {
|
|
166
|
+
return /* @__PURE__ */ jsx(Dialog$1.Root, { "data-slot": "dialog", ...props });
|
|
167
|
+
}
|
|
168
|
+
function DialogPortal({
|
|
169
|
+
...props
|
|
170
|
+
}) {
|
|
171
|
+
return /* @__PURE__ */ jsx(Dialog$1.Portal, { "data-slot": "dialog-portal", ...props });
|
|
172
|
+
}
|
|
173
|
+
function DialogOverlay({
|
|
174
|
+
className,
|
|
175
|
+
...props
|
|
176
|
+
}) {
|
|
177
|
+
return /* @__PURE__ */ jsx(
|
|
178
|
+
Dialog$1.Overlay,
|
|
179
|
+
{
|
|
180
|
+
"data-slot": "dialog-overlay",
|
|
181
|
+
className: cn(
|
|
182
|
+
"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",
|
|
183
|
+
className
|
|
184
|
+
),
|
|
185
|
+
...props
|
|
186
|
+
}
|
|
160
187
|
);
|
|
161
|
-
|
|
188
|
+
}
|
|
189
|
+
function DialogContent({
|
|
190
|
+
className,
|
|
191
|
+
children,
|
|
192
|
+
showCloseButton = true,
|
|
193
|
+
...props
|
|
194
|
+
}) {
|
|
195
|
+
return /* @__PURE__ */ jsxs(DialogPortal, { children: [
|
|
196
|
+
/* @__PURE__ */ jsx(DialogOverlay, {}),
|
|
197
|
+
/* @__PURE__ */ jsxs(
|
|
198
|
+
Dialog$1.Content,
|
|
199
|
+
{
|
|
200
|
+
"data-slot": "dialog-content",
|
|
201
|
+
className: cn(
|
|
202
|
+
"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",
|
|
203
|
+
className
|
|
204
|
+
),
|
|
205
|
+
...props,
|
|
206
|
+
children: [
|
|
207
|
+
children,
|
|
208
|
+
showCloseButton && /* @__PURE__ */ jsx(Dialog$1.Close, { "data-slot": "dialog-close", asChild: true, children: /* @__PURE__ */ jsxs(
|
|
209
|
+
Button,
|
|
210
|
+
{
|
|
211
|
+
variant: "ghost",
|
|
212
|
+
className: "absolute top-5 right-5 bg-secondary",
|
|
213
|
+
size: "icon-sm",
|
|
214
|
+
children: [
|
|
215
|
+
/* @__PURE__ */ jsx(
|
|
216
|
+
XIcon,
|
|
217
|
+
{}
|
|
218
|
+
),
|
|
219
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
|
|
220
|
+
]
|
|
221
|
+
}
|
|
222
|
+
) })
|
|
223
|
+
]
|
|
224
|
+
}
|
|
225
|
+
)
|
|
226
|
+
] });
|
|
227
|
+
}
|
|
228
|
+
function DialogHeader({ className, ...props }) {
|
|
229
|
+
return /* @__PURE__ */ jsx(
|
|
230
|
+
"div",
|
|
231
|
+
{
|
|
232
|
+
"data-slot": "dialog-header",
|
|
233
|
+
className: cn("flex flex-col gap-2", className),
|
|
234
|
+
...props
|
|
235
|
+
}
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
function DialogFooter({
|
|
239
|
+
className,
|
|
240
|
+
showCloseButton = false,
|
|
241
|
+
children,
|
|
242
|
+
...props
|
|
243
|
+
}) {
|
|
162
244
|
return /* @__PURE__ */ jsxs(
|
|
163
245
|
"div",
|
|
164
246
|
{
|
|
165
|
-
"data-
|
|
166
|
-
className:
|
|
247
|
+
"data-slot": "dialog-footer",
|
|
248
|
+
className: cn(
|
|
249
|
+
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
|
250
|
+
className
|
|
251
|
+
),
|
|
252
|
+
...props,
|
|
167
253
|
children: [
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
254
|
+
children,
|
|
255
|
+
showCloseButton && /* @__PURE__ */ jsx(Dialog$1.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", children: "Close" }) })
|
|
256
|
+
]
|
|
257
|
+
}
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
function DialogTitle({
|
|
261
|
+
className,
|
|
262
|
+
...props
|
|
263
|
+
}) {
|
|
264
|
+
return /* @__PURE__ */ jsx(
|
|
265
|
+
Dialog$1.Title,
|
|
266
|
+
{
|
|
267
|
+
"data-slot": "dialog-title",
|
|
268
|
+
className: cn(
|
|
269
|
+
"font-heading text-lg leading-none font-semibold tracking-wider uppercase",
|
|
270
|
+
className
|
|
271
|
+
),
|
|
272
|
+
...props
|
|
273
|
+
}
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
function DialogDescription({
|
|
277
|
+
className,
|
|
278
|
+
...props
|
|
279
|
+
}) {
|
|
280
|
+
return /* @__PURE__ */ jsx(
|
|
281
|
+
Dialog$1.Description,
|
|
282
|
+
{
|
|
283
|
+
"data-slot": "dialog-description",
|
|
284
|
+
className: cn(
|
|
285
|
+
"mt-0.5 text-sm leading-relaxed text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
|
|
286
|
+
className
|
|
287
|
+
),
|
|
288
|
+
...props
|
|
289
|
+
}
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
function TooltipProvider({
|
|
293
|
+
delayDuration = 0,
|
|
294
|
+
...props
|
|
295
|
+
}) {
|
|
296
|
+
return /* @__PURE__ */ jsx(
|
|
297
|
+
Tooltip$1.Provider,
|
|
298
|
+
{
|
|
299
|
+
"data-slot": "tooltip-provider",
|
|
300
|
+
delayDuration,
|
|
301
|
+
...props
|
|
302
|
+
}
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
function Tooltip({
|
|
306
|
+
...props
|
|
307
|
+
}) {
|
|
308
|
+
return /* @__PURE__ */ jsx(Tooltip$1.Root, { "data-slot": "tooltip", ...props });
|
|
309
|
+
}
|
|
310
|
+
function TooltipTrigger({
|
|
311
|
+
...props
|
|
312
|
+
}) {
|
|
313
|
+
return /* @__PURE__ */ jsx(Tooltip$1.Trigger, { "data-slot": "tooltip-trigger", ...props });
|
|
314
|
+
}
|
|
315
|
+
function TooltipContent({
|
|
316
|
+
className,
|
|
317
|
+
sideOffset = 0,
|
|
318
|
+
children,
|
|
319
|
+
...props
|
|
320
|
+
}) {
|
|
321
|
+
return /* @__PURE__ */ jsx(Tooltip$1.Portal, { children: /* @__PURE__ */ jsxs(
|
|
322
|
+
Tooltip$1.Content,
|
|
323
|
+
{
|
|
324
|
+
"data-slot": "tooltip-content",
|
|
325
|
+
sideOffset,
|
|
326
|
+
className: cn(
|
|
327
|
+
"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",
|
|
328
|
+
className
|
|
329
|
+
),
|
|
330
|
+
...props,
|
|
331
|
+
children: [
|
|
332
|
+
children,
|
|
333
|
+
/* @__PURE__ */ jsx(Tooltip$1.Arrow, { className: "z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-none bg-foreground fill-foreground" })
|
|
186
334
|
]
|
|
187
335
|
}
|
|
336
|
+
) });
|
|
337
|
+
}
|
|
338
|
+
function BrowserPane({ sessionId, state, adapter }) {
|
|
339
|
+
const [fullscreenOpen, setFullscreenOpen] = useState(false);
|
|
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);
|
|
345
|
+
const hasLiveViewAdapter = typeof adapter.browserLiveViewUrl === "function";
|
|
346
|
+
const hasPreviewAdapter = typeof adapter.getBrowserPreviewSnapshot === "function";
|
|
347
|
+
const hasControlAdapter = typeof adapter.acquireBrowserControl === "function" && typeof adapter.releaseBrowserControl === "function";
|
|
348
|
+
const liveViewUrl = useMemo(() => {
|
|
349
|
+
if (!hasLiveViewAdapter) return "";
|
|
350
|
+
return adapter.browserLiveViewUrl(sessionId);
|
|
351
|
+
}, [adapter, hasLiveViewAdapter, sessionId]);
|
|
352
|
+
const hasLiveView = state.status !== "provisioning" && state.status !== "closed";
|
|
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
|
+
);
|
|
381
|
+
useEffect(() => {
|
|
382
|
+
setFullscreenOpen(false);
|
|
383
|
+
setOpenFullscreenOnControl(false);
|
|
384
|
+
setLocalControlActive(false);
|
|
385
|
+
setPreviewSnapshot(null);
|
|
386
|
+
setPreviewLoading(false);
|
|
387
|
+
setPreviewError(null);
|
|
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
|
+
]);
|
|
429
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
430
|
+
/* @__PURE__ */ jsxs(
|
|
431
|
+
"div",
|
|
432
|
+
{
|
|
433
|
+
"data-testid": "browser-pane",
|
|
434
|
+
className: "flex h-full min-h-0 flex-col bg-background",
|
|
435
|
+
children: [
|
|
436
|
+
/* @__PURE__ */ jsxs("header", { className: "flex min-h-10 items-center gap-2 border-b border-line bg-card px-3 text-xs text-foreground", children: [
|
|
437
|
+
/* @__PURE__ */ jsx(ZapIcon, { className: "size-3.5 text-muted-foreground", "aria-hidden": "true" }),
|
|
438
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Browser" }),
|
|
439
|
+
/* @__PURE__ */ jsx(BrowserStatusDot, { status: state.status }),
|
|
440
|
+
state.controlOwner && /* @__PURE__ */ jsxs("span", { className: "min-w-0 truncate text-amber-500", children: [
|
|
441
|
+
state.controlOwner,
|
|
442
|
+
" has control"
|
|
443
|
+
] }),
|
|
444
|
+
canOpenFullscreen && /* @__PURE__ */ jsx("div", { className: "ml-auto flex items-center", children: /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
445
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
446
|
+
"button",
|
|
447
|
+
{
|
|
448
|
+
type: "button",
|
|
449
|
+
"aria-label": "Maximize browser",
|
|
450
|
+
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",
|
|
451
|
+
onClick: () => setFullscreenOpen(true),
|
|
452
|
+
children: /* @__PURE__ */ jsx(Maximize2Icon, { className: "size-3.5", "aria-hidden": "true" })
|
|
453
|
+
}
|
|
454
|
+
) }),
|
|
455
|
+
/* @__PURE__ */ jsx(TooltipContent, { side: "bottom", children: "Full screen" })
|
|
456
|
+
] }) }) })
|
|
457
|
+
] }),
|
|
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,
|
|
460
|
+
{
|
|
461
|
+
src: previewSnapshot.src,
|
|
462
|
+
fit: "contain",
|
|
463
|
+
testId: "browser-preview-image"
|
|
464
|
+
}
|
|
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." }) }),
|
|
466
|
+
hasLiveView && hasControlAdapter && /* @__PURE__ */ jsx(
|
|
467
|
+
BrowserControlBar,
|
|
468
|
+
{
|
|
469
|
+
sessionId,
|
|
470
|
+
hasControl: localControlActive,
|
|
471
|
+
adapter,
|
|
472
|
+
onControlAcquired: () => {
|
|
473
|
+
setLocalControlActive(true);
|
|
474
|
+
setOpenFullscreenOnControl(true);
|
|
475
|
+
},
|
|
476
|
+
onControlReleased: () => setLocalControlActive(false)
|
|
477
|
+
}
|
|
478
|
+
)
|
|
479
|
+
]
|
|
480
|
+
}
|
|
481
|
+
),
|
|
482
|
+
/* @__PURE__ */ jsx(Dialog, { open: fullscreenOpen, onOpenChange: handleFullscreenOpenChange, children: /* @__PURE__ */ jsxs(
|
|
483
|
+
DialogContent,
|
|
484
|
+
{
|
|
485
|
+
"aria-describedby": void 0,
|
|
486
|
+
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",
|
|
487
|
+
children: [
|
|
488
|
+
/* @__PURE__ */ jsxs(DialogHeader, { className: "h-10 shrink-0 flex-row items-center gap-2 border-b border-line bg-card px-4 py-0", children: [
|
|
489
|
+
/* @__PURE__ */ jsx(ZapIcon, { className: "size-3.5 text-muted-foreground", "aria-hidden": "true" }),
|
|
490
|
+
/* @__PURE__ */ jsx(DialogTitle, { className: "text-xs normal-case tracking-normal", children: "Browser" }),
|
|
491
|
+
/* @__PURE__ */ jsx(BrowserStatusDot, { status: state.status }),
|
|
492
|
+
state.controlOwner && /* @__PURE__ */ jsxs("span", { className: "min-w-0 truncate text-xs text-amber-500", children: [
|
|
493
|
+
state.controlOwner,
|
|
494
|
+
" has control"
|
|
495
|
+
] })
|
|
496
|
+
] }),
|
|
497
|
+
/* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 bg-black", children: canUseLiveView ? /* @__PURE__ */ jsx(
|
|
498
|
+
BrowserLiveView,
|
|
499
|
+
{
|
|
500
|
+
src: liveViewUrl,
|
|
501
|
+
testId: "browser-fullscreen-iframe"
|
|
502
|
+
}
|
|
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." }) })
|
|
511
|
+
]
|
|
512
|
+
}
|
|
513
|
+
) })
|
|
514
|
+
] });
|
|
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
|
+
}
|
|
188
530
|
);
|
|
189
531
|
}
|
|
190
532
|
var Conversation = ({ className, ...props }) => /* @__PURE__ */ jsx(
|
|
@@ -255,66 +597,20 @@ var ConversationScrollButton = ({
|
|
|
255
597
|
}
|
|
256
598
|
);
|
|
257
599
|
};
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
...props
|
|
273
|
-
}) {
|
|
274
|
-
return /* @__PURE__ */ jsx(Tooltip$1.Root, { "data-slot": "tooltip", ...props });
|
|
275
|
-
}
|
|
276
|
-
function TooltipTrigger({
|
|
277
|
-
...props
|
|
278
|
-
}) {
|
|
279
|
-
return /* @__PURE__ */ jsx(Tooltip$1.Trigger, { "data-slot": "tooltip-trigger", ...props });
|
|
280
|
-
}
|
|
281
|
-
function TooltipContent({
|
|
282
|
-
className,
|
|
283
|
-
sideOffset = 0,
|
|
284
|
-
children,
|
|
285
|
-
...props
|
|
286
|
-
}) {
|
|
287
|
-
return /* @__PURE__ */ jsx(Tooltip$1.Portal, { children: /* @__PURE__ */ jsxs(
|
|
288
|
-
Tooltip$1.Content,
|
|
289
|
-
{
|
|
290
|
-
"data-slot": "tooltip-content",
|
|
291
|
-
sideOffset,
|
|
292
|
-
className: cn(
|
|
293
|
-
"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",
|
|
294
|
-
className
|
|
295
|
-
),
|
|
296
|
-
...props,
|
|
297
|
-
children: [
|
|
298
|
-
children,
|
|
299
|
-
/* @__PURE__ */ jsx(Tooltip$1.Arrow, { className: "z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-none bg-foreground fill-foreground" })
|
|
300
|
-
]
|
|
301
|
-
}
|
|
302
|
-
) });
|
|
303
|
-
}
|
|
304
|
-
var Message = ({ className, from, ...props }) => /* @__PURE__ */ jsx(
|
|
305
|
-
"div",
|
|
306
|
-
{
|
|
307
|
-
className: cn(
|
|
308
|
-
"group flex w-full flex-col gap-2",
|
|
309
|
-
from === "user" ? "is-user max-w-[85%]" : "is-assistant max-w-full",
|
|
310
|
-
className
|
|
311
|
-
),
|
|
312
|
-
...props
|
|
313
|
-
}
|
|
314
|
-
);
|
|
315
|
-
var MessageContent = ({
|
|
316
|
-
children,
|
|
317
|
-
className,
|
|
600
|
+
var Message = ({ className, from, ...props }) => /* @__PURE__ */ jsx(
|
|
601
|
+
"div",
|
|
602
|
+
{
|
|
603
|
+
className: cn(
|
|
604
|
+
"group flex w-full flex-col gap-2",
|
|
605
|
+
from === "user" ? "is-user max-w-[85%]" : "is-assistant max-w-full",
|
|
606
|
+
className
|
|
607
|
+
),
|
|
608
|
+
...props
|
|
609
|
+
}
|
|
610
|
+
);
|
|
611
|
+
var MessageContent = ({
|
|
612
|
+
children,
|
|
613
|
+
className,
|
|
318
614
|
...props
|
|
319
615
|
}) => /* @__PURE__ */ jsx(
|
|
320
616
|
"div",
|
|
@@ -674,66 +970,82 @@ function TimelineSeparator({
|
|
|
674
970
|
);
|
|
675
971
|
}
|
|
676
972
|
function BrowserActivityGroup({ calls }) {
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
/* @__PURE__ */
|
|
681
|
-
"button",
|
|
682
|
-
{
|
|
683
|
-
type: "button",
|
|
684
|
-
onClick: () => setOpen((current) => !current),
|
|
685
|
-
className: "flex w-full items-center gap-2 px-3 py-2 text-left hover:bg-muted/40",
|
|
686
|
-
children: [
|
|
687
|
-
open ? /* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-3 shrink-0", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ChevronRightIcon, { className: "size-3 shrink-0", "aria-hidden": "true" }),
|
|
688
|
-
/* @__PURE__ */ jsx(ZapIcon, { className: "size-3 shrink-0 text-amber-500", "aria-hidden": "true" }),
|
|
689
|
-
/* @__PURE__ */ jsx("span", { className: "font-semibold text-foreground", children: "browser" }),
|
|
690
|
-
/* @__PURE__ */ jsxs("span", { className: "min-w-0 truncate text-muted-foreground", children: [
|
|
691
|
-
"(",
|
|
692
|
-
calls.length,
|
|
693
|
-
" ",
|
|
694
|
-
calls.length === 1 ? "action" : "actions",
|
|
695
|
-
" - latest:",
|
|
696
|
-
" ",
|
|
697
|
-
latest ? summarise(latest) : "none",
|
|
698
|
-
")"
|
|
699
|
-
] })
|
|
700
|
-
]
|
|
701
|
-
}
|
|
702
|
-
),
|
|
703
|
-
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) => {
|
|
704
977
|
const error = stringValue(parseJson(call.result).error);
|
|
705
|
-
return /* @__PURE__ */ jsxs(
|
|
706
|
-
"
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
error
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
),
|
|
718
|
-
/* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: summarise(call) }),
|
|
719
|
-
error && /* @__PURE__ */ jsx("span", { className: "ml-auto max-w-40 truncate text-destructive", children: error })
|
|
720
|
-
]
|
|
721
|
-
},
|
|
722
|
-
call.id
|
|
723
|
-
);
|
|
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);
|
|
724
990
|
}) })
|
|
725
991
|
] });
|
|
726
992
|
}
|
|
727
|
-
function
|
|
728
|
-
const verb = call.toolName.replace(/^browser_/, "");
|
|
993
|
+
function summarize(call) {
|
|
729
994
|
const args = parseJson(call.args);
|
|
730
|
-
const
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
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;
|
|
737
1049
|
}
|
|
738
1050
|
function parseJson(raw) {
|
|
739
1051
|
if (!raw) return {};
|
|
@@ -1773,135 +2085,20 @@ function WebToolBlock({ tc }) {
|
|
|
1773
2085
|
displayText && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/70 truncate text-xs", children: displayText })
|
|
1774
2086
|
] });
|
|
1775
2087
|
}
|
|
1776
|
-
function
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
}
|
|
1784
|
-
return /* @__PURE__ */
|
|
1785
|
-
}
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
...props
|
|
1789
|
-
}) {
|
|
1790
|
-
return /* @__PURE__ */ jsx(
|
|
1791
|
-
Dialog$1.Overlay,
|
|
1792
|
-
{
|
|
1793
|
-
"data-slot": "dialog-overlay",
|
|
1794
|
-
className: cn(
|
|
1795
|
-
"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",
|
|
1796
|
-
className
|
|
1797
|
-
),
|
|
1798
|
-
...props
|
|
1799
|
-
}
|
|
1800
|
-
);
|
|
1801
|
-
}
|
|
1802
|
-
function DialogContent({
|
|
1803
|
-
className,
|
|
1804
|
-
children,
|
|
1805
|
-
showCloseButton = true,
|
|
1806
|
-
...props
|
|
1807
|
-
}) {
|
|
1808
|
-
return /* @__PURE__ */ jsxs(DialogPortal, { children: [
|
|
1809
|
-
/* @__PURE__ */ jsx(DialogOverlay, {}),
|
|
1810
|
-
/* @__PURE__ */ jsxs(
|
|
1811
|
-
Dialog$1.Content,
|
|
1812
|
-
{
|
|
1813
|
-
"data-slot": "dialog-content",
|
|
1814
|
-
className: cn(
|
|
1815
|
-
"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",
|
|
1816
|
-
className
|
|
1817
|
-
),
|
|
1818
|
-
...props,
|
|
1819
|
-
children: [
|
|
1820
|
-
children,
|
|
1821
|
-
showCloseButton && /* @__PURE__ */ jsx(Dialog$1.Close, { "data-slot": "dialog-close", asChild: true, children: /* @__PURE__ */ jsxs(
|
|
1822
|
-
Button,
|
|
1823
|
-
{
|
|
1824
|
-
variant: "ghost",
|
|
1825
|
-
className: "absolute top-5 right-5 bg-secondary",
|
|
1826
|
-
size: "icon-sm",
|
|
1827
|
-
children: [
|
|
1828
|
-
/* @__PURE__ */ jsx(
|
|
1829
|
-
XIcon,
|
|
1830
|
-
{}
|
|
1831
|
-
),
|
|
1832
|
-
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
|
|
1833
|
-
]
|
|
1834
|
-
}
|
|
1835
|
-
) })
|
|
1836
|
-
]
|
|
1837
|
-
}
|
|
1838
|
-
)
|
|
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 })
|
|
1839
2100
|
] });
|
|
1840
2101
|
}
|
|
1841
|
-
function DialogHeader({ className, ...props }) {
|
|
1842
|
-
return /* @__PURE__ */ jsx(
|
|
1843
|
-
"div",
|
|
1844
|
-
{
|
|
1845
|
-
"data-slot": "dialog-header",
|
|
1846
|
-
className: cn("flex flex-col gap-2", className),
|
|
1847
|
-
...props
|
|
1848
|
-
}
|
|
1849
|
-
);
|
|
1850
|
-
}
|
|
1851
|
-
function DialogFooter({
|
|
1852
|
-
className,
|
|
1853
|
-
showCloseButton = false,
|
|
1854
|
-
children,
|
|
1855
|
-
...props
|
|
1856
|
-
}) {
|
|
1857
|
-
return /* @__PURE__ */ jsxs(
|
|
1858
|
-
"div",
|
|
1859
|
-
{
|
|
1860
|
-
"data-slot": "dialog-footer",
|
|
1861
|
-
className: cn(
|
|
1862
|
-
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
|
1863
|
-
className
|
|
1864
|
-
),
|
|
1865
|
-
...props,
|
|
1866
|
-
children: [
|
|
1867
|
-
children,
|
|
1868
|
-
showCloseButton && /* @__PURE__ */ jsx(Dialog$1.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", children: "Close" }) })
|
|
1869
|
-
]
|
|
1870
|
-
}
|
|
1871
|
-
);
|
|
1872
|
-
}
|
|
1873
|
-
function DialogTitle({
|
|
1874
|
-
className,
|
|
1875
|
-
...props
|
|
1876
|
-
}) {
|
|
1877
|
-
return /* @__PURE__ */ jsx(
|
|
1878
|
-
Dialog$1.Title,
|
|
1879
|
-
{
|
|
1880
|
-
"data-slot": "dialog-title",
|
|
1881
|
-
className: cn(
|
|
1882
|
-
"font-heading text-lg leading-none font-semibold tracking-wider uppercase",
|
|
1883
|
-
className
|
|
1884
|
-
),
|
|
1885
|
-
...props
|
|
1886
|
-
}
|
|
1887
|
-
);
|
|
1888
|
-
}
|
|
1889
|
-
function DialogDescription({
|
|
1890
|
-
className,
|
|
1891
|
-
...props
|
|
1892
|
-
}) {
|
|
1893
|
-
return /* @__PURE__ */ jsx(
|
|
1894
|
-
Dialog$1.Description,
|
|
1895
|
-
{
|
|
1896
|
-
"data-slot": "dialog-description",
|
|
1897
|
-
className: cn(
|
|
1898
|
-
"mt-0.5 text-sm leading-relaxed text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
|
|
1899
|
-
className
|
|
1900
|
-
),
|
|
1901
|
-
...props
|
|
1902
|
-
}
|
|
1903
|
-
);
|
|
1904
|
-
}
|
|
1905
2102
|
function splitChangeLines(change) {
|
|
1906
2103
|
const v = change.value;
|
|
1907
2104
|
if (v === "") return [];
|
|
@@ -3839,6 +4036,8 @@ function ToolCallBlock({
|
|
|
3839
4036
|
case "web_search":
|
|
3840
4037
|
case "web_crawl":
|
|
3841
4038
|
return /* @__PURE__ */ jsx(WebToolBlock, { tc });
|
|
4039
|
+
case "vision_analyze":
|
|
4040
|
+
return /* @__PURE__ */ jsx(VisionAnalyzeBlock, { tc });
|
|
3842
4041
|
case "read_file":
|
|
3843
4042
|
return /* @__PURE__ */ jsx(ReadFileBlock, { tc, onFileSelect });
|
|
3844
4043
|
case "write_file":
|
|
@@ -5332,18 +5531,21 @@ function ChatComposerInner({
|
|
|
5332
5531
|
[menuOpen, filteredCommands, selectedIndex, handleCommandSelect, textInput]
|
|
5333
5532
|
);
|
|
5334
5533
|
const handleSubmit = useCallback(
|
|
5335
|
-
(message) => {
|
|
5534
|
+
async (message) => {
|
|
5336
5535
|
const text = message.text.trim();
|
|
5337
|
-
if (!text ||
|
|
5536
|
+
if (!text || disabled) return;
|
|
5338
5537
|
const images = [];
|
|
5339
5538
|
for (const file of message.files) {
|
|
5340
5539
|
if (file.mediaType?.startsWith("image/") && file.url) {
|
|
5341
5540
|
images.push({ data: file.url, mimeType: file.mediaType });
|
|
5342
5541
|
}
|
|
5343
5542
|
}
|
|
5344
|
-
|
|
5543
|
+
if (isRunning) {
|
|
5544
|
+
await onStop();
|
|
5545
|
+
}
|
|
5546
|
+
await onSend(text, images.length > 0 ? images : void 0);
|
|
5345
5547
|
},
|
|
5346
|
-
[onSend, isRunning, disabled]
|
|
5548
|
+
[onSend, onStop, isRunning, disabled]
|
|
5347
5549
|
);
|
|
5348
5550
|
return /* @__PURE__ */ jsxs(Popover, { open: menuOpen, onOpenChange: (open) => {
|
|
5349
5551
|
if (!open) {
|
|
@@ -7185,10 +7387,9 @@ function WorkspacePanel({
|
|
|
7185
7387
|
sessionId,
|
|
7186
7388
|
selectedPath,
|
|
7187
7389
|
onSelectedPathChange,
|
|
7188
|
-
collapsed = false,
|
|
7189
|
-
onCollapsedChange,
|
|
7190
7390
|
refreshSignal = 0,
|
|
7191
|
-
disabled = false
|
|
7391
|
+
disabled = false,
|
|
7392
|
+
fillParent = false
|
|
7192
7393
|
}) {
|
|
7193
7394
|
const fileInputRef = useRef(null);
|
|
7194
7395
|
const [entries, setEntries] = useState([]);
|
|
@@ -7389,34 +7590,17 @@ function WorkspacePanel({
|
|
|
7389
7590
|
window.removeEventListener("mouseup", onMouseUp);
|
|
7390
7591
|
};
|
|
7391
7592
|
}, []);
|
|
7392
|
-
if (collapsed) {
|
|
7393
|
-
return /* @__PURE__ */ jsx(
|
|
7394
|
-
"aside",
|
|
7395
|
-
{
|
|
7396
|
-
"data-testid": "workspace-panel",
|
|
7397
|
-
role: "button",
|
|
7398
|
-
tabIndex: 0,
|
|
7399
|
-
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",
|
|
7400
|
-
"aria-label": "Expand workspace",
|
|
7401
|
-
onClick: () => onCollapsedChange?.(false),
|
|
7402
|
-
onKeyDown: (event) => {
|
|
7403
|
-
if (event.key === "Enter" || event.key === " ") {
|
|
7404
|
-
event.preventDefault();
|
|
7405
|
-
onCollapsedChange?.(false);
|
|
7406
|
-
}
|
|
7407
|
-
},
|
|
7408
|
-
children: /* @__PURE__ */ jsx(FolderOpenIcon, { className: "size-4 text-amber-500" })
|
|
7409
|
-
}
|
|
7410
|
-
);
|
|
7411
|
-
}
|
|
7412
7593
|
return /* @__PURE__ */ jsxs(
|
|
7413
7594
|
"aside",
|
|
7414
7595
|
{
|
|
7415
7596
|
"data-testid": "workspace-panel",
|
|
7416
|
-
className:
|
|
7417
|
-
|
|
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 },
|
|
7418
7602
|
children: [
|
|
7419
|
-
/* @__PURE__ */ jsx(
|
|
7603
|
+
!fillParent && /* @__PURE__ */ jsx(
|
|
7420
7604
|
"div",
|
|
7421
7605
|
{
|
|
7422
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",
|
|
@@ -7442,19 +7626,6 @@ function WorkspacePanel({
|
|
|
7442
7626
|
/* @__PURE__ */ jsx("div", { className: "truncate text-sm font-medium text-foreground", children: rootName }),
|
|
7443
7627
|
/* @__PURE__ */ jsx("div", { className: "truncate text-xs text-faint", children: disabled ? "Workspace - Read-only" : "Workspace" })
|
|
7444
7628
|
] }),
|
|
7445
|
-
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
7446
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
7447
|
-
Button,
|
|
7448
|
-
{
|
|
7449
|
-
variant: "ghost",
|
|
7450
|
-
size: "icon-sm",
|
|
7451
|
-
onClick: () => onCollapsedChange?.(true),
|
|
7452
|
-
"aria-label": "Collapse workspace",
|
|
7453
|
-
children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: "size-4" })
|
|
7454
|
-
}
|
|
7455
|
-
) }),
|
|
7456
|
-
/* @__PURE__ */ jsx(TooltipContent, { side: "bottom", children: "Collapse" })
|
|
7457
|
-
] }),
|
|
7458
7629
|
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
7459
7630
|
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
7460
7631
|
Button,
|
|
@@ -7627,7 +7798,8 @@ var WORKSPACE_MUTATING_TOOLS = /* @__PURE__ */ new Set([
|
|
|
7627
7798
|
"terminal",
|
|
7628
7799
|
"write_file",
|
|
7629
7800
|
"patch",
|
|
7630
|
-
"execute_code"
|
|
7801
|
+
"execute_code",
|
|
7802
|
+
"browser_screenshot"
|
|
7631
7803
|
]);
|
|
7632
7804
|
var AGENT_CHAT_LISTENED_EVENTS = [
|
|
7633
7805
|
"user.message",
|
|
@@ -8553,7 +8725,6 @@ function AgentChat({
|
|
|
8553
8725
|
disabled
|
|
8554
8726
|
}) {
|
|
8555
8727
|
const [workspacePath, setWorkspacePath] = useState(null);
|
|
8556
|
-
const [workspaceCollapsed, setWorkspaceCollapsed] = useState(false);
|
|
8557
8728
|
const runtime = useAgentChatRuntime({
|
|
8558
8729
|
adapter,
|
|
8559
8730
|
agentId,
|
|
@@ -8564,6 +8735,7 @@ function AgentChat({
|
|
|
8564
8735
|
const effectiveDisabled = disabled || isReadOnlySession;
|
|
8565
8736
|
const disabledReason = isReadOnlySession ? "Scheduled run is read-only" : void 0;
|
|
8566
8737
|
const browserState = runtime.state.browser;
|
|
8738
|
+
const hasBrowserPanel = browserState !== null && sessionId;
|
|
8567
8739
|
useEffect(() => {
|
|
8568
8740
|
onMessagesChange?.(runtime.messages);
|
|
8569
8741
|
}, [onMessagesChange, runtime.messages]);
|
|
@@ -8582,52 +8754,767 @@ function AgentChat({
|
|
|
8582
8754
|
sessionId,
|
|
8583
8755
|
onFileSelect: handleFileSelect
|
|
8584
8756
|
},
|
|
8585
|
-
children: /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(
|
|
8586
|
-
|
|
8587
|
-
|
|
8588
|
-
|
|
8589
|
-
|
|
8590
|
-
|
|
8591
|
-
|
|
8592
|
-
|
|
8593
|
-
|
|
8594
|
-
|
|
8595
|
-
|
|
8596
|
-
|
|
8597
|
-
|
|
8598
|
-
|
|
8599
|
-
|
|
8600
|
-
|
|
8601
|
-
|
|
8602
|
-
|
|
8603
|
-
|
|
8604
|
-
|
|
8605
|
-
|
|
8606
|
-
|
|
8607
|
-
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
8612
|
-
|
|
8613
|
-
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
|
|
8623
|
-
|
|
8624
|
-
|
|
8625
|
-
|
|
8626
|
-
|
|
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
|
+
) })
|
|
8627
8836
|
}
|
|
8628
8837
|
);
|
|
8629
8838
|
}
|
|
8630
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,
|
|
9027
|
+
{
|
|
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
|
+
]
|
|
9081
|
+
}
|
|
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,
|
|
9123
|
+
{
|
|
9124
|
+
type: "button",
|
|
9125
|
+
size: "sm",
|
|
9126
|
+
disabled,
|
|
9127
|
+
onClick: () => void decide("approve"),
|
|
9128
|
+
children: "Approve"
|
|
9129
|
+
}
|
|
9130
|
+
),
|
|
9131
|
+
/* @__PURE__ */ jsx(
|
|
9132
|
+
Button,
|
|
9133
|
+
{
|
|
9134
|
+
type: "button",
|
|
9135
|
+
size: "sm",
|
|
9136
|
+
variant: "destructive",
|
|
9137
|
+
disabled,
|
|
9138
|
+
onClick: () => void decide("reject"),
|
|
9139
|
+
children: "Reject"
|
|
9140
|
+
}
|
|
9141
|
+
)
|
|
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);
|
|
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]
|
|
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
|
+
] });
|
|
9466
|
+
}
|
|
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;
|
|
8631
9518
|
var DEFAULT_POLL_INTERVAL_MS = 3e4;
|
|
8632
9519
|
function fingerprint(items) {
|
|
8633
9520
|
return items.map(
|
|
@@ -8642,7 +9529,7 @@ function formatKind(value) {
|
|
|
8642
9529
|
if (!value) return "Scheduled";
|
|
8643
9530
|
return value.split(/[_-]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
8644
9531
|
}
|
|
8645
|
-
function
|
|
9532
|
+
function formatRelative2(value, label) {
|
|
8646
9533
|
if (!value) return null;
|
|
8647
9534
|
const date = new Date(value);
|
|
8648
9535
|
if (Number.isNaN(date.getTime())) return null;
|
|
@@ -8692,10 +9579,10 @@ function ScheduledWorkRow({
|
|
|
8692
9579
|
const lastSessionId = item.lastSessionId ?? null;
|
|
8693
9580
|
const meta = [
|
|
8694
9581
|
scheduleDisplay(item),
|
|
8695
|
-
|
|
8696
|
-
|
|
9582
|
+
formatRelative2(item.nextRunAt, "Next"),
|
|
9583
|
+
formatRelative2(item.lastRunAt, "Last"),
|
|
8697
9584
|
formatRuns(item.runCount),
|
|
8698
|
-
item.expiresAt ?
|
|
9585
|
+
item.expiresAt ? formatRelative2(item.expiresAt, "Expires") : null
|
|
8699
9586
|
].filter(Boolean);
|
|
8700
9587
|
const openLastRun = () => {
|
|
8701
9588
|
if (lastSessionId) onOpenLastRun(lastSessionId);
|
|
@@ -8775,7 +9662,7 @@ function ScheduledWorkPanel({
|
|
|
8775
9662
|
adapter,
|
|
8776
9663
|
agentId,
|
|
8777
9664
|
title = "Scheduled Work",
|
|
8778
|
-
limit =
|
|
9665
|
+
limit = DEFAULT_LIMIT2,
|
|
8779
9666
|
status = "active",
|
|
8780
9667
|
hideHeader = false,
|
|
8781
9668
|
pollIntervalMs = DEFAULT_POLL_INTERVAL_MS,
|
|
@@ -9295,6 +10182,6 @@ function SessionTreePanel({
|
|
|
9295
10182
|
] });
|
|
9296
10183
|
}
|
|
9297
10184
|
|
|
9298
|
-
export { AgentChat, AgentChatAdapterProvider, MessageResponse, ScheduledWorkPanel, SessionTreePanel, useAgentChatAdapterContext, useAgentChatRuntime };
|
|
10185
|
+
export { AgentChat, AgentChatAdapterProvider, InboxPanel, MessageResponse, ScheduledWorkPanel, SessionTreePanel, useAgentChatAdapterContext, useAgentChatRuntime, useInboxUnreadCount };
|
|
9299
10186
|
//# sourceMappingURL=index.js.map
|
|
9300
10187
|
//# sourceMappingURL=index.js.map
|