@invergent/agent-chat-react 1.6.1 → 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 +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.cjs
CHANGED
|
@@ -266,7 +266,9 @@ function Button({
|
|
|
266
266
|
function BrowserControlBar({
|
|
267
267
|
sessionId,
|
|
268
268
|
hasControl,
|
|
269
|
-
adapter
|
|
269
|
+
adapter,
|
|
270
|
+
onControlAcquired,
|
|
271
|
+
onControlReleased
|
|
270
272
|
}) {
|
|
271
273
|
const [pending, setPending] = react.useState(false);
|
|
272
274
|
const [error, setError] = react.useState(null);
|
|
@@ -277,8 +279,10 @@ function BrowserControlBar({
|
|
|
277
279
|
try {
|
|
278
280
|
if (hasControl) {
|
|
279
281
|
await adapter.releaseBrowserControl(sessionId);
|
|
282
|
+
onControlReleased?.();
|
|
280
283
|
} else {
|
|
281
284
|
await adapter.acquireBrowserControl(sessionId);
|
|
285
|
+
onControlAcquired?.();
|
|
282
286
|
}
|
|
283
287
|
} catch (nextError) {
|
|
284
288
|
setError(nextError.message);
|
|
@@ -526,19 +530,95 @@ function TooltipContent({
|
|
|
526
530
|
}
|
|
527
531
|
function BrowserPane({ sessionId, state, adapter }) {
|
|
528
532
|
const [fullscreenOpen, setFullscreenOpen] = react.useState(false);
|
|
529
|
-
const [
|
|
533
|
+
const [openFullscreenOnControl, setOpenFullscreenOnControl] = react.useState(false);
|
|
534
|
+
const [localControlActive, setLocalControlActive] = react.useState(false);
|
|
535
|
+
const [previewSnapshot, setPreviewSnapshot] = react.useState(null);
|
|
536
|
+
const [previewLoading, setPreviewLoading] = react.useState(false);
|
|
537
|
+
const [previewError, setPreviewError] = react.useState(null);
|
|
530
538
|
const hasLiveViewAdapter = typeof adapter.browserLiveViewUrl === "function";
|
|
539
|
+
const hasPreviewAdapter = typeof adapter.getBrowserPreviewSnapshot === "function";
|
|
531
540
|
const hasControlAdapter = typeof adapter.acquireBrowserControl === "function" && typeof adapter.releaseBrowserControl === "function";
|
|
532
541
|
const liveViewUrl = react.useMemo(() => {
|
|
533
542
|
if (!hasLiveViewAdapter) return "";
|
|
534
543
|
return adapter.browserLiveViewUrl(sessionId);
|
|
535
544
|
}, [adapter, hasLiveViewAdapter, sessionId]);
|
|
536
545
|
const hasLiveView = state.status !== "provisioning" && state.status !== "closed";
|
|
537
|
-
const
|
|
546
|
+
const hasUserControl = localControlActive;
|
|
547
|
+
const canUseLiveView = hasLiveView && hasUserControl && Boolean(liveViewUrl);
|
|
548
|
+
const canOpenPreview = hasLiveView && (hasPreviewAdapter || canUseLiveView);
|
|
549
|
+
const canOpenFullscreen = canOpenPreview;
|
|
550
|
+
const refreshPreview = react.useCallback(
|
|
551
|
+
async (signal) => {
|
|
552
|
+
if (!hasPreviewAdapter || canUseLiveView) return;
|
|
553
|
+
setPreviewLoading(true);
|
|
554
|
+
setPreviewError(null);
|
|
555
|
+
try {
|
|
556
|
+
const snapshot = await adapter.getBrowserPreviewSnapshot?.(sessionId);
|
|
557
|
+
if (signal?.aborted) return;
|
|
558
|
+
setPreviewSnapshot(snapshot ?? null);
|
|
559
|
+
if (!snapshot) {
|
|
560
|
+
setPreviewError("Browser preview is unavailable.");
|
|
561
|
+
}
|
|
562
|
+
} catch (error) {
|
|
563
|
+
if (signal?.aborted) return;
|
|
564
|
+
setPreviewSnapshot(null);
|
|
565
|
+
setPreviewError(
|
|
566
|
+
error instanceof Error ? error.message : "Browser preview failed."
|
|
567
|
+
);
|
|
568
|
+
} finally {
|
|
569
|
+
if (!signal?.aborted) setPreviewLoading(false);
|
|
570
|
+
}
|
|
571
|
+
},
|
|
572
|
+
[adapter, canUseLiveView, hasPreviewAdapter, sessionId]
|
|
573
|
+
);
|
|
538
574
|
react.useEffect(() => {
|
|
539
575
|
setFullscreenOpen(false);
|
|
540
|
-
|
|
576
|
+
setOpenFullscreenOnControl(false);
|
|
577
|
+
setLocalControlActive(false);
|
|
578
|
+
setPreviewSnapshot(null);
|
|
579
|
+
setPreviewLoading(false);
|
|
580
|
+
setPreviewError(null);
|
|
541
581
|
}, [sessionId]);
|
|
582
|
+
react.useEffect(() => {
|
|
583
|
+
if (!openFullscreenOnControl) return;
|
|
584
|
+
if (!canUseLiveView) return;
|
|
585
|
+
setFullscreenOpen(true);
|
|
586
|
+
setOpenFullscreenOnControl(false);
|
|
587
|
+
}, [canUseLiveView, openFullscreenOnControl]);
|
|
588
|
+
const handleFullscreenOpenChange = react.useCallback(
|
|
589
|
+
(open) => {
|
|
590
|
+
setFullscreenOpen(open);
|
|
591
|
+
if (open || !hasUserControl || !hasControlAdapter) return;
|
|
592
|
+
setLocalControlActive(false);
|
|
593
|
+
void adapter.releaseBrowserControl(sessionId).catch((error) => {
|
|
594
|
+
console.error("Failed to release browser control", error);
|
|
595
|
+
});
|
|
596
|
+
},
|
|
597
|
+
[adapter, hasControlAdapter, hasUserControl, sessionId]
|
|
598
|
+
);
|
|
599
|
+
react.useEffect(() => {
|
|
600
|
+
if (!hasLiveView || canUseLiveView) {
|
|
601
|
+
setPreviewLoading(false);
|
|
602
|
+
setPreviewError(null);
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
if (!hasPreviewAdapter) return;
|
|
606
|
+
const abort = new AbortController();
|
|
607
|
+
void refreshPreview(abort.signal);
|
|
608
|
+
const timer = window.setInterval(() => {
|
|
609
|
+
void refreshPreview(abort.signal);
|
|
610
|
+
}, 5e3);
|
|
611
|
+
return () => {
|
|
612
|
+
abort.abort();
|
|
613
|
+
window.clearInterval(timer);
|
|
614
|
+
};
|
|
615
|
+
}, [
|
|
616
|
+
canUseLiveView,
|
|
617
|
+
fullscreenOpen,
|
|
618
|
+
hasLiveView,
|
|
619
|
+
hasPreviewAdapter,
|
|
620
|
+
refreshPreview
|
|
621
|
+
]);
|
|
542
622
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
543
623
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
544
624
|
"div",
|
|
@@ -568,31 +648,31 @@ function BrowserPane({ sessionId, state, adapter }) {
|
|
|
568
648
|
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "bottom", children: "Full screen" })
|
|
569
649
|
] }) }) })
|
|
570
650
|
] }),
|
|
571
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-0 flex-1 bg-black", children: state.status === "provisioning" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Starting browser..." }) : state.status === "closed" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Browser closed." }) :
|
|
572
|
-
|
|
651
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-0 flex-1 bg-black", children: state.status === "provisioning" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Starting browser..." }) : state.status === "closed" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Browser closed." }) : canUseLiveView ? /* @__PURE__ */ jsxRuntime.jsx(BrowserLiveView, { src: liveViewUrl }) : previewSnapshot ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
652
|
+
BrowserPreviewImage,
|
|
573
653
|
{
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
onClick: () => setInlinePreviewOpen(true),
|
|
578
|
-
children: [
|
|
579
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.PlayIcon, { className: "size-3.5", "aria-hidden": "true" }),
|
|
580
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Open live preview" })
|
|
581
|
-
]
|
|
654
|
+
src: previewSnapshot.src,
|
|
655
|
+
fit: "contain",
|
|
656
|
+
testId: "browser-preview-image"
|
|
582
657
|
}
|
|
583
|
-
) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Browser
|
|
658
|
+
) : previewLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Loading browser preview..." }) : previewError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: previewError }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Browser preview is unavailable." }) }),
|
|
584
659
|
hasLiveView && hasControlAdapter && /* @__PURE__ */ jsxRuntime.jsx(
|
|
585
660
|
BrowserControlBar,
|
|
586
661
|
{
|
|
587
662
|
sessionId,
|
|
588
|
-
hasControl:
|
|
589
|
-
adapter
|
|
663
|
+
hasControl: localControlActive,
|
|
664
|
+
adapter,
|
|
665
|
+
onControlAcquired: () => {
|
|
666
|
+
setLocalControlActive(true);
|
|
667
|
+
setOpenFullscreenOnControl(true);
|
|
668
|
+
},
|
|
669
|
+
onControlReleased: () => setLocalControlActive(false)
|
|
590
670
|
}
|
|
591
671
|
)
|
|
592
672
|
]
|
|
593
673
|
}
|
|
594
674
|
),
|
|
595
|
-
/* @__PURE__ */ jsxRuntime.jsx(Dialog, { open: fullscreenOpen, onOpenChange:
|
|
675
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog, { open: fullscreenOpen, onOpenChange: handleFullscreenOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
596
676
|
DialogContent,
|
|
597
677
|
{
|
|
598
678
|
"aria-describedby": void 0,
|
|
@@ -607,18 +687,41 @@ function BrowserPane({ sessionId, state, adapter }) {
|
|
|
607
687
|
" has control"
|
|
608
688
|
] })
|
|
609
689
|
] }),
|
|
610
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-0 flex-1 bg-black", children:
|
|
690
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-0 flex-1 bg-black", children: canUseLiveView ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
611
691
|
BrowserLiveView,
|
|
612
692
|
{
|
|
613
693
|
src: liveViewUrl,
|
|
614
694
|
testId: "browser-fullscreen-iframe"
|
|
615
695
|
}
|
|
616
|
-
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
696
|
+
) : previewSnapshot ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
697
|
+
BrowserPreviewImage,
|
|
698
|
+
{
|
|
699
|
+
src: previewSnapshot.src,
|
|
700
|
+
fit: "contain",
|
|
701
|
+
testId: "browser-fullscreen-preview-image"
|
|
702
|
+
}
|
|
703
|
+
) : previewLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Loading browser preview..." }) : previewError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: previewError }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center bg-background text-sm text-muted-foreground", children: "Browser preview is unavailable." }) })
|
|
617
704
|
]
|
|
618
705
|
}
|
|
619
706
|
) })
|
|
620
707
|
] });
|
|
621
708
|
}
|
|
709
|
+
function BrowserPreviewImage({
|
|
710
|
+
src,
|
|
711
|
+
fit,
|
|
712
|
+
testId
|
|
713
|
+
}) {
|
|
714
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
715
|
+
"img",
|
|
716
|
+
{
|
|
717
|
+
"data-testid": testId,
|
|
718
|
+
src,
|
|
719
|
+
alt: "Browser preview",
|
|
720
|
+
className: `h-full w-full bg-black ${fit === "cover" ? "object-cover object-top" : "object-contain"}`,
|
|
721
|
+
draggable: false
|
|
722
|
+
}
|
|
723
|
+
);
|
|
724
|
+
}
|
|
622
725
|
|
|
623
726
|
// src/components/ai-elements/conversation.tsx
|
|
624
727
|
init_utils();
|
|
@@ -1075,66 +1178,82 @@ function TimelineSeparator({
|
|
|
1075
1178
|
);
|
|
1076
1179
|
}
|
|
1077
1180
|
function BrowserActivityGroup({ calls }) {
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1082
|
-
"button",
|
|
1083
|
-
{
|
|
1084
|
-
type: "button",
|
|
1085
|
-
onClick: () => setOpen((current) => !current),
|
|
1086
|
-
className: "flex w-full items-center gap-2 px-3 py-2 text-left hover:bg-muted/40",
|
|
1087
|
-
children: [
|
|
1088
|
-
open ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDownIcon, { className: "size-3 shrink-0", "aria-hidden": "true" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRightIcon, { className: "size-3 shrink-0", "aria-hidden": "true" }),
|
|
1089
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ZapIcon, { className: "size-3 shrink-0 text-amber-500", "aria-hidden": "true" }),
|
|
1090
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-foreground", children: "browser" }),
|
|
1091
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0 truncate text-muted-foreground", children: [
|
|
1092
|
-
"(",
|
|
1093
|
-
calls.length,
|
|
1094
|
-
" ",
|
|
1095
|
-
calls.length === 1 ? "action" : "actions",
|
|
1096
|
-
" - latest:",
|
|
1097
|
-
" ",
|
|
1098
|
-
latest ? summarise(latest) : "none",
|
|
1099
|
-
")"
|
|
1100
|
-
] })
|
|
1101
|
-
]
|
|
1102
|
-
}
|
|
1103
|
-
),
|
|
1104
|
-
open && /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "border-t border-line py-1", children: calls.map((call) => {
|
|
1181
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 border border-line bg-card px-3 py-2 text-xs", children: [
|
|
1182
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ZapIcon, { className: "size-3 shrink-0 text-amber-500", "aria-hidden": "true" }),
|
|
1183
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 font-semibold text-foreground", children: "Browser:" }),
|
|
1184
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate text-muted-foreground", children: calls.map((call, index) => {
|
|
1105
1185
|
const error = stringValue(parseJson(call.result).error);
|
|
1106
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1107
|
-
"
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
error
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
),
|
|
1119
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate", children: summarise(call) }),
|
|
1120
|
-
error && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto max-w-40 truncate text-destructive", children: error })
|
|
1121
|
-
]
|
|
1122
|
-
},
|
|
1123
|
-
call.id
|
|
1124
|
-
);
|
|
1186
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
1187
|
+
index > 0 && ", ",
|
|
1188
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1189
|
+
"span",
|
|
1190
|
+
{
|
|
1191
|
+
className: error ? "text-destructive" : void 0,
|
|
1192
|
+
"data-testid": error ? `activity-error-${call.id}` : void 0,
|
|
1193
|
+
title: error || void 0,
|
|
1194
|
+
children: summarize(call)
|
|
1195
|
+
}
|
|
1196
|
+
)
|
|
1197
|
+
] }, call.id);
|
|
1125
1198
|
}) })
|
|
1126
1199
|
] });
|
|
1127
1200
|
}
|
|
1128
|
-
function
|
|
1129
|
-
const verb = call.toolName.replace(/^browser_/, "");
|
|
1201
|
+
function summarize(call) {
|
|
1130
1202
|
const args = parseJson(call.args);
|
|
1131
|
-
const
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1203
|
+
const handler = ACTION_SUMMARIES[call.toolName];
|
|
1204
|
+
return handler ? handler(args) : call.toolName.replace(/^browser_/, "");
|
|
1205
|
+
}
|
|
1206
|
+
var ACTION_SUMMARIES = {
|
|
1207
|
+
browser_navigate: (args) => `navigate to ${stringValue(args.url) || "?"}`,
|
|
1208
|
+
browser_get_state: () => "get state",
|
|
1209
|
+
browser_close: () => "close",
|
|
1210
|
+
browser_click: (args) => {
|
|
1211
|
+
const ref = stringValue(args.ref);
|
|
1212
|
+
if (ref) return `click ${ref}`;
|
|
1213
|
+
const x = asNumber(args.x);
|
|
1214
|
+
const y = asNumber(args.y);
|
|
1215
|
+
if (x !== null && y !== null) return `click (${x}, ${y})`;
|
|
1216
|
+
return "click";
|
|
1217
|
+
},
|
|
1218
|
+
browser_type: (args) => {
|
|
1219
|
+
const text = stringValue(args.text);
|
|
1220
|
+
const ref = stringValue(args.ref);
|
|
1221
|
+
const quoted = text ? `"${truncateText(text)}"` : "";
|
|
1222
|
+
if (!quoted) return ref ? `type into ${ref}` : "type";
|
|
1223
|
+
return ref ? `type ${quoted} into ${ref}` : `type ${quoted}`;
|
|
1224
|
+
},
|
|
1225
|
+
browser_press_key: (args) => {
|
|
1226
|
+
const keys = Array.isArray(args.keys) ? args.keys.map(String) : [];
|
|
1227
|
+
return keys.length ? `press ${keys.join("+")}` : "press key";
|
|
1228
|
+
},
|
|
1229
|
+
browser_scroll: (args) => {
|
|
1230
|
+
const x = asNumber(args.x);
|
|
1231
|
+
const y = asNumber(args.y);
|
|
1232
|
+
const dx = asNumber(args.delta_x) ?? 0;
|
|
1233
|
+
const dy = asNumber(args.delta_y) ?? 0;
|
|
1234
|
+
const delta = dx || dy ? `\u0394(${dx}, ${dy})` : "";
|
|
1235
|
+
const origin = x !== null && y !== null ? `at (${x}, ${y})` : "";
|
|
1236
|
+
return ["scroll", delta, origin].filter(Boolean).join(" ");
|
|
1237
|
+
},
|
|
1238
|
+
browser_drag: (args) => {
|
|
1239
|
+
const path = Array.isArray(args.path) ? args.path : [];
|
|
1240
|
+
if (path.length < 2) return "drag";
|
|
1241
|
+
const first = path[0];
|
|
1242
|
+
const last = path[path.length - 1];
|
|
1243
|
+
const point = (p) => `(${asNumber(p[0]) ?? "?"}, ${asNumber(p[1]) ?? "?"})`;
|
|
1244
|
+
return `drag ${point(first)} \u2192 ${point(last)}`;
|
|
1245
|
+
},
|
|
1246
|
+
browser_wait: (args) => {
|
|
1247
|
+
const ms = asNumber(args.ms);
|
|
1248
|
+
return ms === null ? "wait" : `wait ${ms}ms`;
|
|
1249
|
+
},
|
|
1250
|
+
browser_screenshot: (args) => args.annotate === true ? "screenshot (annotated)" : "screenshot"
|
|
1251
|
+
};
|
|
1252
|
+
function truncateText(text, max = 32) {
|
|
1253
|
+
return text.length > max ? `${text.slice(0, max - 1)}\u2026` : text;
|
|
1254
|
+
}
|
|
1255
|
+
function asNumber(value) {
|
|
1256
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1138
1257
|
}
|
|
1139
1258
|
function parseJson(raw) {
|
|
1140
1259
|
if (!raw) return {};
|
|
@@ -2193,6 +2312,20 @@ function WebToolBlock({ tc }) {
|
|
|
2193
2312
|
displayText && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/70 truncate text-xs", children: displayText })
|
|
2194
2313
|
] });
|
|
2195
2314
|
}
|
|
2315
|
+
function VisionAnalyzeBlock({ tc }) {
|
|
2316
|
+
let image = "";
|
|
2317
|
+
try {
|
|
2318
|
+
const args = JSON.parse(tc.args);
|
|
2319
|
+
const ref = String(args.image ?? args.image_url ?? args.image_path ?? "");
|
|
2320
|
+
image = ref.split("/").pop() ?? ref;
|
|
2321
|
+
} catch {
|
|
2322
|
+
}
|
|
2323
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-sm py-0.5", children: [
|
|
2324
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.EyeIcon, { className: "size-3.5 text-muted-foreground/60 shrink-0" }),
|
|
2325
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: "Vision Analyze" }),
|
|
2326
|
+
image && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/70 truncate text-xs", children: image })
|
|
2327
|
+
] });
|
|
2328
|
+
}
|
|
2196
2329
|
init_utils();
|
|
2197
2330
|
|
|
2198
2331
|
// src/components/chat/diff-viewer.tsx
|
|
@@ -4159,6 +4292,8 @@ function ToolCallBlock({
|
|
|
4159
4292
|
case "web_search":
|
|
4160
4293
|
case "web_crawl":
|
|
4161
4294
|
return /* @__PURE__ */ jsxRuntime.jsx(WebToolBlock, { tc });
|
|
4295
|
+
case "vision_analyze":
|
|
4296
|
+
return /* @__PURE__ */ jsxRuntime.jsx(VisionAnalyzeBlock, { tc });
|
|
4162
4297
|
case "read_file":
|
|
4163
4298
|
return /* @__PURE__ */ jsxRuntime.jsx(ReadFileBlock, { tc, onFileSelect });
|
|
4164
4299
|
case "write_file":
|
|
@@ -5679,18 +5814,21 @@ function ChatComposerInner({
|
|
|
5679
5814
|
[menuOpen, filteredCommands, selectedIndex, handleCommandSelect, textInput]
|
|
5680
5815
|
);
|
|
5681
5816
|
const handleSubmit = react.useCallback(
|
|
5682
|
-
(message) => {
|
|
5817
|
+
async (message) => {
|
|
5683
5818
|
const text = message.text.trim();
|
|
5684
|
-
if (!text ||
|
|
5819
|
+
if (!text || disabled) return;
|
|
5685
5820
|
const images = [];
|
|
5686
5821
|
for (const file of message.files) {
|
|
5687
5822
|
if (file.mediaType?.startsWith("image/") && file.url) {
|
|
5688
5823
|
images.push({ data: file.url, mimeType: file.mediaType });
|
|
5689
5824
|
}
|
|
5690
5825
|
}
|
|
5691
|
-
|
|
5826
|
+
if (isRunning) {
|
|
5827
|
+
await onStop();
|
|
5828
|
+
}
|
|
5829
|
+
await onSend(text, images.length > 0 ? images : void 0);
|
|
5692
5830
|
},
|
|
5693
|
-
[onSend, isRunning, disabled]
|
|
5831
|
+
[onSend, onStop, isRunning, disabled]
|
|
5694
5832
|
);
|
|
5695
5833
|
return /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: menuOpen, onOpenChange: (open) => {
|
|
5696
5834
|
if (!open) {
|
|
@@ -7551,10 +7689,9 @@ function WorkspacePanel({
|
|
|
7551
7689
|
sessionId,
|
|
7552
7690
|
selectedPath,
|
|
7553
7691
|
onSelectedPathChange,
|
|
7554
|
-
collapsed = false,
|
|
7555
|
-
onCollapsedChange,
|
|
7556
7692
|
refreshSignal = 0,
|
|
7557
|
-
disabled = false
|
|
7693
|
+
disabled = false,
|
|
7694
|
+
fillParent = false
|
|
7558
7695
|
}) {
|
|
7559
7696
|
const fileInputRef = react.useRef(null);
|
|
7560
7697
|
const [entries, setEntries] = react.useState([]);
|
|
@@ -7755,34 +7892,17 @@ function WorkspacePanel({
|
|
|
7755
7892
|
window.removeEventListener("mouseup", onMouseUp);
|
|
7756
7893
|
};
|
|
7757
7894
|
}, []);
|
|
7758
|
-
if (collapsed) {
|
|
7759
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7760
|
-
"aside",
|
|
7761
|
-
{
|
|
7762
|
-
"data-testid": "workspace-panel",
|
|
7763
|
-
role: "button",
|
|
7764
|
-
tabIndex: 0,
|
|
7765
|
-
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",
|
|
7766
|
-
"aria-label": "Expand workspace",
|
|
7767
|
-
onClick: () => onCollapsedChange?.(false),
|
|
7768
|
-
onKeyDown: (event) => {
|
|
7769
|
-
if (event.key === "Enter" || event.key === " ") {
|
|
7770
|
-
event.preventDefault();
|
|
7771
|
-
onCollapsedChange?.(false);
|
|
7772
|
-
}
|
|
7773
|
-
},
|
|
7774
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FolderOpenIcon, { className: "size-4 text-amber-500" })
|
|
7775
|
-
}
|
|
7776
|
-
);
|
|
7777
|
-
}
|
|
7778
7895
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7779
7896
|
"aside",
|
|
7780
7897
|
{
|
|
7781
7898
|
"data-testid": "workspace-panel",
|
|
7782
|
-
className:
|
|
7783
|
-
|
|
7899
|
+
className: cn(
|
|
7900
|
+
"relative z-10 flex h-full min-h-0 flex-col overflow-hidden border-l border-muted-foreground/20 bg-card",
|
|
7901
|
+
fillParent && "w-full"
|
|
7902
|
+
),
|
|
7903
|
+
style: fillParent ? void 0 : { width, minWidth: MIN_WIDTH, maxWidth: MAX_WIDTH },
|
|
7784
7904
|
children: [
|
|
7785
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7905
|
+
!fillParent && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7786
7906
|
"div",
|
|
7787
7907
|
{
|
|
7788
7908
|
className: "absolute inset-y-0 left-0 z-20 w-1.5 cursor-col-resize transition-colors hover:bg-primary/20 active:bg-primary/30",
|
|
@@ -7808,19 +7928,6 @@ function WorkspacePanel({
|
|
|
7808
7928
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-sm font-medium text-foreground", children: rootName }),
|
|
7809
7929
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-xs text-faint", children: disabled ? "Workspace - Read-only" : "Workspace" })
|
|
7810
7930
|
] }),
|
|
7811
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
7812
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
7813
|
-
Button,
|
|
7814
|
-
{
|
|
7815
|
-
variant: "ghost",
|
|
7816
|
-
size: "icon-sm",
|
|
7817
|
-
onClick: () => onCollapsedChange?.(true),
|
|
7818
|
-
"aria-label": "Collapse workspace",
|
|
7819
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRightIcon, { className: "size-4" })
|
|
7820
|
-
}
|
|
7821
|
-
) }),
|
|
7822
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "bottom", children: "Collapse" })
|
|
7823
|
-
] }),
|
|
7824
7931
|
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
7825
7932
|
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
7826
7933
|
Button,
|
|
@@ -7993,7 +8100,8 @@ var WORKSPACE_MUTATING_TOOLS = /* @__PURE__ */ new Set([
|
|
|
7993
8100
|
"terminal",
|
|
7994
8101
|
"write_file",
|
|
7995
8102
|
"patch",
|
|
7996
|
-
"execute_code"
|
|
8103
|
+
"execute_code",
|
|
8104
|
+
"browser_screenshot"
|
|
7997
8105
|
]);
|
|
7998
8106
|
var AGENT_CHAT_LISTENED_EVENTS = [
|
|
7999
8107
|
"user.message",
|
|
@@ -8919,7 +9027,6 @@ function AgentChat({
|
|
|
8919
9027
|
disabled
|
|
8920
9028
|
}) {
|
|
8921
9029
|
const [workspacePath, setWorkspacePath] = react.useState(null);
|
|
8922
|
-
const [workspaceCollapsed, setWorkspaceCollapsed] = react.useState(false);
|
|
8923
9030
|
const runtime = useAgentChatRuntime({
|
|
8924
9031
|
adapter,
|
|
8925
9032
|
agentId,
|
|
@@ -8930,6 +9037,7 @@ function AgentChat({
|
|
|
8930
9037
|
const effectiveDisabled = disabled || isReadOnlySession;
|
|
8931
9038
|
const disabledReason = isReadOnlySession ? "Scheduled run is read-only" : void 0;
|
|
8932
9039
|
const browserState = runtime.state.browser;
|
|
9040
|
+
const hasBrowserPanel = browserState !== null && sessionId;
|
|
8933
9041
|
react.useEffect(() => {
|
|
8934
9042
|
onMessagesChange?.(runtime.messages);
|
|
8935
9043
|
}, [onMessagesChange, runtime.messages]);
|
|
@@ -8948,53 +9056,769 @@ function AgentChat({
|
|
|
8948
9056
|
sessionId,
|
|
8949
9057
|
onFileSelect: handleFileSelect
|
|
8950
9058
|
},
|
|
8951
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8952
|
-
|
|
8953
|
-
|
|
9059
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9060
|
+
"section",
|
|
9061
|
+
{
|
|
9062
|
+
"data-testid": "agent-chat-layout",
|
|
9063
|
+
className: hasBrowserPanel ? "relative min-h-0 flex-1 overflow-hidden bg-background text-sm text-foreground" : "flex min-h-0 flex-1 overflow-hidden bg-background text-sm text-foreground",
|
|
9064
|
+
style: { direction: "ltr" },
|
|
9065
|
+
children: [
|
|
9066
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9067
|
+
"div",
|
|
9068
|
+
{
|
|
9069
|
+
"data-testid": "chat-panel",
|
|
9070
|
+
className: hasBrowserPanel ? "absolute inset-y-0 left-0 flex min-h-0 min-w-0 flex-col overflow-hidden" : "flex min-w-0 flex-1 flex-col overflow-hidden",
|
|
9071
|
+
style: hasBrowserPanel ? { right: 440 } : void 0,
|
|
9072
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
9073
|
+
ChatThread,
|
|
9074
|
+
{
|
|
9075
|
+
sessionId,
|
|
9076
|
+
messages: runtime.messages,
|
|
9077
|
+
isRunning: runtime.isRunning,
|
|
9078
|
+
isLoadingHistory: runtime.isLoadingHistory,
|
|
9079
|
+
onSend: (content, images) => runtime.send(content, images),
|
|
9080
|
+
onStop: () => runtime.stop(),
|
|
9081
|
+
onRetry: runtime.retry,
|
|
9082
|
+
onFileSelect: handleFileSelect,
|
|
9083
|
+
disabled: effectiveDisabled,
|
|
9084
|
+
disabledReason,
|
|
9085
|
+
tokenUsage: runtime.tokenUsage,
|
|
9086
|
+
retryIndicator: runtime.retryIndicator
|
|
9087
|
+
}
|
|
9088
|
+
)
|
|
9089
|
+
}
|
|
9090
|
+
),
|
|
9091
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
9092
|
+
"div",
|
|
9093
|
+
{
|
|
9094
|
+
"data-testid": "right-stack",
|
|
9095
|
+
className: hasBrowserPanel ? "absolute inset-y-0 flex min-h-0 flex-col overflow-hidden" : "flex min-h-0 shrink-0 flex-col",
|
|
9096
|
+
style: hasBrowserPanel ? { right: 0, width: 440 } : void 0,
|
|
9097
|
+
children: [
|
|
9098
|
+
hasBrowserPanel && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9099
|
+
"div",
|
|
9100
|
+
{
|
|
9101
|
+
"data-testid": "browser-panel",
|
|
9102
|
+
className: "h-1/2 min-h-0 w-full overflow-hidden border-b border-line",
|
|
9103
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
9104
|
+
BrowserPane,
|
|
9105
|
+
{
|
|
9106
|
+
sessionId,
|
|
9107
|
+
state: browserState,
|
|
9108
|
+
adapter
|
|
9109
|
+
}
|
|
9110
|
+
)
|
|
9111
|
+
}
|
|
9112
|
+
),
|
|
9113
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9114
|
+
"div",
|
|
9115
|
+
{
|
|
9116
|
+
"data-testid": "workspace-panel-frame",
|
|
9117
|
+
className: hasBrowserPanel ? "h-1/2 min-h-0 w-full overflow-hidden" : "min-h-0",
|
|
9118
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
9119
|
+
WorkspacePanel,
|
|
9120
|
+
{
|
|
9121
|
+
adapter,
|
|
9122
|
+
sessionId,
|
|
9123
|
+
selectedPath: workspacePath,
|
|
9124
|
+
onSelectedPathChange: setWorkspacePath,
|
|
9125
|
+
refreshSignal: runtime.workspaceRefreshKey,
|
|
9126
|
+
disabled: effectiveDisabled,
|
|
9127
|
+
fillParent: Boolean(hasBrowserPanel)
|
|
9128
|
+
}
|
|
9129
|
+
)
|
|
9130
|
+
}
|
|
9131
|
+
)
|
|
9132
|
+
]
|
|
9133
|
+
}
|
|
9134
|
+
)
|
|
9135
|
+
]
|
|
9136
|
+
}
|
|
9137
|
+
) })
|
|
9138
|
+
}
|
|
9139
|
+
);
|
|
9140
|
+
}
|
|
9141
|
+
init_utils();
|
|
9142
|
+
var DEFAULT_LIMIT = 50;
|
|
9143
|
+
function requireInboxAdapter(adapter) {
|
|
9144
|
+
if (!adapter.listInbox || !adapter.getInboxItem || !adapter.markInboxItemRead || !adapter.acknowledgeInboxItem || !adapter.respondGovernanceInboxItem || !adapter.openInboxStream) {
|
|
9145
|
+
throw new Error("Inbox is not supported by this adapter.");
|
|
9146
|
+
}
|
|
9147
|
+
return adapter;
|
|
9148
|
+
}
|
|
9149
|
+
var KIND_LABEL2 = {
|
|
9150
|
+
input_required: "Input needed",
|
|
9151
|
+
action_required: "Action needed",
|
|
9152
|
+
task_complete: "Task complete",
|
|
9153
|
+
governance_gate: "Approval",
|
|
9154
|
+
progress_checkin: "Progress"
|
|
9155
|
+
};
|
|
9156
|
+
function kindLabel(kind) {
|
|
9157
|
+
return KIND_LABEL2[kind] ?? kind.replace(/[_-]+/g, " ");
|
|
9158
|
+
}
|
|
9159
|
+
function statusLabel(status) {
|
|
9160
|
+
return status.split(/[_-]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
9161
|
+
}
|
|
9162
|
+
function formatRelative(value) {
|
|
9163
|
+
const date = new Date(value);
|
|
9164
|
+
if (Number.isNaN(date.getTime())) return "";
|
|
9165
|
+
return dateFns.formatDistanceToNow(date, { addSuffix: true });
|
|
9166
|
+
}
|
|
9167
|
+
function kindIcon(kind) {
|
|
9168
|
+
if (kind === "input_required") return lucideReact.MessageSquareIcon;
|
|
9169
|
+
if (kind === "action_required") return lucideReact.ExternalLinkIcon;
|
|
9170
|
+
if (kind === "task_complete") return lucideReact.ClipboardCheckIcon;
|
|
9171
|
+
if (kind === "governance_gate") return lucideReact.ShieldAlertIcon;
|
|
9172
|
+
if (kind === "progress_checkin") return lucideReact.TimerIcon;
|
|
9173
|
+
return lucideReact.CircleDotIcon;
|
|
9174
|
+
}
|
|
9175
|
+
function sortItems(items) {
|
|
9176
|
+
return [...items].sort((a, b) => {
|
|
9177
|
+
if (a.createdAt !== b.createdAt) return a.createdAt > b.createdAt ? -1 : 1;
|
|
9178
|
+
return b.id - a.id;
|
|
9179
|
+
});
|
|
9180
|
+
}
|
|
9181
|
+
function QuestionInput({
|
|
9182
|
+
prompt,
|
|
9183
|
+
choices,
|
|
9184
|
+
disabled,
|
|
9185
|
+
value,
|
|
9186
|
+
onChange
|
|
9187
|
+
}) {
|
|
9188
|
+
if (choices && choices.length > 0) {
|
|
9189
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9190
|
+
"select",
|
|
9191
|
+
{
|
|
9192
|
+
"aria-label": prompt,
|
|
9193
|
+
className: "h-9 w-full border border-line bg-background px-2 text-sm text-foreground outline-none focus:border-primary disabled:opacity-50",
|
|
9194
|
+
disabled,
|
|
9195
|
+
value,
|
|
9196
|
+
onChange: (event) => onChange(event.target.value),
|
|
9197
|
+
children: [
|
|
9198
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "Select" }),
|
|
9199
|
+
choices.map((choice) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: choice.label, children: choice.label }, choice.label))
|
|
9200
|
+
]
|
|
9201
|
+
}
|
|
9202
|
+
);
|
|
9203
|
+
}
|
|
9204
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9205
|
+
"input",
|
|
9206
|
+
{
|
|
9207
|
+
"aria-label": prompt,
|
|
9208
|
+
className: "h-9 w-full border border-line bg-background px-2 text-sm text-foreground outline-none focus:border-primary disabled:opacity-50",
|
|
9209
|
+
disabled,
|
|
9210
|
+
value,
|
|
9211
|
+
onChange: (event) => onChange(event.target.value)
|
|
9212
|
+
}
|
|
9213
|
+
);
|
|
9214
|
+
}
|
|
9215
|
+
function InboxDetailActions({
|
|
9216
|
+
item,
|
|
9217
|
+
adapter,
|
|
9218
|
+
onDeleted,
|
|
9219
|
+
onSessionSelect,
|
|
9220
|
+
children
|
|
9221
|
+
}) {
|
|
9222
|
+
const [deleting, setDeleting] = react.useState(false);
|
|
9223
|
+
async function deleteItem() {
|
|
9224
|
+
if (!adapter.deleteInboxItem) return;
|
|
9225
|
+
setDeleting(true);
|
|
9226
|
+
try {
|
|
9227
|
+
await onDeleted(item.id);
|
|
9228
|
+
} finally {
|
|
9229
|
+
setDeleting(false);
|
|
9230
|
+
}
|
|
9231
|
+
}
|
|
9232
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-2", children: [
|
|
9233
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
9234
|
+
Button,
|
|
9235
|
+
{
|
|
9236
|
+
type: "button",
|
|
9237
|
+
size: "sm",
|
|
9238
|
+
onClick: () => onSessionSelect?.(item.sessionId),
|
|
9239
|
+
"aria-label": "Open session",
|
|
9240
|
+
children: [
|
|
9241
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLinkIcon, { className: "size-3.5" }),
|
|
9242
|
+
"Open session"
|
|
9243
|
+
]
|
|
9244
|
+
}
|
|
9245
|
+
),
|
|
9246
|
+
children,
|
|
9247
|
+
adapter.deleteInboxItem && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9248
|
+
Button,
|
|
9249
|
+
{
|
|
9250
|
+
type: "button",
|
|
9251
|
+
size: "sm",
|
|
9252
|
+
variant: "ghost",
|
|
9253
|
+
onClick: () => void deleteItem(),
|
|
9254
|
+
disabled: deleting,
|
|
9255
|
+
"aria-label": "Delete inbox item",
|
|
9256
|
+
title: "Delete inbox item",
|
|
9257
|
+
children: [
|
|
9258
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2Icon, { className: "size-3.5" }),
|
|
9259
|
+
deleting ? "Deleting" : "Delete"
|
|
9260
|
+
]
|
|
9261
|
+
}
|
|
9262
|
+
)
|
|
9263
|
+
] });
|
|
9264
|
+
}
|
|
9265
|
+
function InputRequiredDetail({
|
|
9266
|
+
item,
|
|
9267
|
+
adapter,
|
|
9268
|
+
onUpdated,
|
|
9269
|
+
onDeleted,
|
|
9270
|
+
onSessionSelect
|
|
9271
|
+
}) {
|
|
9272
|
+
const questions = Array.isArray(item.payload.questions) ? item.payload.questions.map((question) => ({
|
|
9273
|
+
prompt: typeof question.prompt === "string" ? question.prompt : "",
|
|
9274
|
+
choices: Array.isArray(question.choices) ? question.choices.map((choice) => ({
|
|
9275
|
+
label: typeof choice.label === "string" ? choice.label : "",
|
|
9276
|
+
description: typeof choice.description === "string" ? choice.description : void 0
|
|
9277
|
+
})).filter((choice) => choice.label) : void 0
|
|
9278
|
+
})).filter((question) => question.prompt) : [];
|
|
9279
|
+
const [answers, setAnswers] = react.useState({});
|
|
9280
|
+
const [submitting, setSubmitting] = react.useState(false);
|
|
9281
|
+
const disabled = item.status !== "pending" || submitting;
|
|
9282
|
+
const canSubmit = questions.every((question) => answers[question.prompt]?.trim());
|
|
9283
|
+
async function submit() {
|
|
9284
|
+
const toolCallId = typeof item.payload.tool_call_id === "string" ? item.payload.tool_call_id : "";
|
|
9285
|
+
if (!toolCallId || !canSubmit) return;
|
|
9286
|
+
setSubmitting(true);
|
|
9287
|
+
try {
|
|
9288
|
+
const responses = questions.map((question) => ({
|
|
9289
|
+
question: question.prompt,
|
|
9290
|
+
answer: answers[question.prompt]?.trim() ?? "",
|
|
9291
|
+
is_other: false
|
|
9292
|
+
}));
|
|
9293
|
+
await adapter.submitClarifyResponse({
|
|
9294
|
+
sessionId: item.sessionId,
|
|
9295
|
+
toolCallId,
|
|
9296
|
+
responses
|
|
9297
|
+
});
|
|
9298
|
+
onUpdated(await adapter.getInboxItem({ itemId: item.id }));
|
|
9299
|
+
} finally {
|
|
9300
|
+
setSubmitting(false);
|
|
9301
|
+
}
|
|
9302
|
+
}
|
|
9303
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
9304
|
+
item.body && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap text-sm text-muted-foreground", children: item.body }),
|
|
9305
|
+
questions.map((question) => /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block space-y-1.5", children: [
|
|
9306
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-foreground", children: question.prompt }),
|
|
9307
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9308
|
+
QuestionInput,
|
|
9309
|
+
{
|
|
9310
|
+
prompt: question.prompt,
|
|
9311
|
+
choices: question.choices,
|
|
9312
|
+
disabled,
|
|
9313
|
+
value: answers[question.prompt] ?? "",
|
|
9314
|
+
onChange: (value) => setAnswers((current) => ({
|
|
9315
|
+
...current,
|
|
9316
|
+
[question.prompt]: value
|
|
9317
|
+
}))
|
|
9318
|
+
}
|
|
9319
|
+
)
|
|
9320
|
+
] }, question.prompt)),
|
|
9321
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9322
|
+
InboxDetailActions,
|
|
9323
|
+
{
|
|
9324
|
+
item,
|
|
9325
|
+
adapter,
|
|
9326
|
+
onDeleted,
|
|
9327
|
+
onSessionSelect,
|
|
9328
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
9329
|
+
Button,
|
|
8954
9330
|
{
|
|
8955
|
-
|
|
8956
|
-
|
|
8957
|
-
|
|
8958
|
-
|
|
8959
|
-
|
|
8960
|
-
|
|
8961
|
-
|
|
8962
|
-
|
|
8963
|
-
|
|
8964
|
-
|
|
8965
|
-
|
|
8966
|
-
|
|
9331
|
+
type: "button",
|
|
9332
|
+
size: "sm",
|
|
9333
|
+
onClick: () => void submit(),
|
|
9334
|
+
disabled: disabled || !canSubmit,
|
|
9335
|
+
"aria-label": "Submit inbox response",
|
|
9336
|
+
children: submitting ? "Submitting" : "Submit"
|
|
9337
|
+
}
|
|
9338
|
+
)
|
|
9339
|
+
}
|
|
9340
|
+
)
|
|
9341
|
+
] });
|
|
9342
|
+
}
|
|
9343
|
+
function AckDetail({
|
|
9344
|
+
item,
|
|
9345
|
+
adapter,
|
|
9346
|
+
onUpdated,
|
|
9347
|
+
onDeleted,
|
|
9348
|
+
onSessionSelect
|
|
9349
|
+
}) {
|
|
9350
|
+
async function acknowledge() {
|
|
9351
|
+
onUpdated(await adapter.acknowledgeInboxItem({ itemId: item.id }));
|
|
9352
|
+
}
|
|
9353
|
+
const outcome = typeof item.payload.outcome === "string" ? item.payload.outcome : "";
|
|
9354
|
+
const duration = typeof item.payload.duration_seconds === "number" ? item.payload.duration_seconds : null;
|
|
9355
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
9356
|
+
outcome && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "secondary", children: outcome }),
|
|
9357
|
+
item.body && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap text-sm", children: item.body }),
|
|
9358
|
+
typeof item.payload.error === "string" && /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "overflow-x-auto bg-destructive/10 p-3 text-xs text-destructive", children: item.payload.error }),
|
|
9359
|
+
duration !== null && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
9360
|
+
"Duration: ",
|
|
9361
|
+
Math.round(duration / 60),
|
|
9362
|
+
" min (",
|
|
9363
|
+
duration,
|
|
9364
|
+
" s)"
|
|
9365
|
+
] }),
|
|
9366
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9367
|
+
InboxDetailActions,
|
|
9368
|
+
{
|
|
9369
|
+
item,
|
|
9370
|
+
adapter,
|
|
9371
|
+
onDeleted,
|
|
9372
|
+
onSessionSelect,
|
|
9373
|
+
children: item.status === "pending" && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9374
|
+
Button,
|
|
9375
|
+
{
|
|
9376
|
+
type: "button",
|
|
9377
|
+
size: "sm",
|
|
9378
|
+
onClick: () => void acknowledge(),
|
|
9379
|
+
"aria-label": "Acknowledge inbox item",
|
|
9380
|
+
children: [
|
|
9381
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckIcon, { className: "size-3.5" }),
|
|
9382
|
+
"Acknowledge"
|
|
9383
|
+
]
|
|
8967
9384
|
}
|
|
8968
|
-
)
|
|
8969
|
-
|
|
8970
|
-
|
|
8971
|
-
|
|
9385
|
+
)
|
|
9386
|
+
}
|
|
9387
|
+
)
|
|
9388
|
+
] });
|
|
9389
|
+
}
|
|
9390
|
+
function GovernanceDetail({
|
|
9391
|
+
item,
|
|
9392
|
+
adapter,
|
|
9393
|
+
onUpdated,
|
|
9394
|
+
onDeleted,
|
|
9395
|
+
onSessionSelect
|
|
9396
|
+
}) {
|
|
9397
|
+
async function decide(decision) {
|
|
9398
|
+
onUpdated(
|
|
9399
|
+
await adapter.respondGovernanceInboxItem({ itemId: item.id, decision })
|
|
9400
|
+
);
|
|
9401
|
+
}
|
|
9402
|
+
const toolName = typeof item.payload.tool_name === "string" ? item.payload.tool_name : "tool";
|
|
9403
|
+
const args = typeof item.payload.arguments_excerpt === "string" ? item.payload.arguments_excerpt : "";
|
|
9404
|
+
const reason = typeof item.payload.deny_reason === "string" ? item.payload.deny_reason : "";
|
|
9405
|
+
const disabled = item.status !== "pending";
|
|
9406
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
9407
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
9408
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-base font-semibold text-foreground", children: [
|
|
9409
|
+
"Approve ",
|
|
9410
|
+
toolName,
|
|
9411
|
+
"?"
|
|
9412
|
+
] }),
|
|
9413
|
+
reason && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: reason })
|
|
9414
|
+
] }),
|
|
9415
|
+
args && /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "overflow-x-auto border border-line bg-muted p-3 text-xs", children: args }),
|
|
9416
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
9417
|
+
InboxDetailActions,
|
|
9418
|
+
{
|
|
9419
|
+
item,
|
|
9420
|
+
adapter,
|
|
9421
|
+
onDeleted,
|
|
9422
|
+
onSessionSelect,
|
|
9423
|
+
children: [
|
|
9424
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9425
|
+
Button,
|
|
8972
9426
|
{
|
|
8973
|
-
|
|
8974
|
-
|
|
8975
|
-
|
|
9427
|
+
type: "button",
|
|
9428
|
+
size: "sm",
|
|
9429
|
+
disabled,
|
|
9430
|
+
onClick: () => void decide("approve"),
|
|
9431
|
+
children: "Approve"
|
|
8976
9432
|
}
|
|
8977
|
-
)
|
|
9433
|
+
),
|
|
8978
9434
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8979
|
-
|
|
9435
|
+
Button,
|
|
8980
9436
|
{
|
|
8981
|
-
|
|
8982
|
-
|
|
8983
|
-
|
|
8984
|
-
|
|
8985
|
-
|
|
8986
|
-
|
|
8987
|
-
refreshSignal: runtime.workspaceRefreshKey,
|
|
8988
|
-
disabled: effectiveDisabled
|
|
9437
|
+
type: "button",
|
|
9438
|
+
size: "sm",
|
|
9439
|
+
variant: "destructive",
|
|
9440
|
+
disabled,
|
|
9441
|
+
onClick: () => void decide("reject"),
|
|
9442
|
+
children: "Reject"
|
|
8989
9443
|
}
|
|
8990
9444
|
)
|
|
8991
|
-
]
|
|
8992
|
-
|
|
9445
|
+
]
|
|
9446
|
+
}
|
|
9447
|
+
)
|
|
9448
|
+
] });
|
|
9449
|
+
}
|
|
9450
|
+
function ActionRequiredDetail({
|
|
9451
|
+
item,
|
|
9452
|
+
adapter,
|
|
9453
|
+
onUpdated,
|
|
9454
|
+
onDeleted,
|
|
9455
|
+
onSessionSelect
|
|
9456
|
+
}) {
|
|
9457
|
+
const [submitting, setSubmitting] = react.useState(false);
|
|
9458
|
+
const actionType = typeof item.payload.action_type === "string" ? item.payload.action_type : "";
|
|
9459
|
+
const context = typeof item.payload.context === "string" ? item.payload.context : "";
|
|
9460
|
+
const disabled = item.status !== "pending" || submitting || !adapter.respondActionRequiredInboxItem;
|
|
9461
|
+
async function complete() {
|
|
9462
|
+
if (!adapter.respondActionRequiredInboxItem) return;
|
|
9463
|
+
setSubmitting(true);
|
|
9464
|
+
try {
|
|
9465
|
+
onUpdated(await adapter.respondActionRequiredInboxItem({ itemId: item.id }));
|
|
9466
|
+
} finally {
|
|
9467
|
+
setSubmitting(false);
|
|
8993
9468
|
}
|
|
9469
|
+
}
|
|
9470
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
9471
|
+
item.body && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap text-sm", children: item.body }),
|
|
9472
|
+
context && context !== item.body && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap text-sm text-muted-foreground", children: context }),
|
|
9473
|
+
actionType && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "secondary", children: actionType }),
|
|
9474
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9475
|
+
InboxDetailActions,
|
|
9476
|
+
{
|
|
9477
|
+
item,
|
|
9478
|
+
adapter,
|
|
9479
|
+
onDeleted,
|
|
9480
|
+
onSessionSelect,
|
|
9481
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9482
|
+
Button,
|
|
9483
|
+
{
|
|
9484
|
+
type: "button",
|
|
9485
|
+
size: "sm",
|
|
9486
|
+
onClick: () => void complete(),
|
|
9487
|
+
disabled,
|
|
9488
|
+
"aria-label": "Mark action complete",
|
|
9489
|
+
children: [
|
|
9490
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckIcon, { className: "size-3.5" }),
|
|
9491
|
+
submitting ? "Marking" : "I completed this"
|
|
9492
|
+
]
|
|
9493
|
+
}
|
|
9494
|
+
)
|
|
9495
|
+
}
|
|
9496
|
+
)
|
|
9497
|
+
] });
|
|
9498
|
+
}
|
|
9499
|
+
function ProgressDetail({
|
|
9500
|
+
item,
|
|
9501
|
+
adapter,
|
|
9502
|
+
onUpdated,
|
|
9503
|
+
onDeleted,
|
|
9504
|
+
onSessionSelect
|
|
9505
|
+
}) {
|
|
9506
|
+
async function acknowledge() {
|
|
9507
|
+
onUpdated(await adapter.acknowledgeInboxItem({ itemId: item.id }));
|
|
9508
|
+
}
|
|
9509
|
+
const rows = [];
|
|
9510
|
+
for (const row of [
|
|
9511
|
+
["Iterations", item.payload.iterations],
|
|
9512
|
+
["Last tool", item.payload.last_tool],
|
|
9513
|
+
["Elapsed", item.payload.elapsed_seconds]
|
|
9514
|
+
]) {
|
|
9515
|
+
if (row[1] !== void 0 && row[1] !== null && row[1] !== "") {
|
|
9516
|
+
rows.push(row);
|
|
9517
|
+
}
|
|
9518
|
+
}
|
|
9519
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
9520
|
+
item.body && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap text-sm", children: item.body }),
|
|
9521
|
+
rows.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("dl", { className: "space-y-1 text-xs text-muted-foreground", children: rows.map(([label, value]) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
|
|
9522
|
+
/* @__PURE__ */ jsxRuntime.jsx("dt", { className: "w-20 shrink-0", children: String(label) }),
|
|
9523
|
+
/* @__PURE__ */ jsxRuntime.jsx("dd", { className: "min-w-0", children: String(value) })
|
|
9524
|
+
] }, String(label))) }),
|
|
9525
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9526
|
+
InboxDetailActions,
|
|
9527
|
+
{
|
|
9528
|
+
item,
|
|
9529
|
+
adapter,
|
|
9530
|
+
onDeleted,
|
|
9531
|
+
onSessionSelect,
|
|
9532
|
+
children: item.status === "pending" && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9533
|
+
Button,
|
|
9534
|
+
{
|
|
9535
|
+
type: "button",
|
|
9536
|
+
size: "sm",
|
|
9537
|
+
onClick: () => void acknowledge(),
|
|
9538
|
+
"aria-label": "Acknowledge inbox item",
|
|
9539
|
+
children: [
|
|
9540
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckIcon, { className: "size-3.5" }),
|
|
9541
|
+
"Acknowledge"
|
|
9542
|
+
]
|
|
9543
|
+
}
|
|
9544
|
+
)
|
|
9545
|
+
}
|
|
9546
|
+
)
|
|
9547
|
+
] });
|
|
9548
|
+
}
|
|
9549
|
+
function InboxDetail({
|
|
9550
|
+
item,
|
|
9551
|
+
adapter,
|
|
9552
|
+
onUpdated,
|
|
9553
|
+
onDeleted,
|
|
9554
|
+
onSessionSelect
|
|
9555
|
+
}) {
|
|
9556
|
+
const Icon = kindIcon(item.kind);
|
|
9557
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "min-w-0 flex-1 overflow-y-auto p-6", children: [
|
|
9558
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-5 flex min-w-0 items-start gap-3", children: [
|
|
9559
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-0.5 flex size-8 shrink-0 items-center justify-center border border-line text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "size-4" }) }),
|
|
9560
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
9561
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
9562
|
+
/* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "secondary", children: kindLabel(item.kind) }),
|
|
9563
|
+
/* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: item.status === "pending" ? "default" : "secondary", children: statusLabel(item.status) })
|
|
9564
|
+
] }),
|
|
9565
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "mt-2 text-lg font-semibold leading-snug text-foreground", children: item.title })
|
|
9566
|
+
] })
|
|
9567
|
+
] }),
|
|
9568
|
+
item.kind === "input_required" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
9569
|
+
InputRequiredDetail,
|
|
9570
|
+
{
|
|
9571
|
+
item,
|
|
9572
|
+
adapter,
|
|
9573
|
+
onUpdated,
|
|
9574
|
+
onDeleted,
|
|
9575
|
+
onSessionSelect
|
|
9576
|
+
}
|
|
9577
|
+
) : item.kind === "action_required" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
9578
|
+
ActionRequiredDetail,
|
|
9579
|
+
{
|
|
9580
|
+
item,
|
|
9581
|
+
adapter,
|
|
9582
|
+
onUpdated,
|
|
9583
|
+
onDeleted,
|
|
9584
|
+
onSessionSelect
|
|
9585
|
+
}
|
|
9586
|
+
) : item.kind === "governance_gate" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
9587
|
+
GovernanceDetail,
|
|
9588
|
+
{
|
|
9589
|
+
item,
|
|
9590
|
+
adapter,
|
|
9591
|
+
onUpdated,
|
|
9592
|
+
onDeleted,
|
|
9593
|
+
onSessionSelect
|
|
9594
|
+
}
|
|
9595
|
+
) : item.kind === "progress_checkin" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
9596
|
+
ProgressDetail,
|
|
9597
|
+
{
|
|
9598
|
+
item,
|
|
9599
|
+
adapter,
|
|
9600
|
+
onUpdated,
|
|
9601
|
+
onDeleted,
|
|
9602
|
+
onSessionSelect
|
|
9603
|
+
}
|
|
9604
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
9605
|
+
AckDetail,
|
|
9606
|
+
{
|
|
9607
|
+
item,
|
|
9608
|
+
adapter,
|
|
9609
|
+
onUpdated,
|
|
9610
|
+
onDeleted,
|
|
9611
|
+
onSessionSelect
|
|
9612
|
+
}
|
|
9613
|
+
)
|
|
9614
|
+
] });
|
|
9615
|
+
}
|
|
9616
|
+
function InboxPanel({
|
|
9617
|
+
adapter,
|
|
9618
|
+
title = "Inbox",
|
|
9619
|
+
selectedId,
|
|
9620
|
+
onSelectedIdChange,
|
|
9621
|
+
onSessionSelect,
|
|
9622
|
+
hideHeader = false,
|
|
9623
|
+
limit = DEFAULT_LIMIT
|
|
9624
|
+
}) {
|
|
9625
|
+
const inboxAdapter = react.useMemo(() => requireInboxAdapter(adapter), [adapter]);
|
|
9626
|
+
const [items, setItems] = react.useState([]);
|
|
9627
|
+
const [nextCursor, setNextCursor] = react.useState(null);
|
|
9628
|
+
const [internalSelectedId, setInternalSelectedId] = react.useState(null);
|
|
9629
|
+
const [loading, setLoading] = react.useState(false);
|
|
9630
|
+
const [error, setError] = react.useState(null);
|
|
9631
|
+
const requestId = react.useRef(0);
|
|
9632
|
+
const selectedItemId = selectedId ?? internalSelectedId;
|
|
9633
|
+
const selectedItem = react.useMemo(
|
|
9634
|
+
() => items.find((item) => item.id === selectedItemId) ?? null,
|
|
9635
|
+
[items, selectedItemId]
|
|
9636
|
+
);
|
|
9637
|
+
const applyItem = react.useCallback((nextItem) => {
|
|
9638
|
+
setItems(
|
|
9639
|
+
(current) => sortItems(
|
|
9640
|
+
current.map((item) => item.id === nextItem.id ? nextItem : item)
|
|
9641
|
+
)
|
|
9642
|
+
);
|
|
9643
|
+
}, []);
|
|
9644
|
+
const selectItem = react.useCallback(
|
|
9645
|
+
async (itemId) => {
|
|
9646
|
+
if (selectedId === void 0) setInternalSelectedId(itemId);
|
|
9647
|
+
onSelectedIdChange?.(itemId);
|
|
9648
|
+
const item = await inboxAdapter.getInboxItem({ itemId });
|
|
9649
|
+
applyItem(item);
|
|
9650
|
+
if (!item.readAt) {
|
|
9651
|
+
applyItem(await inboxAdapter.markInboxItemRead({ itemId }));
|
|
9652
|
+
}
|
|
9653
|
+
},
|
|
9654
|
+
[applyItem, inboxAdapter, onSelectedIdChange, selectedId]
|
|
9655
|
+
);
|
|
9656
|
+
const load = react.useCallback(
|
|
9657
|
+
async (cursor) => {
|
|
9658
|
+
const id = ++requestId.current;
|
|
9659
|
+
setLoading(true);
|
|
9660
|
+
setError(null);
|
|
9661
|
+
try {
|
|
9662
|
+
const response = await inboxAdapter.listInbox({
|
|
9663
|
+
cursor: cursor ?? void 0,
|
|
9664
|
+
limit
|
|
9665
|
+
});
|
|
9666
|
+
if (id !== requestId.current) return;
|
|
9667
|
+
setItems(
|
|
9668
|
+
(current) => sortItems(cursor ? [...current, ...response.items] : response.items)
|
|
9669
|
+
);
|
|
9670
|
+
setNextCursor(response.nextCursor);
|
|
9671
|
+
} catch (err) {
|
|
9672
|
+
if (id === requestId.current) {
|
|
9673
|
+
setError(err instanceof Error ? err.message : "Failed to load inbox");
|
|
9674
|
+
}
|
|
9675
|
+
} finally {
|
|
9676
|
+
if (id === requestId.current) setLoading(false);
|
|
9677
|
+
}
|
|
9678
|
+
},
|
|
9679
|
+
[inboxAdapter, limit]
|
|
8994
9680
|
);
|
|
9681
|
+
react.useEffect(() => {
|
|
9682
|
+
void load(null);
|
|
9683
|
+
}, [load]);
|
|
9684
|
+
react.useEffect(() => {
|
|
9685
|
+
const stream = inboxAdapter.openInboxStream();
|
|
9686
|
+
stream.addEventListener("item", (event) => {
|
|
9687
|
+
const payload = JSON.parse(event.data);
|
|
9688
|
+
if (typeof payload.item_id !== "number") return;
|
|
9689
|
+
void inboxAdapter.getInboxItem({ itemId: payload.item_id }).then(applyItem);
|
|
9690
|
+
});
|
|
9691
|
+
return () => stream.close();
|
|
9692
|
+
}, [applyItem, inboxAdapter]);
|
|
9693
|
+
function updateSelectedItem(item) {
|
|
9694
|
+
applyItem(item);
|
|
9695
|
+
}
|
|
9696
|
+
const deleteItem = react.useCallback(
|
|
9697
|
+
async (itemId) => {
|
|
9698
|
+
if (!inboxAdapter.deleteInboxItem) return;
|
|
9699
|
+
await inboxAdapter.deleteInboxItem({ itemId });
|
|
9700
|
+
setItems((current) => current.filter((item) => item.id !== itemId));
|
|
9701
|
+
if (selectedItemId === itemId) {
|
|
9702
|
+
if (selectedId === void 0) setInternalSelectedId(null);
|
|
9703
|
+
onSelectedIdChange?.(null);
|
|
9704
|
+
}
|
|
9705
|
+
},
|
|
9706
|
+
[inboxAdapter, onSelectedIdChange, selectedId, selectedItemId]
|
|
9707
|
+
);
|
|
9708
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full min-h-0 min-w-0 bg-background text-foreground", children: [
|
|
9709
|
+
/* @__PURE__ */ jsxRuntime.jsxs("aside", { className: "flex w-80 min-w-72 max-w-sm shrink-0 flex-col border-r border-line", children: [
|
|
9710
|
+
!hideHeader && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-14 items-center gap-2 border-b border-line px-4", children: [
|
|
9711
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.InboxIcon, { className: "size-4 text-muted-foreground" }),
|
|
9712
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "font-semibold", children: title })
|
|
9713
|
+
] }),
|
|
9714
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-h-0 flex-1 overflow-y-auto", children: [
|
|
9715
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-3 text-sm text-destructive", children: error }),
|
|
9716
|
+
!error && items.length === 0 && !loading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-8 text-sm text-muted-foreground", children: "No inbox items" }),
|
|
9717
|
+
items.map((item) => {
|
|
9718
|
+
const Icon = kindIcon(item.kind);
|
|
9719
|
+
const selected = item.id === selectedItemId;
|
|
9720
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9721
|
+
"button",
|
|
9722
|
+
{
|
|
9723
|
+
type: "button",
|
|
9724
|
+
"aria-label": `Open inbox item ${item.title}`,
|
|
9725
|
+
className: cn(
|
|
9726
|
+
"flex w-full min-w-0 gap-3 border-l-2 px-3 py-3 text-left transition-colors hover:bg-input",
|
|
9727
|
+
selected ? "border-l-primary bg-line text-foreground" : "border-l-transparent text-subtle",
|
|
9728
|
+
!item.readAt && "font-medium text-foreground"
|
|
9729
|
+
),
|
|
9730
|
+
onClick: () => void selectItem(item.id),
|
|
9731
|
+
children: [
|
|
9732
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "mt-0.5 size-4 shrink-0 text-muted-foreground" }),
|
|
9733
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
9734
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-center justify-between gap-2", children: [
|
|
9735
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-xs uppercase tracking-wide text-muted-foreground", children: kindLabel(item.kind) }),
|
|
9736
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: formatRelative(item.createdAt) })
|
|
9737
|
+
] }),
|
|
9738
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1 block truncate text-sm", children: item.title }),
|
|
9739
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1 block text-xs text-muted-foreground", children: statusLabel(item.status) })
|
|
9740
|
+
] })
|
|
9741
|
+
]
|
|
9742
|
+
},
|
|
9743
|
+
item.id
|
|
9744
|
+
);
|
|
9745
|
+
}),
|
|
9746
|
+
nextCursor && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9747
|
+
"button",
|
|
9748
|
+
{
|
|
9749
|
+
type: "button",
|
|
9750
|
+
className: "w-full px-3 py-3 text-sm text-muted-foreground hover:bg-input",
|
|
9751
|
+
onClick: () => void load(nextCursor),
|
|
9752
|
+
disabled: loading,
|
|
9753
|
+
children: loading ? "Loading" : "Load more"
|
|
9754
|
+
}
|
|
9755
|
+
)
|
|
9756
|
+
] })
|
|
9757
|
+
] }),
|
|
9758
|
+
selectedItem ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
9759
|
+
InboxDetail,
|
|
9760
|
+
{
|
|
9761
|
+
item: selectedItem,
|
|
9762
|
+
adapter: inboxAdapter,
|
|
9763
|
+
onUpdated: updateSelectedItem,
|
|
9764
|
+
onDeleted: deleteItem,
|
|
9765
|
+
onSessionSelect
|
|
9766
|
+
}
|
|
9767
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("main", { className: "flex min-w-0 flex-1 items-center justify-center px-4 text-sm text-muted-foreground", children: "Select an item" })
|
|
9768
|
+
] });
|
|
9769
|
+
}
|
|
9770
|
+
function useInboxUnreadCount(adapter) {
|
|
9771
|
+
const [unreadCount, setUnreadCount] = react.useState(0);
|
|
9772
|
+
const [hasLoaded, setHasLoaded] = react.useState(false);
|
|
9773
|
+
const [error, setError] = react.useState(null);
|
|
9774
|
+
react.useEffect(() => {
|
|
9775
|
+
let cancelled = false;
|
|
9776
|
+
let stream = null;
|
|
9777
|
+
if (!adapter.listInbox || !adapter.openInboxStream) {
|
|
9778
|
+
setError("Inbox is not supported by this adapter.");
|
|
9779
|
+
setHasLoaded(true);
|
|
9780
|
+
return () => {
|
|
9781
|
+
cancelled = true;
|
|
9782
|
+
};
|
|
9783
|
+
}
|
|
9784
|
+
adapter.listInbox({ status: "pending", limit: 200 }).then((response) => {
|
|
9785
|
+
if (cancelled) return;
|
|
9786
|
+
setUnreadCount(response.items.filter((item) => !item.readAt).length);
|
|
9787
|
+
setHasLoaded(true);
|
|
9788
|
+
}).catch((err) => {
|
|
9789
|
+
if (cancelled) return;
|
|
9790
|
+
setError(err instanceof Error ? err.message : "Failed to load inbox");
|
|
9791
|
+
setHasLoaded(true);
|
|
9792
|
+
});
|
|
9793
|
+
try {
|
|
9794
|
+
stream = adapter.openInboxStream();
|
|
9795
|
+
stream.addEventListener("snapshot", (event) => {
|
|
9796
|
+
if (cancelled) return;
|
|
9797
|
+
const payload = JSON.parse(event.data);
|
|
9798
|
+
const ids = Array.isArray(payload.unread_ids) ? payload.unread_ids : [];
|
|
9799
|
+
setUnreadCount(ids.length);
|
|
9800
|
+
setHasLoaded(true);
|
|
9801
|
+
});
|
|
9802
|
+
stream.addEventListener("item", () => {
|
|
9803
|
+
if (cancelled) return;
|
|
9804
|
+
setUnreadCount((count) => count + 1);
|
|
9805
|
+
setHasLoaded(true);
|
|
9806
|
+
});
|
|
9807
|
+
stream.onerror = () => {
|
|
9808
|
+
if (!cancelled) setError("Inbox stream disconnected.");
|
|
9809
|
+
};
|
|
9810
|
+
} catch (err) {
|
|
9811
|
+
setError(err instanceof Error ? err.message : "Failed to open inbox stream");
|
|
9812
|
+
}
|
|
9813
|
+
return () => {
|
|
9814
|
+
cancelled = true;
|
|
9815
|
+
stream?.close();
|
|
9816
|
+
};
|
|
9817
|
+
}, [adapter]);
|
|
9818
|
+
return { unreadCount, hasLoaded, error };
|
|
8995
9819
|
}
|
|
8996
9820
|
init_utils();
|
|
8997
|
-
var
|
|
9821
|
+
var DEFAULT_LIMIT2 = 50;
|
|
8998
9822
|
var DEFAULT_POLL_INTERVAL_MS = 3e4;
|
|
8999
9823
|
function fingerprint(items) {
|
|
9000
9824
|
return items.map(
|
|
@@ -9009,7 +9833,7 @@ function formatKind(value) {
|
|
|
9009
9833
|
if (!value) return "Scheduled";
|
|
9010
9834
|
return value.split(/[_-]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
9011
9835
|
}
|
|
9012
|
-
function
|
|
9836
|
+
function formatRelative2(value, label) {
|
|
9013
9837
|
if (!value) return null;
|
|
9014
9838
|
const date = new Date(value);
|
|
9015
9839
|
if (Number.isNaN(date.getTime())) return null;
|
|
@@ -9059,10 +9883,10 @@ function ScheduledWorkRow({
|
|
|
9059
9883
|
const lastSessionId = item.lastSessionId ?? null;
|
|
9060
9884
|
const meta = [
|
|
9061
9885
|
scheduleDisplay(item),
|
|
9062
|
-
|
|
9063
|
-
|
|
9886
|
+
formatRelative2(item.nextRunAt, "Next"),
|
|
9887
|
+
formatRelative2(item.lastRunAt, "Last"),
|
|
9064
9888
|
formatRuns(item.runCount),
|
|
9065
|
-
item.expiresAt ?
|
|
9889
|
+
item.expiresAt ? formatRelative2(item.expiresAt, "Expires") : null
|
|
9066
9890
|
].filter(Boolean);
|
|
9067
9891
|
const openLastRun = () => {
|
|
9068
9892
|
if (lastSessionId) onOpenLastRun(lastSessionId);
|
|
@@ -9142,7 +9966,7 @@ function ScheduledWorkPanel({
|
|
|
9142
9966
|
adapter,
|
|
9143
9967
|
agentId,
|
|
9144
9968
|
title = "Scheduled Work",
|
|
9145
|
-
limit =
|
|
9969
|
+
limit = DEFAULT_LIMIT2,
|
|
9146
9970
|
status = "active",
|
|
9147
9971
|
hideHeader = false,
|
|
9148
9972
|
pollIntervalMs = DEFAULT_POLL_INTERVAL_MS,
|
|
@@ -9665,10 +10489,12 @@ function SessionTreePanel({
|
|
|
9665
10489
|
|
|
9666
10490
|
exports.AgentChat = AgentChat;
|
|
9667
10491
|
exports.AgentChatAdapterProvider = AgentChatAdapterProvider;
|
|
10492
|
+
exports.InboxPanel = InboxPanel;
|
|
9668
10493
|
exports.MessageResponse = MessageResponse;
|
|
9669
10494
|
exports.ScheduledWorkPanel = ScheduledWorkPanel;
|
|
9670
10495
|
exports.SessionTreePanel = SessionTreePanel;
|
|
9671
10496
|
exports.useAgentChatAdapterContext = useAgentChatAdapterContext;
|
|
9672
10497
|
exports.useAgentChatRuntime = useAgentChatRuntime;
|
|
10498
|
+
exports.useInboxUnreadCount = useInboxUnreadCount;
|
|
9673
10499
|
//# sourceMappingURL=index.cjs.map
|
|
9674
10500
|
//# sourceMappingURL=index.cjs.map
|