@frontera-sdk/chat-ui 1.50.46 → 1.50.48

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.
@@ -1,8 +1,8 @@
1
1
  'use client';
2
2
  import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from './chunk-2KEBRV3W.js';
3
- import { useTranslation } from './chunk-QSSFBZAS.js';
4
- import { ToolCallItem } from './chunk-OP4MLJDA.js';
5
- import { Shimmer, Collapse, ToolGroupReasoningItem, SkillToolItem, useStreamdownPlugins, getToolMeta, STREAMDOWN_LINK_SAFETY, AppIconFrame, AppIcon, Collapsible, CollapsibleTrigger, CollapsibleContent, Icon } from './chunk-XJFTVKLW.js';
3
+ import { useTranslation } from './chunk-FTLVGUG5.js';
4
+ import { ToolCallItem } from './chunk-76WUIIOX.js';
5
+ import { Shimmer, Collapse, ToolGroupReasoningItem, SkillToolItem, useStreamdownPlugins, getToolMeta, STREAMDOWN_LINK_SAFETY, AppIconFrame, AppIcon, Collapsible, CollapsibleTrigger, CollapsibleContent, Icon } from './chunk-IBCOQG5T.js';
6
6
  import { Button, Skeleton, buttonVariants } from './chunk-GTQH7MPF.js';
7
7
  import { Dialog, DialogContent, DialogTitle, DialogDescription, DialogClose, usePortalContainer, DialogHeader, DialogFooter } from './chunk-IQRPGFLI.js';
8
8
  import { formatSpecifier, precisionFixed_default, precisionRound_default, precisionPrefix_default, formatPrefix, format, ChartBlockSkeleton, RawJsonFallback, RECHARTS_THEME_CLS, formatCompact, ChartTooltipContent, resolveColor, useChartColors, ChartReadout, ChartErrorBoundary } from './chunk-6BPHEBCF.js';
@@ -7924,12 +7924,12 @@ var require_use_sync_external_store_shim_production = __commonJS({
7924
7924
  return x2 === y2 && (0 !== x2 || 1 / x2 === 1 / y2) || x2 !== x2 && y2 !== y2;
7925
7925
  }
7926
7926
  var objectIs = "function" === typeof Object.is ? Object.is : is3;
7927
- var useState49 = React60.useState;
7928
- var useEffect39 = React60.useEffect;
7927
+ var useState48 = React60.useState;
7928
+ var useEffect38 = React60.useEffect;
7929
7929
  var useLayoutEffect10 = React60.useLayoutEffect;
7930
7930
  var useDebugValue2 = React60.useDebugValue;
7931
7931
  function useSyncExternalStore$2(subscribe, getSnapshot) {
7932
- var value = getSnapshot(), _useState = useState49({ inst: { value, getSnapshot } }), inst = _useState[0].inst, forceUpdate = _useState[1];
7932
+ var value = getSnapshot(), _useState = useState48({ inst: { value, getSnapshot } }), inst = _useState[0].inst, forceUpdate = _useState[1];
7933
7933
  useLayoutEffect10(
7934
7934
  function() {
7935
7935
  inst.value = value;
@@ -7938,7 +7938,7 @@ var require_use_sync_external_store_shim_production = __commonJS({
7938
7938
  },
7939
7939
  [subscribe, value, getSnapshot]
7940
7940
  );
7941
- useEffect39(
7941
+ useEffect38(
7942
7942
  function() {
7943
7943
  checkIfSnapshotChanged(inst) && forceUpdate({ inst });
7944
7944
  return subscribe(function() {
@@ -7986,7 +7986,7 @@ var require_use_sync_external_store_shim_development = __commonJS({
7986
7986
  "The result of getSnapshot should be cached to avoid an infinite loop"
7987
7987
  ), didWarnUncachedGetSnapshot = true);
7988
7988
  }
7989
- cachedValue = useState49({
7989
+ cachedValue = useState48({
7990
7990
  inst: { value, getSnapshot }
7991
7991
  });
7992
7992
  var inst = cachedValue[0].inst, forceUpdate = cachedValue[1];
@@ -7998,7 +7998,7 @@ var require_use_sync_external_store_shim_development = __commonJS({
7998
7998
  },
7999
7999
  [subscribe, value, getSnapshot]
8000
8000
  );
8001
- useEffect39(
8001
+ useEffect38(
8002
8002
  function() {
8003
8003
  checkIfSnapshotChanged(inst) && forceUpdate({ inst });
8004
8004
  return subscribe(function() {
@@ -8024,7 +8024,7 @@ var require_use_sync_external_store_shim_development = __commonJS({
8024
8024
  return getSnapshot();
8025
8025
  }
8026
8026
  "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
8027
- var React60 = __require("react"), objectIs = "function" === typeof Object.is ? Object.is : is3, useState49 = React60.useState, useEffect39 = React60.useEffect, useLayoutEffect10 = React60.useLayoutEffect, useDebugValue2 = React60.useDebugValue, didWarnOld18Alpha = false, didWarnUncachedGetSnapshot = false, shim = "undefined" === typeof window || "undefined" === typeof window.document || "undefined" === typeof window.document.createElement ? useSyncExternalStore$1 : useSyncExternalStore$2;
8027
+ var React60 = __require("react"), objectIs = "function" === typeof Object.is ? Object.is : is3, useState48 = React60.useState, useEffect38 = React60.useEffect, useLayoutEffect10 = React60.useLayoutEffect, useDebugValue2 = React60.useDebugValue, didWarnOld18Alpha = false, didWarnUncachedGetSnapshot = false, shim = "undefined" === typeof window || "undefined" === typeof window.document || "undefined" === typeof window.document.createElement ? useSyncExternalStore$1 : useSyncExternalStore$2;
8028
8028
  exports.useSyncExternalStore = void 0 !== React60.useSyncExternalStore ? React60.useSyncExternalStore : shim;
8029
8029
  "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error());
8030
8030
  })();
@@ -8052,12 +8052,12 @@ var require_with_selector_production = __commonJS({
8052
8052
  }
8053
8053
  var objectIs = "function" === typeof Object.is ? Object.is : is3;
8054
8054
  var useSyncExternalStore3 = shim.useSyncExternalStore;
8055
- var useRef37 = React60.useRef;
8056
- var useEffect39 = React60.useEffect;
8055
+ var useRef36 = React60.useRef;
8056
+ var useEffect38 = React60.useEffect;
8057
8057
  var useMemo30 = React60.useMemo;
8058
8058
  var useDebugValue2 = React60.useDebugValue;
8059
8059
  exports.useSyncExternalStoreWithSelector = function(subscribe, getSnapshot, getServerSnapshot, selector, isEqual2) {
8060
- var instRef = useRef37(null);
8060
+ var instRef = useRef36(null);
8061
8061
  if (null === instRef.current) {
8062
8062
  var inst = { hasValue: false, value: null };
8063
8063
  instRef.current = inst;
@@ -8097,7 +8097,7 @@ var require_with_selector_production = __commonJS({
8097
8097
  [getSnapshot, getServerSnapshot, selector, isEqual2]
8098
8098
  );
8099
8099
  var value = useSyncExternalStore3(subscribe, instRef[0], instRef[1]);
8100
- useEffect39(
8100
+ useEffect38(
8101
8101
  function() {
8102
8102
  inst.hasValue = true;
8103
8103
  inst.value = value;
@@ -8118,9 +8118,9 @@ var require_with_selector_development = __commonJS({
8118
8118
  return x2 === y2 && (0 !== x2 || 1 / x2 === 1 / y2) || x2 !== x2 && y2 !== y2;
8119
8119
  }
8120
8120
  "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
8121
- var React60 = __require("react"), shim = require_shim(), objectIs = "function" === typeof Object.is ? Object.is : is3, useSyncExternalStore3 = shim.useSyncExternalStore, useRef37 = React60.useRef, useEffect39 = React60.useEffect, useMemo30 = React60.useMemo, useDebugValue2 = React60.useDebugValue;
8121
+ var React60 = __require("react"), shim = require_shim(), objectIs = "function" === typeof Object.is ? Object.is : is3, useSyncExternalStore3 = shim.useSyncExternalStore, useRef36 = React60.useRef, useEffect38 = React60.useEffect, useMemo30 = React60.useMemo, useDebugValue2 = React60.useDebugValue;
8122
8122
  exports.useSyncExternalStoreWithSelector = function(subscribe, getSnapshot, getServerSnapshot, selector, isEqual2) {
8123
- var instRef = useRef37(null);
8123
+ var instRef = useRef36(null);
8124
8124
  if (null === instRef.current) {
8125
8125
  var inst = { hasValue: false, value: null };
8126
8126
  instRef.current = inst;
@@ -8161,7 +8161,7 @@ var require_with_selector_development = __commonJS({
8161
8161
  [getSnapshot, getServerSnapshot, selector, isEqual2]
8162
8162
  );
8163
8163
  var value = useSyncExternalStore3(subscribe, instRef[0], instRef[1]);
8164
- useEffect39(
8164
+ useEffect38(
8165
8165
  function() {
8166
8166
  inst.hasValue = true;
8167
8167
  inst.value = value;
@@ -8196,12 +8196,12 @@ var require_use_sync_external_store_with_selector_production = __commonJS({
8196
8196
  }
8197
8197
  var objectIs = "function" === typeof Object.is ? Object.is : is3;
8198
8198
  var useSyncExternalStore3 = React60.useSyncExternalStore;
8199
- var useRef37 = React60.useRef;
8200
- var useEffect39 = React60.useEffect;
8199
+ var useRef36 = React60.useRef;
8200
+ var useEffect38 = React60.useEffect;
8201
8201
  var useMemo30 = React60.useMemo;
8202
8202
  var useDebugValue2 = React60.useDebugValue;
8203
8203
  exports.useSyncExternalStoreWithSelector = function(subscribe, getSnapshot, getServerSnapshot, selector, isEqual2) {
8204
- var instRef = useRef37(null);
8204
+ var instRef = useRef36(null);
8205
8205
  if (null === instRef.current) {
8206
8206
  var inst = { hasValue: false, value: null };
8207
8207
  instRef.current = inst;
@@ -8241,7 +8241,7 @@ var require_use_sync_external_store_with_selector_production = __commonJS({
8241
8241
  [getSnapshot, getServerSnapshot, selector, isEqual2]
8242
8242
  );
8243
8243
  var value = useSyncExternalStore3(subscribe, instRef[0], instRef[1]);
8244
- useEffect39(
8244
+ useEffect38(
8245
8245
  function() {
8246
8246
  inst.hasValue = true;
8247
8247
  inst.value = value;
@@ -8262,9 +8262,9 @@ var require_use_sync_external_store_with_selector_development = __commonJS({
8262
8262
  return x2 === y2 && (0 !== x2 || 1 / x2 === 1 / y2) || x2 !== x2 && y2 !== y2;
8263
8263
  }
8264
8264
  "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
8265
- var React60 = __require("react"), objectIs = "function" === typeof Object.is ? Object.is : is3, useSyncExternalStore3 = React60.useSyncExternalStore, useRef37 = React60.useRef, useEffect39 = React60.useEffect, useMemo30 = React60.useMemo, useDebugValue2 = React60.useDebugValue;
8265
+ var React60 = __require("react"), objectIs = "function" === typeof Object.is ? Object.is : is3, useSyncExternalStore3 = React60.useSyncExternalStore, useRef36 = React60.useRef, useEffect38 = React60.useEffect, useMemo30 = React60.useMemo, useDebugValue2 = React60.useDebugValue;
8266
8266
  exports.useSyncExternalStoreWithSelector = function(subscribe, getSnapshot, getServerSnapshot, selector, isEqual2) {
8267
- var instRef = useRef37(null);
8267
+ var instRef = useRef36(null);
8268
8268
  if (null === instRef.current) {
8269
8269
  var inst = { hasValue: false, value: null };
8270
8270
  instRef.current = inst;
@@ -8305,7 +8305,7 @@ var require_use_sync_external_store_with_selector_development = __commonJS({
8305
8305
  [getSnapshot, getServerSnapshot, selector, isEqual2]
8306
8306
  );
8307
8307
  var value = useSyncExternalStore3(subscribe, instRef[0], instRef[1]);
8308
- useEffect39(
8308
+ useEffect38(
8309
8309
  function() {
8310
8310
  inst.hasValue = true;
8311
8311
  inst.value = value;
@@ -10792,9 +10792,6 @@ function getCanvasStreamData(part) {
10792
10792
  errorText: getToolErrorText(part)
10793
10793
  };
10794
10794
  }
10795
- function isApprovalRequested(part) {
10796
- return part.state === "approval-requested";
10797
- }
10798
10795
  function getApprovalId(part) {
10799
10796
  const approval = part.approval;
10800
10797
  return approval?.id;
@@ -13561,862 +13558,325 @@ function OAuthAuthorizeCard({
13561
13558
  )
13562
13559
  ] });
13563
13560
  }
13564
- var DOMAIN7 = "conversation-desktop";
13565
- var conversationDesktopKeys = {
13566
- all: [DOMAIN7],
13567
- status: (conversationId) => [DOMAIN7, "status", conversationId]
13568
- };
13569
- function conversationDesktop(client, conversationId) {
13570
- return client.v1.conversations({ conversationId });
13571
- }
13572
- async function fetchDesktopStatus(client, conversationId, signal) {
13573
- const { data, error } = await conversationDesktop(client, conversationId).desktop.get({
13574
- fetch: { signal }
13575
- });
13576
- if (error) throw error;
13577
- return unwrapData(data);
13561
+ function getDesktop() {
13562
+ if (typeof window === "undefined") return null;
13563
+ return window.fronteraDesktop ?? null;
13578
13564
  }
13579
- async function fetchDesktopScreenshot(client, conversationId, maxDim, signal) {
13580
- const res = await conversationDesktop(client, conversationId).desktop.screenshot.get({
13581
- query: { maxDim: String(maxDim) },
13582
- fetch: { signal }
13583
- });
13584
- if (res.error) {
13585
- const status = res.error.status;
13586
- const value = res.error.value;
13587
- if (status === 503 || isWakingBody(value) || isWakingBody(res.data)) {
13588
- return { ok: false, waking: true };
13589
- }
13590
- throw res.error;
13591
- }
13592
- const payload = res.data;
13593
- if (isWakingBody(payload)) return { ok: false, waking: true };
13594
- const blob = desktopScreenshotBlob(payload);
13595
- return { ok: true, blob };
13565
+ var noopSubscribe = () => () => {
13566
+ };
13567
+ function getDesktopAccessBridge() {
13568
+ return getDesktop()?.desktopAccess ?? null;
13596
13569
  }
13597
- function isWakingBody(value) {
13598
- return Boolean(
13599
- value && typeof value === "object" && "waking" in value && value.waking === true
13570
+ var getDesktopAccessSnapshot = () => getDesktopAccessBridge();
13571
+ var getDesktopAccessServerSnapshot = () => null;
13572
+ function useDesktopAccessBridge() {
13573
+ return useSyncExternalStore(
13574
+ noopSubscribe,
13575
+ getDesktopAccessSnapshot,
13576
+ getDesktopAccessServerSnapshot
13600
13577
  );
13601
13578
  }
13602
- function desktopScreenshotBlob(payload) {
13603
- if (payload instanceof Blob) return payload;
13604
- if (payload instanceof ArrayBuffer) {
13605
- return new Blob([payload], { type: "image/png" });
13606
- }
13607
- if (ArrayBuffer.isView(payload)) {
13608
- const view = payload;
13609
- const copy3 = new Uint8Array(view.byteLength);
13610
- copy3.set(new Uint8Array(view.buffer, view.byteOffset, view.byteLength));
13611
- return new Blob([copy3], { type: "image/png" });
13612
- }
13613
- throw new Error("Desktop screenshot was not an image");
13614
- }
13615
- function useConversationDesktopStatus(conversationId) {
13616
- const client = useClient();
13617
- return useQuery({
13618
- queryKey: conversationDesktopKeys.status(conversationId ?? ""),
13619
- queryFn: () => fetchDesktopStatus(client, conversationId),
13620
- enabled: !!conversationId,
13579
+ env("NEXT_PUBLIC_SERVICE_URL") || "http://localhost:4000";
13580
+ function useDesktopAccessLocalStatus() {
13581
+ const bridge = useDesktopAccessBridge();
13582
+ const queryClient = useQueryClient();
13583
+ const query = useQuery({
13584
+ queryKey: desktopAccessKeys.local(),
13585
+ queryFn: () => bridge.status(),
13586
+ enabled: bridge !== null,
13621
13587
  staleTime: Infinity,
13622
- gcTime: 5 * 60 * 1e3,
13623
- retry: false,
13624
- refetchOnWindowFocus: false,
13625
- refetchOnReconnect: false
13588
+ retry: 1
13626
13589
  });
13590
+ useEffect(() => {
13591
+ if (!bridge) return;
13592
+ return bridge.onEvent((event) => {
13593
+ if (event.type === "status" && event.status) {
13594
+ queryClient.setQueryData(desktopAccessKeys.local(), event.status);
13595
+ } else {
13596
+ void queryClient.invalidateQueries({ queryKey: desktopAccessKeys.local() });
13597
+ }
13598
+ void queryClient.invalidateQueries({ queryKey: desktopAccessKeys.all });
13599
+ });
13600
+ }, [bridge, queryClient]);
13601
+ return query;
13627
13602
  }
13628
- var useDesktopScreenStore = create((set2, get3) => ({
13629
- openConversationId: null,
13630
- lightboxOpen: false,
13631
- agentDisplayName: null,
13632
- open: (conversationId) => set2({ openConversationId: conversationId, lightboxOpen: false }),
13633
- close: () => set2({ openConversationId: null, lightboxOpen: false }),
13634
- toggle: (conversationId) => {
13635
- if (get3().openConversationId === conversationId) {
13636
- set2({ openConversationId: null, lightboxOpen: false });
13637
- return;
13603
+ function useDecideDesktopGrantLocally() {
13604
+ const bridge = useDesktopAccessBridge();
13605
+ const queryClient = useQueryClient();
13606
+ return useMutation({
13607
+ mutationFn: async (input) => {
13608
+ if (!bridge) throw new Error("Desktop Access bridge is not available");
13609
+ return bridge.decideGrant(input);
13610
+ },
13611
+ onSuccess: () => {
13612
+ void queryClient.invalidateQueries({ queryKey: desktopAccessKeys.all });
13638
13613
  }
13639
- set2({ openConversationId: conversationId, lightboxOpen: false });
13640
- },
13641
- setLightboxOpen: (lightboxOpen) => set2({ lightboxOpen }),
13642
- setAgentDisplayName: (agentDisplayName) => set2({ agentDisplayName })
13643
- }));
13644
- function DesktopScreenThumbnail({
13645
- imageUrl,
13646
- state,
13647
- onOpen
13648
- }) {
13649
- const { t } = useTranslation();
13650
- const agentDisplayName = useDesktopScreenStore((s2) => s2.agentDisplayName);
13651
- const caption = agentDisplayName ? t("chat.desktop.agentsScreen", { name: agentDisplayName }) : null;
13652
- return /* @__PURE__ */ jsxs("div", { children: [
13653
- /* @__PURE__ */ jsxs("div", { className: "group relative aspect-video overflow-hidden rounded-xl", children: [
13654
- imageUrl ? (
13655
- // eslint-disable-next-line @next/next/no-img-element -- blob: screenshot URLs
13656
- /* @__PURE__ */ jsx(
13657
- "img",
13658
- {
13659
- src: imageUrl,
13660
- alt: caption ?? t("chat.desktop.screen"),
13661
- className: "absolute inset-0 size-full object-cover"
13662
- }
13663
- )
13664
- ) : /* @__PURE__ */ jsx(Skeleton, { className: "absolute inset-0 size-full rounded-none" }),
13665
- /* @__PURE__ */ jsx(
13666
- "div",
13667
- {
13668
- className: cn(
13669
- "absolute inset-0 flex items-center justify-center bg-scrim/30 opacity-0 transition-opacity duration-150 group-hover:opacity-100",
13670
- "focus-within:opacity-100"
13671
- ),
13672
- children: /* @__PURE__ */ jsx(
13673
- Button,
13674
- {
13675
- type: "button",
13676
- variant: "default-neutral",
13677
- size: "sm",
13678
- className: "rounded-full px-4",
13679
- onClick: onOpen,
13680
- children: t("chat.desktop.open")
13681
- }
13682
- )
13683
- }
13684
- ),
13685
- state === "off" && !imageUrl ? /* @__PURE__ */ jsx("span", { className: "sr-only", children: t("chat.desktop.asleep") }) : null
13686
- ] }),
13687
- caption ? /* @__PURE__ */ jsx("p", { className: "mt-2 text-center text-xs text-muted-foreground", children: caption }) : null
13688
- ] });
13614
+ });
13615
+ }
13616
+ function useDecideDesktopApprovalLocally() {
13617
+ const bridge = useDesktopAccessBridge();
13618
+ const queryClient = useQueryClient();
13619
+ return useMutation({
13620
+ mutationFn: async (input) => {
13621
+ if (!bridge) throw new Error("Desktop Access bridge is not available");
13622
+ return bridge.decideApproval(input);
13623
+ },
13624
+ onSuccess: () => {
13625
+ void queryClient.invalidateQueries({ queryKey: desktopAccessKeys.all });
13626
+ }
13627
+ });
13689
13628
  }
13690
13629
 
13691
- // src/vendor/ui/chat/desktop-screen-panel/types.ts
13692
- var DESKTOP_SCREEN_POLL_MS = 2500;
13693
- var DESKTOP_THUMBNAIL_MAX_DIM = 640;
13630
+ // src/vendor/ui/desktop-access/utils/desktop-health.ts
13631
+ var PRESENTATION = {
13632
+ desktop_offline: { tone: "bg-muted-foreground", textTone: "text-muted-foreground", labelKey: "desktop_offline", animate: false, usable: false },
13633
+ connecting: { tone: "bg-muted-foreground", textTone: "text-muted-foreground", labelKey: "connecting", animate: true, usable: false },
13634
+ connected_unregistered: { tone: "bg-warning", textTone: "text-warning", labelKey: "connected_unregistered", animate: false, usable: false },
13635
+ registered_unroutable: { tone: "bg-warning", textTone: "text-warning", labelKey: "registered_unroutable", animate: false, usable: false },
13636
+ available_no_grant: { tone: "bg-warning", textTone: "text-warning", labelKey: "available_no_grant", animate: false, usable: false },
13637
+ waiting_for_approval: { tone: "bg-warning", textTone: "text-warning", labelKey: "waiting_for_approval", animate: false, usable: false },
13638
+ blocked_by_org_policy: { tone: "bg-destructive", textTone: "text-destructive", labelKey: "blocked_by_org_policy", animate: false, usable: false },
13639
+ blocked_by_os_permission: { tone: "bg-destructive", textTone: "text-destructive", labelKey: "blocked_by_os_permission", animate: false, usable: false },
13640
+ blocked_by_device_policy: { tone: "bg-destructive", textTone: "text-destructive", labelKey: "blocked_by_device_policy", animate: false, usable: false },
13641
+ resource_unavailable: { tone: "bg-warning", textTone: "text-warning", labelKey: "resource_unavailable", animate: false, usable: false },
13642
+ upgrade_required: { tone: "bg-destructive", textTone: "text-destructive", labelKey: "upgrade_required", animate: false, usable: false },
13643
+ ready: { tone: "bg-success", textTone: "text-foreground", labelKey: "ready", animate: false, usable: true },
13644
+ executing: { tone: "bg-success", textTone: "text-foreground", labelKey: "executing", animate: true, usable: true },
13645
+ degraded: { tone: "bg-warning", textTone: "text-warning", labelKey: "degraded", animate: false, usable: true }
13646
+ };
13647
+ function describeDesktopHealth(health) {
13648
+ return health && PRESENTATION[health] || PRESENTATION.desktop_offline;
13649
+ }
13650
+ function isDeviceReachable(health) {
13651
+ if (!health) return false;
13652
+ return health !== "desktop_offline" && health !== "connecting" && health !== "upgrade_required";
13653
+ }
13694
13654
 
13695
- // src/vendor/ui/chat/desktop-screen-panel/hooks/use-desktop-screen-poll.ts
13696
- function tabIsVisible() {
13697
- if (typeof document === "undefined") return true;
13698
- return document.visibilityState === "visible";
13655
+ // src/vendor/ui/desktop-access/utils/desktop-labels.ts
13656
+ var desktopLabelKey = {
13657
+ resourceKind: (kind) => `desktopAccess.resourceKind.${kind}`,
13658
+ operation: (op) => `desktopAccess.operation.${op}`,
13659
+ scope: (scope) => `desktopAccess.scope.${scope}`,
13660
+ writeMode: (mode) => `desktopAccess.writeMode.${mode}`,
13661
+ riskClass: (risk) => `desktopAccess.risk.${risk}`,
13662
+ capabilityClass: (cls) => `desktopAccess.capability.${cls}`,
13663
+ /** `desktop.files.read` → `desktopAccess.tool.files.read` */
13664
+ capability: (name) => `desktopAccess.tool.${name.replace(/^desktop\./, "")}`,
13665
+ health: (health) => `desktopAccess.health.${health}`,
13666
+ trust: (trust) => `desktopAccess.trust.${trust}`,
13667
+ requestStatus: (status) => `desktopAccess.request.status.${status}`,
13668
+ osPermission: (kind) => `desktopAccess.osPermission.${kind}`,
13669
+ osPermissionState: (state) => `desktopAccess.osPermissionState.${state}`,
13670
+ auditKind: (kind) => `desktopAccess.audit.kind.${kind}`
13671
+ };
13672
+ function capabilityFromToolName(toolName) {
13673
+ if (!toolName.startsWith("local_")) return null;
13674
+ const rest = toolName.slice("local_".length);
13675
+ const [group, ...action] = rest.split("_");
13676
+ if (!group || action.length === 0) return null;
13677
+ const family = group === "command" ? "commands" : group;
13678
+ return `desktop.${family}.${action.join("_")}`;
13699
13679
  }
13700
- function useDesktopScreenPoll({
13701
- conversationId,
13702
- open,
13703
- maxDim,
13704
- fetchers,
13705
- pollIntervalMs = DESKTOP_SCREEN_POLL_MS
13706
- }) {
13707
- const [state, setState] = useState("off");
13708
- const [imageUrl, setImageUrl] = useState(null);
13709
- const [reconnecting, setReconnecting] = useState(false);
13710
- const [display, setDisplay] = useState();
13711
- const [detail, setDetail] = useState();
13712
- const imageUrlRef = useRef(null);
13713
- const fetchersRef = useRef(fetchers);
13714
- fetchersRef.current = fetchers;
13715
- useEffect(() => {
13716
- imageUrlRef.current = imageUrl;
13717
- }, [imageUrl]);
13718
- useEffect(() => {
13719
- if (!open) {
13720
- setState("off");
13721
- setReconnecting(false);
13722
- setDisplay(void 0);
13723
- const previous = imageUrlRef.current;
13724
- if (previous) {
13725
- URL.revokeObjectURL(previous);
13726
- imageUrlRef.current = null;
13727
- setImageUrl(null);
13728
- }
13729
- }
13730
- }, [open]);
13731
- useEffect(() => {
13732
- if (!open || !conversationId) return;
13733
- let cancelled2 = false;
13734
- let inFlight = false;
13735
- const controller = new AbortController();
13736
- const tick = async () => {
13737
- if (cancelled2 || inFlight || !tabIsVisible()) return;
13738
- inFlight = true;
13739
- try {
13740
- const status = await fetchersRef.current.fetchStatus(
13741
- conversationId,
13742
- controller.signal
13743
- );
13744
- if (cancelled2) return;
13745
- setDisplay(status.display);
13746
- setDetail(status.detail);
13747
- if (!status.enabled) {
13748
- setState("off");
13749
- setReconnecting(false);
13750
- return;
13751
- }
13752
- if (status.state === "unavailable" || status.state === "upgradable") {
13753
- setState(status.state);
13754
- setReconnecting(false);
13755
- return;
13756
- }
13757
- const shot = await fetchersRef.current.fetchScreenshot(
13758
- conversationId,
13759
- maxDim,
13760
- controller.signal
13761
- );
13762
- if (cancelled2) return;
13763
- if (!shot.ok) {
13764
- setState("waking");
13765
- setReconnecting(false);
13766
- return;
13767
- }
13768
- const nextUrl = URL.createObjectURL(shot.blob);
13769
- const previous = imageUrlRef.current;
13770
- imageUrlRef.current = nextUrl;
13771
- setImageUrl(nextUrl);
13772
- if (previous) URL.revokeObjectURL(previous);
13773
- setState("live");
13774
- setReconnecting(false);
13775
- } catch (error) {
13776
- if (cancelled2) return;
13777
- if (controller.signal.aborted) return;
13778
- if (error instanceof DOMException && error.name === "AbortError") return;
13779
- if (imageUrlRef.current) {
13780
- setReconnecting(true);
13781
- } else {
13782
- setState("off");
13783
- }
13784
- } finally {
13785
- inFlight = false;
13786
- }
13787
- };
13788
- const onVisibility = () => {
13789
- if (tabIsVisible()) void tick();
13790
- };
13791
- void tick();
13792
- const interval = setInterval(() => {
13793
- void tick();
13794
- }, pollIntervalMs);
13795
- document.addEventListener("visibilitychange", onVisibility);
13796
- return () => {
13797
- cancelled2 = true;
13798
- controller.abort();
13799
- clearInterval(interval);
13800
- document.removeEventListener("visibilitychange", onVisibility);
13801
- };
13802
- }, [open, conversationId, maxDim, pollIntervalMs]);
13803
- useEffect(() => {
13804
- return () => {
13805
- const previous = imageUrlRef.current;
13806
- if (previous) URL.revokeObjectURL(previous);
13807
- };
13808
- }, []);
13809
- return { state, imageUrl, reconnecting, display, detail };
13680
+ function isDesktopAccessTool(toolName) {
13681
+ return capabilityFromToolName(toolName) !== null;
13810
13682
  }
13811
- var CARD_POLL_MS = 5e3;
13812
- function RequestHumanApprovalCard({
13813
- conversationId,
13814
- title,
13815
- phase,
13816
- onApprove,
13817
- onDeny
13818
- }) {
13819
- const { t } = useTranslation();
13820
- const client = useClient();
13821
- const desktop = useConversationDesktopStatus(conversationId);
13822
- const setLightboxOpen = useDesktopScreenStore((s2) => s2.setLightboxOpen);
13823
- const openPanel = useDesktopScreenStore((s2) => s2.open);
13824
- const fetchers = useMemo(
13825
- () => ({
13826
- fetchStatus: (id, signal) => fetchDesktopStatus(client, id, signal),
13827
- fetchScreenshot: (id, maxDim, signal) => fetchDesktopScreenshot(client, id, maxDim, signal)
13828
- }),
13829
- [client]
13830
- );
13831
- const poll = useDesktopScreenPoll({
13832
- conversationId,
13833
- open: phase === "pending" && !!conversationId && desktop.data?.enabled === true,
13834
- maxDim: DESKTOP_THUMBNAIL_MAX_DIM,
13835
- fetchers,
13836
- pollIntervalMs: CARD_POLL_MS
13837
- });
13838
- const heading = t("chat.desktop.computer");
13839
- const badge = phase === "pending" ? {
13840
- label: t("chat.desktop.actionNeeded"),
13841
- className: "bg-warning/10 text-warning"} : phase === "done" ? {
13842
- label: t("chat.desktop.done"),
13843
- className: "bg-success/10 text-success"} : {
13844
- label: t("chat.desktop.skipped"),
13845
- className: "bg-surface-inset text-muted-foreground"};
13846
- if (phase !== "pending") {
13847
- return /* @__PURE__ */ jsxs(
13848
- "div",
13849
- {
13850
- "data-slot": "request-human-approval-card",
13851
- "data-phase": phase,
13852
- className: "relative my-2 flex items-center gap-2 rounded-xl border border-border bg-surface px-4 py-2",
13853
- children: [
13854
- /* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm font-semibold text-foreground", children: heading }),
13855
- /* @__PURE__ */ jsx(
13856
- "span",
13857
- {
13858
- className: cn(
13859
- "shrink-0 rounded-full px-2 py-0.5 text-xs font-medium",
13860
- badge.className
13861
- ),
13862
- children: badge.label
13863
- }
13864
- ),
13865
- title ? /* @__PURE__ */ jsx("p", { className: "min-w-0 flex-1 truncate text-sm text-foreground", children: title }) : /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1" }),
13866
- /* @__PURE__ */ jsxs(
13867
- Button,
13868
- {
13869
- type: "button",
13870
- variant: "outline",
13871
- size: "sm",
13872
- className: "shrink-0",
13873
- onClick: () => {
13874
- if (conversationId) openPanel(conversationId);
13875
- setLightboxOpen(true);
13876
- },
13877
- children: [
13878
- /* @__PURE__ */ jsx(DeviceDesktopIcon, { className: "size-3.5" }),
13879
- t("chat.desktop.openComputer")
13880
- ]
13881
- }
13882
- )
13883
- ]
13884
- }
13885
- );
13683
+ function formatBytes(bytes) {
13684
+ if (bytes == null || !Number.isFinite(bytes) || bytes < 0) return null;
13685
+ if (bytes < 1024) return `${bytes} B`;
13686
+ const units = ["KB", "MB", "GB", "TB"];
13687
+ let value = bytes / 1024;
13688
+ let unit2 = 0;
13689
+ while (value >= 1024 && unit2 < units.length - 1) {
13690
+ value /= 1024;
13691
+ unit2 += 1;
13886
13692
  }
13693
+ return `${value < 10 ? value.toFixed(1) : Math.round(value)} ${units[unit2]}`;
13694
+ }
13695
+ function shortRevision(revision) {
13696
+ if (!revision) return null;
13697
+ return revision.slice(0, 8);
13698
+ }
13699
+ function DecisionCard({
13700
+ tone = "idle",
13701
+ icon,
13702
+ kind,
13703
+ question,
13704
+ detail,
13705
+ note,
13706
+ actions,
13707
+ settled,
13708
+ children,
13709
+ className,
13710
+ testId
13711
+ }) {
13712
+ const toneClass = tone === "ok" ? "text-success" : tone === "warn" ? "text-warning" : tone === "danger" ? "text-destructive" : "text-muted-foreground";
13887
13713
  return /* @__PURE__ */ jsxs(
13888
13714
  "div",
13889
13715
  {
13890
- "data-slot": "request-human-approval-card",
13891
- "data-phase": phase,
13892
- className: "relative my-2 rounded-xl border border-border bg-surface p-4",
13716
+ "data-testid": testId,
13717
+ "data-tone": tone,
13718
+ "data-settled": settled != null ? "" : void 0,
13719
+ className: cn(
13720
+ "relative mt-2 min-w-0 rounded-xl border border-border bg-surface-raised px-4 py-3",
13721
+ className
13722
+ ),
13893
13723
  children: [
13894
- /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center gap-2", children: [
13895
- /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-foreground", children: heading }),
13896
- /* @__PURE__ */ jsxs(
13897
- "span",
13898
- {
13899
- className: cn(
13900
- "ml-auto inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-xs font-medium",
13901
- badge.className
13902
- ),
13903
- children: [
13904
- /* @__PURE__ */ jsx("span", { className: "size-1.5 rounded-full bg-warning animate-pulse", "aria-hidden": true }),
13905
- badge.label
13906
- ]
13907
- }
13908
- )
13724
+ /* @__PURE__ */ jsxs("div", { className: cn("flex min-w-0 items-center gap-1.5 text-xs", toneClass), children: [
13725
+ /* @__PURE__ */ jsx("span", { className: "flex size-4 shrink-0 items-center justify-center [&_svg]:size-3.5", children: icon ?? /* @__PURE__ */ jsx(DeviceDesktopIcon, { "aria-hidden": true }) }),
13726
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: kind }),
13727
+ settled != null && /* @__PURE__ */ jsx("span", { className: "ml-auto min-w-0 truncate text-right text-foreground", "data-testid": testId ? `${testId}-settled` : void 0, children: settled })
13909
13728
  ] }),
13910
- title ? /* @__PURE__ */ jsx("p", { className: "mb-2 truncate text-sm text-foreground", children: title }) : null,
13911
- /* @__PURE__ */ jsx("div", { className: "mb-4", children: /* @__PURE__ */ jsx(
13912
- DesktopScreenThumbnail,
13913
- {
13914
- imageUrl: poll.imageUrl,
13915
- state: poll.state,
13916
- onOpen: () => setLightboxOpen(true)
13917
- }
13918
- ) }),
13919
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
13920
- /* @__PURE__ */ jsx(
13921
- Button,
13922
- {
13923
- type: "button",
13924
- variant: "default",
13925
- onClick: () => setLightboxOpen(true),
13926
- children: t("chat.desktop.takeOver")
13927
- }
13928
- ),
13929
- /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: onApprove, children: t("chat.desktop.imDone") }),
13930
- /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", className: "ml-auto", onClick: onDeny, children: t("chat.desktop.skip") })
13931
- ] })
13729
+ settled == null && /* @__PURE__ */ jsxs(Fragment, { children: [
13730
+ question != null && /* @__PURE__ */ jsx("p", { className: "mt-1 break-words text-sm text-foreground", children: question }),
13731
+ detail != null && /* @__PURE__ */ jsx("div", { className: "mt-2 min-w-0 overflow-hidden whitespace-pre-wrap break-all rounded-md bg-surface-inset px-3 py-2 font-mono text-xs leading-relaxed text-foreground/90", children: detail }),
13732
+ children,
13733
+ note != null && /* @__PURE__ */ jsx("p", { className: "mt-2 break-words text-xs text-muted-foreground", children: note }),
13734
+ actions != null && /* @__PURE__ */ jsx("div", { className: "mt-3 flex flex-wrap items-center justify-end gap-2", children: actions })
13735
+ ] }),
13736
+ settled != null && children
13932
13737
  ]
13933
13738
  }
13934
13739
  );
13935
13740
  }
13936
-
13937
- // src/vendor/ui/chat/request-human-approval-card/utils.ts
13938
- var REQUEST_HUMAN_TOOL = "request_human";
13939
- function isRequestHumanTool(name) {
13940
- return name === REQUEST_HUMAN_TOOL;
13941
- }
13942
- function requestHumanTitle(input, fallback = "") {
13943
- if (input && typeof input === "object") {
13944
- const record = input;
13945
- for (const key of ["title", "message", "reason", "prompt"]) {
13946
- const value = record[key];
13947
- if (typeof value === "string" && value.trim()) return value.trim();
13948
- }
13949
- }
13950
- return fallback;
13951
- }
13952
- function requestHumanPhaseFromPart(part) {
13953
- const state = typeof part.state === "string" ? part.state : "";
13954
- if (state === "approval-requested") return "pending";
13955
- const approval = part.approval;
13956
- if (approval && approval.approved === false) return "skipped";
13957
- if (state === "output-error" || state === "output-denied" || typeof part.errorText === "string") {
13958
- return "skipped";
13959
- }
13960
- const output = part.output ?? part.result;
13961
- if (output && typeof output === "object") {
13962
- const record = output;
13963
- if (record.skipped === true || record.denied === true || record.approved === false) {
13964
- return "skipped";
13741
+ function Kbd({ className, ...props }) {
13742
+ return /* @__PURE__ */ jsx(
13743
+ "kbd",
13744
+ {
13745
+ "data-slot": "kbd",
13746
+ className: cn(
13747
+ "pointer-events-none inline-flex h-4 min-w-4 select-none items-center justify-center rounded border border-border bg-surface-inset px-1 font-mono text-[10px] font-medium leading-none text-muted-foreground",
13748
+ className
13749
+ ),
13750
+ ...props
13965
13751
  }
13966
- }
13967
- if (state === "output-available" || state === "result" || output != null) return "done";
13968
- if (state === "input-available" || state === "input-streaming" || state === "call" || state === "partial-call") {
13969
- return "pending";
13970
- }
13971
- return "done";
13972
- }
13973
- function requestHumanThreadPhase(part, pendingStatus) {
13974
- if (pendingStatus === "pending") return "pending";
13975
- if (pendingStatus === "abandoned") return "skipped";
13976
- return requestHumanPhaseFromPart(part);
13977
- }
13978
- var ALWAYS_ALLOW_KEY = "sebati-tool-always-allow";
13979
- function getAlwaysAllowedTools() {
13980
- if (typeof window === "undefined") return /* @__PURE__ */ new Set();
13981
- try {
13982
- const stored = localStorage.getItem(ALWAYS_ALLOW_KEY);
13983
- if (stored) return new Set(JSON.parse(stored));
13984
- } catch {
13985
- }
13986
- return /* @__PURE__ */ new Set();
13987
- }
13988
- function addAlwaysAllowedTool(toolName) {
13989
- if (typeof window === "undefined") return;
13990
- const tools = getAlwaysAllowedTools();
13991
- tools.add(toolName);
13992
- localStorage.setItem(ALWAYS_ALLOW_KEY, JSON.stringify([...tools]));
13752
+ );
13993
13753
  }
13994
- function isToolAlwaysAllowed(toolName) {
13995
- return getAlwaysAllowedTools().has(toolName);
13754
+ function DecisionSecondaryButton({
13755
+ label,
13756
+ onClick,
13757
+ disabled,
13758
+ "aria-label": ariaLabel
13759
+ }) {
13760
+ return /* @__PURE__ */ jsxs(Button, { size: "sm", variant: "outline", disabled, onClick, "aria-label": ariaLabel, "data-slot": "decision-secondary", children: [
13761
+ label,
13762
+ /* @__PURE__ */ jsx(Kbd, { "aria-hidden": true, className: "hidden md:inline-flex", children: "Esc" })
13763
+ ] });
13996
13764
  }
13997
- function ToolApprovalCard({
13998
- part,
13999
- onApprove,
14000
- onDeny,
14001
- onAlwaysAllow,
14002
- conversationId = null
13765
+ function DecisionSplitButton({
13766
+ label,
13767
+ onClick,
13768
+ disabled,
13769
+ variant = "default",
13770
+ options,
13771
+ menuLabel,
13772
+ hint = "enter",
13773
+ className,
13774
+ "aria-label": ariaLabel
14003
13775
  }) {
14004
13776
  const { t } = useTranslation();
14005
- const toolName = getToolName(part);
14006
- const input = getToolInput(part);
14007
- const approvalId = getApprovalId(part);
14008
- const [responded, setResponded] = useState(false);
14009
- const desktop = useConversationDesktopStatus(
14010
- isRequestHumanTool(toolName) ? conversationId : null
13777
+ const hasMenu = !!options && options.length > 0;
13778
+ const kbdClass = variant === "default" || variant === "default-neutral" ? "hidden md:inline-flex border-primary-foreground/30 bg-transparent text-primary-foreground/80" : "hidden md:inline-flex bg-transparent";
13779
+ const main = /* @__PURE__ */ jsxs(
13780
+ Button,
13781
+ {
13782
+ size: "sm",
13783
+ variant,
13784
+ disabled,
13785
+ onClick,
13786
+ "aria-label": ariaLabel,
13787
+ className: cn(hasMenu && "rounded-r-none", className),
13788
+ "data-slot": "decision-primary",
13789
+ children: [
13790
+ label,
13791
+ hint === "enter" && /* @__PURE__ */ jsx(Kbd, { "aria-hidden": true, className: kbdClass, children: "\u23CE" })
13792
+ ]
13793
+ }
14011
13794
  );
14012
- if (!approvalId) return null;
14013
- if (isRequestHumanTool(toolName) && desktop.data?.enabled) {
14014
- return /* @__PURE__ */ jsx(
14015
- RequestHumanApprovalCard,
14016
- {
14017
- conversationId,
14018
- title: requestHumanTitle(input),
14019
- phase: "pending",
14020
- onApprove: () => onApprove(approvalId),
14021
- onDeny: () => onDeny(approvalId)
14022
- }
14023
- );
14024
- }
14025
- const displayInput = input ? Object.entries(input).map(([key, val]) => {
14026
- const strVal = typeof val === "string" ? val : JSON.stringify(val);
14027
- const display = strVal.length > 120 ? strVal.slice(0, 117) + "..." : strVal;
14028
- return { key, value: display };
14029
- }) : [];
14030
- const handleApprove = () => {
14031
- setResponded(true);
14032
- onApprove(approvalId);
14033
- };
14034
- const handleDeny = () => {
14035
- setResponded(true);
14036
- onDeny(approvalId);
14037
- };
14038
- const handleAlwaysAllow = () => {
14039
- setResponded(true);
14040
- onAlwaysAllow?.(toolName);
14041
- onApprove(approvalId);
14042
- };
14043
- return (
14044
- // `bg-surface-raised`, not `bg-background`: the thread behind this card is
14045
- // already `surface-chat`, so `background` painted the card DARKER than what
14046
- // it sits on — the one card in the run that must be read looked like a hole
14047
- // punched in the message.
14048
- //
14049
- // `relative` is load-bearing. This card renders inside the step timeline,
14050
- // whose rail is an absolutely-positioned span — so it paints above every
14051
- // static sibling, and a hairline ran straight down the card's left inset.
14052
- // Positioning the card puts it in the same paint layer, later in DOM order,
14053
- // and the rail stops at its edges: the timeline is a thread of small steps,
14054
- // and this is a panel interrupting it, not a bead on it.
14055
- /* @__PURE__ */ jsxs("div", { className: "relative rounded-lg border bg-surface-raised p-3 my-1.5", children: [
14056
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 mb-2", children: /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: t("dialog.toolApproval.title") }) }),
14057
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mb-2", children: /* @__PURE__ */ jsx("span", { className: "font-mono text-foreground", children: toolName }) }),
14058
- displayInput.length > 0 && /* @__PURE__ */ jsx("div", { className: "text-sm bg-surface-inset border rounded p-2 mb-3 space-y-0.5", children: displayInput.map(({ key, value }) => /* @__PURE__ */ jsxs("div", { children: [
14059
- /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
14060
- key,
14061
- ":"
14062
- ] }),
14063
- " ",
14064
- /* @__PURE__ */ jsx("span", { className: "text-foreground", children: value })
14065
- ] }, key)) }),
14066
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
14067
- /* @__PURE__ */ jsx(
14068
- Button,
14069
- {
14070
- variant: "default",
14071
- className: "gap-1.5",
14072
- disabled: responded,
14073
- onClick: handleApprove,
14074
- children: t("dialog.toolApproval.approve")
14075
- }
14076
- ),
14077
- /* @__PURE__ */ jsx(
14078
- Button,
14079
- {
14080
- variant: "outline",
14081
- className: "gap-1.5",
14082
- disabled: responded,
14083
- onClick: handleDeny,
14084
- children: t("dialog.toolApproval.deny")
14085
- }
14086
- ),
14087
- onAlwaysAllow && /* @__PURE__ */ jsx(
14088
- Button,
13795
+ if (!hasMenu) return main;
13796
+ return /* @__PURE__ */ jsxs("div", { className: "inline-flex items-stretch", "data-slot": "decision-split-button", children: [
13797
+ main,
13798
+ /* @__PURE__ */ jsxs(DropdownMenu, { children: [
13799
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
13800
+ Button,
13801
+ {
13802
+ size: "sm",
13803
+ variant,
13804
+ disabled,
13805
+ "aria-label": menuLabel ?? t("desktopAccess.action.moreOptions"),
13806
+ className: "rounded-l-none border-l border-l-background/25 px-1.5",
13807
+ "data-slot": "decision-menu-trigger",
13808
+ children: /* @__PURE__ */ jsx(ChevronDownMediumIcon, { "aria-hidden": true })
13809
+ }
13810
+ ) }),
13811
+ /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", className: "min-w-56", children: /* @__PURE__ */ jsx(TooltipProvider, { children: options.map(
13812
+ (option) => option.disabled && option.disabledReason != null ? /* @__PURE__ */ jsxs(Tooltip, { children: [
13813
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "block", children: /* @__PURE__ */ jsx(DropdownMenuItem, { disabled: true, "data-selected": option.selected ? "" : void 0, children: option.label }) }) }),
13814
+ /* @__PURE__ */ jsx(TooltipContent, { side: "left", children: option.disabledReason })
13815
+ ] }, option.id) : /* @__PURE__ */ jsx(
13816
+ DropdownMenuItem,
14089
13817
  {
14090
- variant: "ghost",
14091
- className: "gap-1.5 text-muted-foreground",
14092
- disabled: responded,
14093
- onClick: handleAlwaysAllow,
14094
- children: t("dialog.toolApproval.alwaysAllow")
14095
- }
13818
+ disabled: option.disabled,
13819
+ "data-selected": option.selected ? "" : void 0,
13820
+ className: "data-[selected]:font-medium",
13821
+ onSelect: () => option.onSelect(),
13822
+ children: option.label
13823
+ },
13824
+ option.id
14096
13825
  )
14097
- ] })
13826
+ ) }) })
14098
13827
  ] })
14099
- );
13828
+ ] });
14100
13829
  }
14101
- function getDesktop() {
14102
- if (typeof window === "undefined") return null;
14103
- return window.fronteraDesktop ?? null;
13830
+ var registry = [];
13831
+ var listening = false;
13832
+ function isTypingTarget(target) {
13833
+ if (!(target instanceof HTMLElement)) return false;
13834
+ if (target.isContentEditable) return true;
13835
+ const tag = target.tagName;
13836
+ if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
13837
+ return !!target.closest('[role="menu"], [role="dialog"], [role="listbox"], [contenteditable="true"]');
14104
13838
  }
14105
- var noopSubscribe = () => () => {
14106
- };
14107
- function getDesktopAccessBridge() {
14108
- return getDesktop()?.desktopAccess ?? null;
13839
+ function onKeyDown(event) {
13840
+ if (event.defaultPrevented || event.isComposing) return;
13841
+ if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return;
13842
+ if (event.key !== "Enter" && event.key !== "Escape") return;
13843
+ const owner = registry[registry.length - 1];
13844
+ if (!owner) return;
13845
+ if (isTypingTarget(event.target) || isTypingTarget(document.activeElement)) return;
13846
+ const handler = event.key === "Enter" ? owner.handlers.current.onPrimary : owner.handlers.current.onSecondary;
13847
+ if (!handler) return;
13848
+ event.preventDefault();
13849
+ handler();
14109
13850
  }
14110
- var getDesktopAccessSnapshot = () => getDesktopAccessBridge();
14111
- var getDesktopAccessServerSnapshot = () => null;
14112
- function useDesktopAccessBridge() {
14113
- return useSyncExternalStore(
14114
- noopSubscribe,
14115
- getDesktopAccessSnapshot,
14116
- getDesktopAccessServerSnapshot
14117
- );
13851
+ function ensureListener() {
13852
+ if (listening || typeof window === "undefined") return;
13853
+ listening = true;
13854
+ window.addEventListener("keydown", onKeyDown);
14118
13855
  }
14119
- env("NEXT_PUBLIC_SERVICE_URL") || "http://localhost:4000";
14120
- function useDesktopAccessLocalStatus() {
14121
- const bridge = useDesktopAccessBridge();
14122
- const queryClient = useQueryClient();
14123
- const query = useQuery({
14124
- queryKey: desktopAccessKeys.local(),
14125
- queryFn: () => bridge.status(),
14126
- enabled: bridge !== null,
14127
- staleTime: Infinity,
14128
- retry: 1
14129
- });
14130
- useEffect(() => {
14131
- if (!bridge) return;
14132
- return bridge.onEvent((event) => {
14133
- if (event.type === "status" && event.status) {
14134
- queryClient.setQueryData(desktopAccessKeys.local(), event.status);
14135
- } else {
14136
- void queryClient.invalidateQueries({ queryKey: desktopAccessKeys.local() });
14137
- }
14138
- void queryClient.invalidateQueries({ queryKey: desktopAccessKeys.all });
14139
- });
14140
- }, [bridge, queryClient]);
14141
- return query;
13856
+ function releaseListener() {
13857
+ if (!listening || registry.length > 0) return;
13858
+ listening = false;
13859
+ window.removeEventListener("keydown", onKeyDown);
14142
13860
  }
14143
- function useDecideDesktopGrantLocally() {
14144
- const bridge = useDesktopAccessBridge();
14145
- const queryClient = useQueryClient();
14146
- return useMutation({
14147
- mutationFn: async (input) => {
14148
- if (!bridge) throw new Error("Desktop Access bridge is not available");
14149
- return bridge.decideGrant(input);
14150
- },
14151
- onSuccess: () => {
14152
- void queryClient.invalidateQueries({ queryKey: desktopAccessKeys.all });
14153
- }
14154
- });
14155
- }
14156
- function useDecideDesktopApprovalLocally() {
14157
- const bridge = useDesktopAccessBridge();
14158
- const queryClient = useQueryClient();
14159
- return useMutation({
14160
- mutationFn: async (input) => {
14161
- if (!bridge) throw new Error("Desktop Access bridge is not available");
14162
- return bridge.decideApproval(input);
14163
- },
14164
- onSuccess: () => {
14165
- void queryClient.invalidateQueries({ queryKey: desktopAccessKeys.all });
14166
- }
14167
- });
14168
- }
14169
-
14170
- // src/vendor/ui/desktop-access/utils/desktop-health.ts
14171
- var PRESENTATION = {
14172
- desktop_offline: { tone: "bg-muted-foreground", textTone: "text-muted-foreground", labelKey: "desktop_offline", animate: false, usable: false },
14173
- connecting: { tone: "bg-muted-foreground", textTone: "text-muted-foreground", labelKey: "connecting", animate: true, usable: false },
14174
- connected_unregistered: { tone: "bg-warning", textTone: "text-warning", labelKey: "connected_unregistered", animate: false, usable: false },
14175
- registered_unroutable: { tone: "bg-warning", textTone: "text-warning", labelKey: "registered_unroutable", animate: false, usable: false },
14176
- available_no_grant: { tone: "bg-warning", textTone: "text-warning", labelKey: "available_no_grant", animate: false, usable: false },
14177
- waiting_for_approval: { tone: "bg-warning", textTone: "text-warning", labelKey: "waiting_for_approval", animate: false, usable: false },
14178
- blocked_by_org_policy: { tone: "bg-destructive", textTone: "text-destructive", labelKey: "blocked_by_org_policy", animate: false, usable: false },
14179
- blocked_by_os_permission: { tone: "bg-destructive", textTone: "text-destructive", labelKey: "blocked_by_os_permission", animate: false, usable: false },
14180
- blocked_by_device_policy: { tone: "bg-destructive", textTone: "text-destructive", labelKey: "blocked_by_device_policy", animate: false, usable: false },
14181
- resource_unavailable: { tone: "bg-warning", textTone: "text-warning", labelKey: "resource_unavailable", animate: false, usable: false },
14182
- upgrade_required: { tone: "bg-destructive", textTone: "text-destructive", labelKey: "upgrade_required", animate: false, usable: false },
14183
- ready: { tone: "bg-success", textTone: "text-foreground", labelKey: "ready", animate: false, usable: true },
14184
- executing: { tone: "bg-success", textTone: "text-foreground", labelKey: "executing", animate: true, usable: true },
14185
- degraded: { tone: "bg-warning", textTone: "text-warning", labelKey: "degraded", animate: false, usable: true }
14186
- };
14187
- function describeDesktopHealth(health) {
14188
- return health && PRESENTATION[health] || PRESENTATION.desktop_offline;
14189
- }
14190
- function isDeviceReachable(health) {
14191
- if (!health) return false;
14192
- return health !== "desktop_offline" && health !== "connecting" && health !== "upgrade_required";
14193
- }
14194
-
14195
- // src/vendor/ui/desktop-access/utils/desktop-labels.ts
14196
- var desktopLabelKey = {
14197
- resourceKind: (kind) => `desktopAccess.resourceKind.${kind}`,
14198
- operation: (op) => `desktopAccess.operation.${op}`,
14199
- scope: (scope) => `desktopAccess.scope.${scope}`,
14200
- writeMode: (mode) => `desktopAccess.writeMode.${mode}`,
14201
- riskClass: (risk) => `desktopAccess.risk.${risk}`,
14202
- capabilityClass: (cls) => `desktopAccess.capability.${cls}`,
14203
- /** `desktop.files.read` → `desktopAccess.tool.files.read` */
14204
- capability: (name) => `desktopAccess.tool.${name.replace(/^desktop\./, "")}`,
14205
- health: (health) => `desktopAccess.health.${health}`,
14206
- trust: (trust) => `desktopAccess.trust.${trust}`,
14207
- requestStatus: (status) => `desktopAccess.request.status.${status}`,
14208
- osPermission: (kind) => `desktopAccess.osPermission.${kind}`,
14209
- osPermissionState: (state) => `desktopAccess.osPermissionState.${state}`,
14210
- auditKind: (kind) => `desktopAccess.audit.kind.${kind}`
14211
- };
14212
- function capabilityFromToolName(toolName) {
14213
- if (!toolName.startsWith("local_")) return null;
14214
- const rest = toolName.slice("local_".length);
14215
- const [group, ...action] = rest.split("_");
14216
- if (!group || action.length === 0) return null;
14217
- const family = group === "command" ? "commands" : group;
14218
- return `desktop.${family}.${action.join("_")}`;
14219
- }
14220
- function isDesktopAccessTool(toolName) {
14221
- return capabilityFromToolName(toolName) !== null;
14222
- }
14223
- function formatBytes(bytes) {
14224
- if (bytes == null || !Number.isFinite(bytes) || bytes < 0) return null;
14225
- if (bytes < 1024) return `${bytes} B`;
14226
- const units = ["KB", "MB", "GB", "TB"];
14227
- let value = bytes / 1024;
14228
- let unit2 = 0;
14229
- while (value >= 1024 && unit2 < units.length - 1) {
14230
- value /= 1024;
14231
- unit2 += 1;
14232
- }
14233
- return `${value < 10 ? value.toFixed(1) : Math.round(value)} ${units[unit2]}`;
14234
- }
14235
- function shortRevision(revision) {
14236
- if (!revision) return null;
14237
- return revision.slice(0, 8);
14238
- }
14239
- function DecisionCard({
14240
- tone = "idle",
14241
- icon,
14242
- kind,
14243
- question,
14244
- detail,
14245
- note,
14246
- actions,
14247
- settled,
14248
- children,
14249
- className,
14250
- testId
14251
- }) {
14252
- const toneClass = tone === "ok" ? "text-success" : tone === "warn" ? "text-warning" : tone === "danger" ? "text-destructive" : "text-muted-foreground";
14253
- return /* @__PURE__ */ jsxs(
14254
- "div",
14255
- {
14256
- "data-testid": testId,
14257
- "data-tone": tone,
14258
- "data-settled": settled != null ? "" : void 0,
14259
- className: cn(
14260
- "relative mt-2 min-w-0 rounded-xl border border-border bg-surface-raised px-4 py-3",
14261
- className
14262
- ),
14263
- children: [
14264
- /* @__PURE__ */ jsxs("div", { className: cn("flex min-w-0 items-center gap-1.5 text-xs", toneClass), children: [
14265
- /* @__PURE__ */ jsx("span", { className: "flex size-4 shrink-0 items-center justify-center [&_svg]:size-3.5", children: icon ?? /* @__PURE__ */ jsx(DeviceDesktopIcon, { "aria-hidden": true }) }),
14266
- /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: kind }),
14267
- settled != null && /* @__PURE__ */ jsx("span", { className: "ml-auto min-w-0 truncate text-right text-foreground", "data-testid": testId ? `${testId}-settled` : void 0, children: settled })
14268
- ] }),
14269
- settled == null && /* @__PURE__ */ jsxs(Fragment, { children: [
14270
- question != null && /* @__PURE__ */ jsx("p", { className: "mt-1 break-words text-sm text-foreground", children: question }),
14271
- detail != null && /* @__PURE__ */ jsx("div", { className: "mt-2 min-w-0 overflow-hidden whitespace-pre-wrap break-all rounded-md bg-surface-inset px-3 py-2 font-mono text-xs leading-relaxed text-foreground/90", children: detail }),
14272
- children,
14273
- note != null && /* @__PURE__ */ jsx("p", { className: "mt-2 break-words text-xs text-muted-foreground", children: note }),
14274
- actions != null && /* @__PURE__ */ jsx("div", { className: "mt-3 flex flex-wrap items-center justify-end gap-2", children: actions })
14275
- ] }),
14276
- settled != null && children
14277
- ]
14278
- }
14279
- );
14280
- }
14281
- function Kbd({ className, ...props }) {
14282
- return /* @__PURE__ */ jsx(
14283
- "kbd",
14284
- {
14285
- "data-slot": "kbd",
14286
- className: cn(
14287
- "pointer-events-none inline-flex h-4 min-w-4 select-none items-center justify-center rounded border border-border bg-surface-inset px-1 font-mono text-[10px] font-medium leading-none text-muted-foreground",
14288
- className
14289
- ),
14290
- ...props
14291
- }
14292
- );
14293
- }
14294
- function DecisionSecondaryButton({
14295
- label,
14296
- onClick,
14297
- disabled,
14298
- "aria-label": ariaLabel
14299
- }) {
14300
- return /* @__PURE__ */ jsxs(Button, { size: "sm", variant: "outline", disabled, onClick, "aria-label": ariaLabel, "data-slot": "decision-secondary", children: [
14301
- label,
14302
- /* @__PURE__ */ jsx(Kbd, { "aria-hidden": true, className: "hidden md:inline-flex", children: "Esc" })
14303
- ] });
14304
- }
14305
- function DecisionSplitButton({
14306
- label,
14307
- onClick,
14308
- disabled,
14309
- variant = "default",
14310
- options,
14311
- menuLabel,
14312
- hint = "enter",
14313
- className,
14314
- "aria-label": ariaLabel
14315
- }) {
14316
- const { t } = useTranslation();
14317
- const hasMenu = !!options && options.length > 0;
14318
- const kbdClass = variant === "default" || variant === "default-neutral" ? "hidden md:inline-flex border-primary-foreground/30 bg-transparent text-primary-foreground/80" : "hidden md:inline-flex bg-transparent";
14319
- const main = /* @__PURE__ */ jsxs(
14320
- Button,
14321
- {
14322
- size: "sm",
14323
- variant,
14324
- disabled,
14325
- onClick,
14326
- "aria-label": ariaLabel,
14327
- className: cn(hasMenu && "rounded-r-none", className),
14328
- "data-slot": "decision-primary",
14329
- children: [
14330
- label,
14331
- hint === "enter" && /* @__PURE__ */ jsx(Kbd, { "aria-hidden": true, className: kbdClass, children: "\u23CE" })
14332
- ]
14333
- }
14334
- );
14335
- if (!hasMenu) return main;
14336
- return /* @__PURE__ */ jsxs("div", { className: "inline-flex items-stretch", "data-slot": "decision-split-button", children: [
14337
- main,
14338
- /* @__PURE__ */ jsxs(DropdownMenu, { children: [
14339
- /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
14340
- Button,
14341
- {
14342
- size: "sm",
14343
- variant,
14344
- disabled,
14345
- "aria-label": menuLabel ?? t("desktopAccess.action.moreOptions"),
14346
- className: "rounded-l-none border-l border-l-background/25 px-1.5",
14347
- "data-slot": "decision-menu-trigger",
14348
- children: /* @__PURE__ */ jsx(ChevronDownMediumIcon, { "aria-hidden": true })
14349
- }
14350
- ) }),
14351
- /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", className: "min-w-56", children: /* @__PURE__ */ jsx(TooltipProvider, { children: options.map(
14352
- (option) => option.disabled && option.disabledReason != null ? /* @__PURE__ */ jsxs(Tooltip, { children: [
14353
- /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "block", children: /* @__PURE__ */ jsx(DropdownMenuItem, { disabled: true, "data-selected": option.selected ? "" : void 0, children: option.label }) }) }),
14354
- /* @__PURE__ */ jsx(TooltipContent, { side: "left", children: option.disabledReason })
14355
- ] }, option.id) : /* @__PURE__ */ jsx(
14356
- DropdownMenuItem,
14357
- {
14358
- disabled: option.disabled,
14359
- "data-selected": option.selected ? "" : void 0,
14360
- className: "data-[selected]:font-medium",
14361
- onSelect: () => option.onSelect(),
14362
- children: option.label
14363
- },
14364
- option.id
14365
- )
14366
- ) }) })
14367
- ] })
14368
- ] });
14369
- }
14370
- var registry = [];
14371
- var listening = false;
14372
- function isTypingTarget(target) {
14373
- if (!(target instanceof HTMLElement)) return false;
14374
- if (target.isContentEditable) return true;
14375
- const tag = target.tagName;
14376
- if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
14377
- return !!target.closest('[role="menu"], [role="dialog"], [role="listbox"], [contenteditable="true"]');
14378
- }
14379
- function onKeyDown(event) {
14380
- if (event.defaultPrevented || event.isComposing) return;
14381
- if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return;
14382
- if (event.key !== "Enter" && event.key !== "Escape") return;
14383
- const owner = registry[registry.length - 1];
14384
- if (!owner) return;
14385
- if (isTypingTarget(event.target) || isTypingTarget(document.activeElement)) return;
14386
- const handler = event.key === "Enter" ? owner.handlers.current.onPrimary : owner.handlers.current.onSecondary;
14387
- if (!handler) return;
14388
- event.preventDefault();
14389
- handler();
14390
- }
14391
- function ensureListener() {
14392
- if (listening || typeof window === "undefined") return;
14393
- listening = true;
14394
- window.addEventListener("keydown", onKeyDown);
14395
- }
14396
- function releaseListener() {
14397
- if (!listening || registry.length > 0) return;
14398
- listening = false;
14399
- window.removeEventListener("keydown", onKeyDown);
14400
- }
14401
- function useDecisionShortcuts({
14402
- active,
14403
- onPrimary,
14404
- onSecondary
14405
- }) {
14406
- const id = useId();
14407
- const handlers = useRef({ onPrimary, onSecondary });
14408
- handlers.current = { onPrimary, onSecondary };
14409
- useEffect(() => {
14410
- if (!active) return;
14411
- const entry = { id, handlers };
14412
- registry.push(entry);
14413
- ensureListener();
14414
- return () => {
14415
- const at = registry.indexOf(entry);
14416
- if (at !== -1) registry.splice(at, 1);
14417
- releaseListener();
14418
- };
14419
- }, [active, id]);
13861
+ function useDecisionShortcuts({
13862
+ active,
13863
+ onPrimary,
13864
+ onSecondary
13865
+ }) {
13866
+ const id = useId();
13867
+ const handlers = useRef({ onPrimary, onSecondary });
13868
+ handlers.current = { onPrimary, onSecondary };
13869
+ useEffect(() => {
13870
+ if (!active) return;
13871
+ const entry = { id, handlers };
13872
+ registry.push(entry);
13873
+ ensureListener();
13874
+ return () => {
13875
+ const at = registry.indexOf(entry);
13876
+ if (at !== -1) registry.splice(at, 1);
13877
+ releaseListener();
13878
+ };
13879
+ }, [active, id]);
14420
13880
  }
14421
13881
 
14422
13882
  // src/vendor/ui/chat/desktop-access/utils/grant-scope-options.ts
@@ -15100,9 +14560,7 @@ function MessageDecisionCards({
15100
14560
  items,
15101
14561
  conversationId,
15102
14562
  isStreamingMessage,
15103
- hasLaterStepsGroup = false,
15104
- onToolApproval,
15105
- showInlineToolApprovals = false
14563
+ hasLaterStepsGroup = false
15106
14564
  }) {
15107
14565
  const toolItems = items.filter(
15108
14566
  (i) => i.type === "tool"
@@ -15110,22 +14568,6 @@ function MessageDecisionCards({
15110
14568
  const { data: pendingStatuses } = useSessionPendingStatuses(conversationId);
15111
14569
  const showLiveDesktopApprovals = isStreamingMessage && !hasLaterStepsGroup && !!conversationId;
15112
14570
  const desktopAccessStatus = useDesktopStatus(conversationId, { enabled: showLiveDesktopApprovals });
15113
- const renderToolApprovals = showInlineToolApprovals && !!onToolApproval;
15114
- const autoApprovedRef = useRef(/* @__PURE__ */ new Set());
15115
- useEffect(() => {
15116
- if (!renderToolApprovals || !onToolApproval) return;
15117
- for (const item of toolItems) {
15118
- if (!isApprovalRequested(item.part)) continue;
15119
- const approvalId = getApprovalId(item.part);
15120
- const toolName = getToolName(item.part);
15121
- if (approvalId && isToolAlwaysAllowed(toolName) && !autoApprovedRef.current.has(approvalId)) {
15122
- autoApprovedRef.current.add(approvalId);
15123
- void onToolApproval(approvalId, true).catch(() => {
15124
- autoApprovedRef.current.delete(approvalId);
15125
- });
15126
- }
15127
- }
15128
- }, [toolItems, onToolApproval, renderToolApprovals]);
15129
14571
  const seenInstalls = /* @__PURE__ */ new Set();
15130
14572
  const seenPendingCalls = /* @__PURE__ */ new Set();
15131
14573
  const seenDesktopRequests = /* @__PURE__ */ new Set();
@@ -15137,23 +14579,6 @@ function MessageDecisionCards({
15137
14579
  seenRoutines.add(signal.routineId);
15138
14580
  return /* @__PURE__ */ jsx(AgentRoutineDraftCard, { ...signal }, `${messageId}-routine-${signal.routineId}`);
15139
14581
  }),
15140
- renderToolApprovals && onToolApproval && toolItems.map((item) => {
15141
- if (!isApprovalRequested(item.part)) return null;
15142
- if (isRequestHumanTool(getToolName(item.part))) return null;
15143
- const aid = getApprovalId(item.part);
15144
- if (!aid || autoApprovedRef.current.has(aid)) return null;
15145
- return /* @__PURE__ */ jsx(
15146
- ToolApprovalCard,
15147
- {
15148
- part: item.part,
15149
- conversationId,
15150
- onApprove: (id) => onToolApproval(id, true),
15151
- onDeny: (id) => onToolApproval(id, false),
15152
- onAlwaysAllow: (toolName) => addAlwaysAllowedTool(toolName)
15153
- },
15154
- `${messageId}-approval-${item.index}`
15155
- );
15156
- }),
15157
14582
  toolItems.map((item) => {
15158
14583
  const nc = getNeedsConnect(item.part);
15159
14584
  if (!nc?.pendingToolCallId || !nc.installId || !nc.capabilityId) return null;
@@ -16002,113 +15427,527 @@ function ChatImageLightbox({
16002
15427
  }
16003
15428
  );
16004
15429
  }
16005
- function getHost(url) {
16006
- try {
16007
- return new URL(url).hostname.replace(/^www\./, "");
16008
- } catch {
16009
- return "source";
16010
- }
15430
+ function getHost(url) {
15431
+ try {
15432
+ return new URL(url).hostname.replace(/^www\./, "");
15433
+ } catch {
15434
+ return "source";
15435
+ }
15436
+ }
15437
+ function getImageView(image, failedImageUrls) {
15438
+ const linkUrl = image.sourceUrl ?? image.url;
15439
+ const label = image.title ?? image.sourceName ?? getHost(linkUrl);
15440
+ const imageUrl = image.thumbnailUrl && !failedImageUrls.has(image.thumbnailUrl) ? image.thumbnailUrl : !failedImageUrls.has(image.url) ? image.url : null;
15441
+ if (!imageUrl) return null;
15442
+ return {
15443
+ image,
15444
+ imageUrl,
15445
+ fullImageUrl: failedImageUrls.has(image.url) ? imageUrl : image.url,
15446
+ label
15447
+ };
15448
+ }
15449
+ function ImageResultsGallery({
15450
+ images,
15451
+ className,
15452
+ label = "Image results",
15453
+ size = "regular"
15454
+ }) {
15455
+ const [selectedIndex, setSelectedIndex] = useState(null);
15456
+ const [failedImageUrls, setFailedImageUrls] = useState(() => /* @__PURE__ */ new Set());
15457
+ const visible = images.slice(0, 6);
15458
+ const imageViews = visible.flatMap((image) => {
15459
+ const imageView = getImageView(image, failedImageUrls);
15460
+ return imageView ? [imageView] : [];
15461
+ });
15462
+ const lightboxItems = imageViews.map((view, index) => ({
15463
+ kind: "remote",
15464
+ id: `image-result-${index}-${view.image.url}`,
15465
+ title: view.label,
15466
+ imageUrl: view.fullImageUrl,
15467
+ thumbnailUrl: view.imageUrl
15468
+ }));
15469
+ const selectedId = selectedIndex === null ? null : lightboxItems[selectedIndex]?.id ?? null;
15470
+ const isCompact = size === "compact";
15471
+ if (imageViews.length === 0) return null;
15472
+ function markImageFailed(url) {
15473
+ setFailedImageUrls((current2) => {
15474
+ if (current2.has(url)) return current2;
15475
+ const next = new Set(current2);
15476
+ next.add(url);
15477
+ return next;
15478
+ });
15479
+ }
15480
+ return /* @__PURE__ */ jsxs("div", { className: cn("not-prose mt-3 mb-5", className), children: [
15481
+ /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center gap-2 text-xs font-medium text-muted-foreground", children: [
15482
+ /* @__PURE__ */ jsx(Images1Icon, { className: "size-3.5" }),
15483
+ /* @__PURE__ */ jsx("span", { children: label })
15484
+ ] }),
15485
+ /* @__PURE__ */ jsx(
15486
+ "div",
15487
+ {
15488
+ className: cn(
15489
+ "grid gap-2",
15490
+ isCompact ? "grid-cols-[repeat(auto-fill,minmax(112px,160px))]" : "grid-cols-2 md:grid-cols-3"
15491
+ ),
15492
+ children: imageViews.map(({ image, imageUrl, label: imageLabel }, index) => /* @__PURE__ */ jsx(
15493
+ "button",
15494
+ {
15495
+ type: "button",
15496
+ "aria-label": `Preview image: ${imageLabel}`,
15497
+ onClick: (event) => {
15498
+ event.stopPropagation();
15499
+ setSelectedIndex(index);
15500
+ },
15501
+ className: cn(
15502
+ "group/image-result cursor-pointer overflow-hidden border border-border bg-surface-raised text-left text-foreground transition-colors hover:border-primary/50 hover:bg-surface-raised-hover focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50",
15503
+ isCompact ? "rounded-md" : "rounded-lg"
15504
+ ),
15505
+ children: /* @__PURE__ */ jsx("div", { className: "aspect-[4/3] overflow-hidden bg-surface-inset", children: /* @__PURE__ */ jsx(
15506
+ "img",
15507
+ {
15508
+ src: imageUrl,
15509
+ alt: imageLabel,
15510
+ loading: "lazy",
15511
+ onError: () => markImageFailed(imageUrl),
15512
+ className: "image-checker h-full w-full object-cover transition-transform [--image-checker-tile:8px] group-hover/image-result:scale-[1.02]"
15513
+ }
15514
+ ) })
15515
+ },
15516
+ `${image.url}-${index}`
15517
+ ))
15518
+ }
15519
+ ),
15520
+ /* @__PURE__ */ jsx(
15521
+ ChatImageLightbox,
15522
+ {
15523
+ items: lightboxItems,
15524
+ selectedId,
15525
+ onSelectedIdChange: (id) => {
15526
+ if (id === null) {
15527
+ setSelectedIndex(null);
15528
+ return;
15529
+ }
15530
+ const index = lightboxItems.findIndex((item) => item.id === id);
15531
+ setSelectedIndex(index >= 0 ? index : null);
15532
+ },
15533
+ onRemoteImageError: markImageFailed
15534
+ }
15535
+ )
15536
+ ] });
15537
+ }
15538
+ var DOMAIN7 = "conversation-desktop";
15539
+ var conversationDesktopKeys = {
15540
+ all: [DOMAIN7],
15541
+ status: (conversationId) => [DOMAIN7, "status", conversationId]
15542
+ };
15543
+ function conversationDesktop(client, conversationId) {
15544
+ return client.v1.conversations({ conversationId });
15545
+ }
15546
+ async function fetchDesktopStatus(client, conversationId, signal) {
15547
+ const { data, error } = await conversationDesktop(client, conversationId).desktop.get({
15548
+ fetch: { signal }
15549
+ });
15550
+ if (error) throw error;
15551
+ return unwrapData(data);
15552
+ }
15553
+ async function fetchDesktopScreenshot(client, conversationId, maxDim, signal) {
15554
+ const res = await conversationDesktop(client, conversationId).desktop.screenshot.get({
15555
+ query: { maxDim: String(maxDim) },
15556
+ fetch: { signal }
15557
+ });
15558
+ if (res.error) {
15559
+ const status = res.error.status;
15560
+ const value = res.error.value;
15561
+ if (status === 503 || isWakingBody(value) || isWakingBody(res.data)) {
15562
+ return { ok: false, waking: true };
15563
+ }
15564
+ throw res.error;
15565
+ }
15566
+ const payload = res.data;
15567
+ if (isWakingBody(payload)) return { ok: false, waking: true };
15568
+ const blob = desktopScreenshotBlob(payload);
15569
+ return { ok: true, blob };
15570
+ }
15571
+ function isWakingBody(value) {
15572
+ return Boolean(
15573
+ value && typeof value === "object" && "waking" in value && value.waking === true
15574
+ );
15575
+ }
15576
+ function desktopScreenshotBlob(payload) {
15577
+ if (payload instanceof Blob) return payload;
15578
+ if (payload instanceof ArrayBuffer) {
15579
+ return new Blob([payload], { type: "image/png" });
15580
+ }
15581
+ if (ArrayBuffer.isView(payload)) {
15582
+ const view = payload;
15583
+ const copy3 = new Uint8Array(view.byteLength);
15584
+ copy3.set(new Uint8Array(view.buffer, view.byteOffset, view.byteLength));
15585
+ return new Blob([copy3], { type: "image/png" });
15586
+ }
15587
+ throw new Error("Desktop screenshot was not an image");
15588
+ }
15589
+ function useConversationDesktopStatus(conversationId) {
15590
+ const client = useClient();
15591
+ return useQuery({
15592
+ queryKey: conversationDesktopKeys.status(conversationId ?? ""),
15593
+ queryFn: () => fetchDesktopStatus(client, conversationId),
15594
+ enabled: !!conversationId,
15595
+ staleTime: Infinity,
15596
+ gcTime: 5 * 60 * 1e3,
15597
+ retry: false,
15598
+ refetchOnWindowFocus: false,
15599
+ refetchOnReconnect: false
15600
+ });
15601
+ }
15602
+ var useDesktopScreenStore = create((set2, get3) => ({
15603
+ openConversationId: null,
15604
+ lightboxOpen: false,
15605
+ agentDisplayName: null,
15606
+ open: (conversationId) => set2({ openConversationId: conversationId, lightboxOpen: false }),
15607
+ close: () => set2({ openConversationId: null, lightboxOpen: false }),
15608
+ toggle: (conversationId) => {
15609
+ if (get3().openConversationId === conversationId) {
15610
+ set2({ openConversationId: null, lightboxOpen: false });
15611
+ return;
15612
+ }
15613
+ set2({ openConversationId: conversationId, lightboxOpen: false });
15614
+ },
15615
+ setLightboxOpen: (lightboxOpen) => set2({ lightboxOpen }),
15616
+ setAgentDisplayName: (agentDisplayName) => set2({ agentDisplayName })
15617
+ }));
15618
+ function DesktopScreenThumbnail({
15619
+ imageUrl,
15620
+ state,
15621
+ onOpen
15622
+ }) {
15623
+ const { t } = useTranslation();
15624
+ const agentDisplayName = useDesktopScreenStore((s2) => s2.agentDisplayName);
15625
+ const caption = agentDisplayName ? t("chat.desktop.agentsScreen", { name: agentDisplayName }) : null;
15626
+ return /* @__PURE__ */ jsxs("div", { children: [
15627
+ /* @__PURE__ */ jsxs("div", { className: "group relative aspect-video overflow-hidden rounded-xl", children: [
15628
+ imageUrl ? (
15629
+ // eslint-disable-next-line @next/next/no-img-element -- blob: screenshot URLs
15630
+ /* @__PURE__ */ jsx(
15631
+ "img",
15632
+ {
15633
+ src: imageUrl,
15634
+ alt: caption ?? t("chat.desktop.screen"),
15635
+ className: "absolute inset-0 size-full object-cover"
15636
+ }
15637
+ )
15638
+ ) : /* @__PURE__ */ jsx(Skeleton, { className: "absolute inset-0 size-full rounded-none" }),
15639
+ /* @__PURE__ */ jsx(
15640
+ "div",
15641
+ {
15642
+ className: cn(
15643
+ "absolute inset-0 flex items-center justify-center bg-scrim/30 opacity-0 transition-opacity duration-150 group-hover:opacity-100",
15644
+ "focus-within:opacity-100"
15645
+ ),
15646
+ children: /* @__PURE__ */ jsx(
15647
+ Button,
15648
+ {
15649
+ type: "button",
15650
+ variant: "default-neutral",
15651
+ size: "sm",
15652
+ className: "rounded-full px-4",
15653
+ onClick: onOpen,
15654
+ children: t("chat.desktop.open")
15655
+ }
15656
+ )
15657
+ }
15658
+ ),
15659
+ state === "off" && !imageUrl ? /* @__PURE__ */ jsx("span", { className: "sr-only", children: t("chat.desktop.asleep") }) : null
15660
+ ] }),
15661
+ caption ? /* @__PURE__ */ jsx("p", { className: "mt-2 text-center text-xs text-muted-foreground", children: caption }) : null
15662
+ ] });
15663
+ }
15664
+
15665
+ // src/vendor/ui/chat/desktop-screen-panel/types.ts
15666
+ var DESKTOP_SCREEN_POLL_MS = 2500;
15667
+ var DESKTOP_THUMBNAIL_MAX_DIM = 640;
15668
+
15669
+ // src/vendor/ui/chat/desktop-screen-panel/hooks/use-desktop-screen-poll.ts
15670
+ function tabIsVisible() {
15671
+ if (typeof document === "undefined") return true;
15672
+ return document.visibilityState === "visible";
15673
+ }
15674
+ function useDesktopScreenPoll({
15675
+ conversationId,
15676
+ open,
15677
+ maxDim,
15678
+ fetchers,
15679
+ pollIntervalMs = DESKTOP_SCREEN_POLL_MS
15680
+ }) {
15681
+ const [state, setState] = useState("off");
15682
+ const [imageUrl, setImageUrl] = useState(null);
15683
+ const [reconnecting, setReconnecting] = useState(false);
15684
+ const [display, setDisplay] = useState();
15685
+ const [detail, setDetail] = useState();
15686
+ const imageUrlRef = useRef(null);
15687
+ const fetchersRef = useRef(fetchers);
15688
+ fetchersRef.current = fetchers;
15689
+ useEffect(() => {
15690
+ imageUrlRef.current = imageUrl;
15691
+ }, [imageUrl]);
15692
+ useEffect(() => {
15693
+ if (!open) {
15694
+ setState("off");
15695
+ setReconnecting(false);
15696
+ setDisplay(void 0);
15697
+ const previous = imageUrlRef.current;
15698
+ if (previous) {
15699
+ URL.revokeObjectURL(previous);
15700
+ imageUrlRef.current = null;
15701
+ setImageUrl(null);
15702
+ }
15703
+ }
15704
+ }, [open]);
15705
+ useEffect(() => {
15706
+ if (!open || !conversationId) return;
15707
+ let cancelled2 = false;
15708
+ let inFlight = false;
15709
+ const controller = new AbortController();
15710
+ const tick = async () => {
15711
+ if (cancelled2 || inFlight || !tabIsVisible()) return;
15712
+ inFlight = true;
15713
+ try {
15714
+ const status = await fetchersRef.current.fetchStatus(
15715
+ conversationId,
15716
+ controller.signal
15717
+ );
15718
+ if (cancelled2) return;
15719
+ setDisplay(status.display);
15720
+ setDetail(status.detail);
15721
+ if (!status.enabled) {
15722
+ setState("off");
15723
+ setReconnecting(false);
15724
+ return;
15725
+ }
15726
+ if (status.state === "unavailable" || status.state === "upgradable") {
15727
+ setState(status.state);
15728
+ setReconnecting(false);
15729
+ return;
15730
+ }
15731
+ const shot = await fetchersRef.current.fetchScreenshot(
15732
+ conversationId,
15733
+ maxDim,
15734
+ controller.signal
15735
+ );
15736
+ if (cancelled2) return;
15737
+ if (!shot.ok) {
15738
+ setState("waking");
15739
+ setReconnecting(false);
15740
+ return;
15741
+ }
15742
+ const nextUrl = URL.createObjectURL(shot.blob);
15743
+ const previous = imageUrlRef.current;
15744
+ imageUrlRef.current = nextUrl;
15745
+ setImageUrl(nextUrl);
15746
+ if (previous) URL.revokeObjectURL(previous);
15747
+ setState("live");
15748
+ setReconnecting(false);
15749
+ } catch (error) {
15750
+ if (cancelled2) return;
15751
+ if (controller.signal.aborted) return;
15752
+ if (error instanceof DOMException && error.name === "AbortError") return;
15753
+ if (imageUrlRef.current) {
15754
+ setReconnecting(true);
15755
+ } else {
15756
+ setState("off");
15757
+ }
15758
+ } finally {
15759
+ inFlight = false;
15760
+ }
15761
+ };
15762
+ const onVisibility = () => {
15763
+ if (tabIsVisible()) void tick();
15764
+ };
15765
+ void tick();
15766
+ const interval = setInterval(() => {
15767
+ void tick();
15768
+ }, pollIntervalMs);
15769
+ document.addEventListener("visibilitychange", onVisibility);
15770
+ return () => {
15771
+ cancelled2 = true;
15772
+ controller.abort();
15773
+ clearInterval(interval);
15774
+ document.removeEventListener("visibilitychange", onVisibility);
15775
+ };
15776
+ }, [open, conversationId, maxDim, pollIntervalMs]);
15777
+ useEffect(() => {
15778
+ return () => {
15779
+ const previous = imageUrlRef.current;
15780
+ if (previous) URL.revokeObjectURL(previous);
15781
+ };
15782
+ }, []);
15783
+ return { state, imageUrl, reconnecting, display, detail };
15784
+ }
15785
+ var CARD_POLL_MS = 5e3;
15786
+ function RequestHumanApprovalCard({
15787
+ conversationId,
15788
+ title,
15789
+ phase,
15790
+ onApprove,
15791
+ onDeny
15792
+ }) {
15793
+ const { t } = useTranslation();
15794
+ const client = useClient();
15795
+ const desktop = useConversationDesktopStatus(conversationId);
15796
+ const setLightboxOpen = useDesktopScreenStore((s2) => s2.setLightboxOpen);
15797
+ const openPanel = useDesktopScreenStore((s2) => s2.open);
15798
+ const fetchers = useMemo(
15799
+ () => ({
15800
+ fetchStatus: (id, signal) => fetchDesktopStatus(client, id, signal),
15801
+ fetchScreenshot: (id, maxDim, signal) => fetchDesktopScreenshot(client, id, maxDim, signal)
15802
+ }),
15803
+ [client]
15804
+ );
15805
+ const poll = useDesktopScreenPoll({
15806
+ conversationId,
15807
+ open: phase === "pending" && !!conversationId && desktop.data?.enabled === true,
15808
+ maxDim: DESKTOP_THUMBNAIL_MAX_DIM,
15809
+ fetchers,
15810
+ pollIntervalMs: CARD_POLL_MS
15811
+ });
15812
+ const heading = t("chat.desktop.computer");
15813
+ const badge = phase === "pending" ? {
15814
+ label: t("chat.desktop.actionNeeded"),
15815
+ className: "bg-warning/10 text-warning"} : phase === "done" ? {
15816
+ label: t("chat.desktop.done"),
15817
+ className: "bg-success/10 text-success"} : {
15818
+ label: t("chat.desktop.skipped"),
15819
+ className: "bg-surface-inset text-muted-foreground"};
15820
+ if (phase !== "pending") {
15821
+ return /* @__PURE__ */ jsxs(
15822
+ "div",
15823
+ {
15824
+ "data-slot": "request-human-approval-card",
15825
+ "data-phase": phase,
15826
+ className: "relative my-2 flex items-center gap-2 rounded-xl border border-border bg-surface px-4 py-2",
15827
+ children: [
15828
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm font-semibold text-foreground", children: heading }),
15829
+ /* @__PURE__ */ jsx(
15830
+ "span",
15831
+ {
15832
+ className: cn(
15833
+ "shrink-0 rounded-full px-2 py-0.5 text-xs font-medium",
15834
+ badge.className
15835
+ ),
15836
+ children: badge.label
15837
+ }
15838
+ ),
15839
+ title ? /* @__PURE__ */ jsx("p", { className: "min-w-0 flex-1 truncate text-sm text-foreground", children: title }) : /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1" }),
15840
+ /* @__PURE__ */ jsxs(
15841
+ Button,
15842
+ {
15843
+ type: "button",
15844
+ variant: "outline",
15845
+ size: "sm",
15846
+ className: "shrink-0",
15847
+ onClick: () => {
15848
+ if (conversationId) openPanel(conversationId);
15849
+ setLightboxOpen(true);
15850
+ },
15851
+ children: [
15852
+ /* @__PURE__ */ jsx(DeviceDesktopIcon, { className: "size-3.5" }),
15853
+ t("chat.desktop.openComputer")
15854
+ ]
15855
+ }
15856
+ )
15857
+ ]
15858
+ }
15859
+ );
15860
+ }
15861
+ return /* @__PURE__ */ jsxs(
15862
+ "div",
15863
+ {
15864
+ "data-slot": "request-human-approval-card",
15865
+ "data-phase": phase,
15866
+ className: "relative my-2 rounded-xl border border-border bg-surface p-4",
15867
+ children: [
15868
+ /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center gap-2", children: [
15869
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-foreground", children: heading }),
15870
+ /* @__PURE__ */ jsxs(
15871
+ "span",
15872
+ {
15873
+ className: cn(
15874
+ "ml-auto inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-xs font-medium",
15875
+ badge.className
15876
+ ),
15877
+ children: [
15878
+ /* @__PURE__ */ jsx("span", { className: "size-1.5 rounded-full bg-warning animate-pulse", "aria-hidden": true }),
15879
+ badge.label
15880
+ ]
15881
+ }
15882
+ )
15883
+ ] }),
15884
+ title ? /* @__PURE__ */ jsx("p", { className: "mb-2 truncate text-sm text-foreground", children: title }) : null,
15885
+ /* @__PURE__ */ jsx("div", { className: "mb-4", children: /* @__PURE__ */ jsx(
15886
+ DesktopScreenThumbnail,
15887
+ {
15888
+ imageUrl: poll.imageUrl,
15889
+ state: poll.state,
15890
+ onOpen: () => setLightboxOpen(true)
15891
+ }
15892
+ ) }),
15893
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
15894
+ /* @__PURE__ */ jsx(
15895
+ Button,
15896
+ {
15897
+ type: "button",
15898
+ variant: "default",
15899
+ onClick: () => setLightboxOpen(true),
15900
+ children: t("chat.desktop.takeOver")
15901
+ }
15902
+ ),
15903
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: onApprove, children: t("chat.desktop.imDone") }),
15904
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", className: "ml-auto", onClick: onDeny, children: t("chat.desktop.skip") })
15905
+ ] })
15906
+ ]
15907
+ }
15908
+ );
15909
+ }
15910
+
15911
+ // src/vendor/ui/chat/request-human-approval-card/utils.ts
15912
+ var REQUEST_HUMAN_TOOL = "request_human";
15913
+ function isRequestHumanTool(name) {
15914
+ return name === REQUEST_HUMAN_TOOL;
16011
15915
  }
16012
- function getImageView(image, failedImageUrls) {
16013
- const linkUrl = image.sourceUrl ?? image.url;
16014
- const label = image.title ?? image.sourceName ?? getHost(linkUrl);
16015
- const imageUrl = image.thumbnailUrl && !failedImageUrls.has(image.thumbnailUrl) ? image.thumbnailUrl : !failedImageUrls.has(image.url) ? image.url : null;
16016
- if (!imageUrl) return null;
16017
- return {
16018
- image,
16019
- imageUrl,
16020
- fullImageUrl: failedImageUrls.has(image.url) ? imageUrl : image.url,
16021
- label
16022
- };
15916
+ function requestHumanTitle(input, fallback = "") {
15917
+ if (input && typeof input === "object") {
15918
+ const record = input;
15919
+ for (const key of ["title", "message", "reason", "prompt"]) {
15920
+ const value = record[key];
15921
+ if (typeof value === "string" && value.trim()) return value.trim();
15922
+ }
15923
+ }
15924
+ return fallback;
16023
15925
  }
16024
- function ImageResultsGallery({
16025
- images,
16026
- className,
16027
- label = "Image results",
16028
- size = "regular"
16029
- }) {
16030
- const [selectedIndex, setSelectedIndex] = useState(null);
16031
- const [failedImageUrls, setFailedImageUrls] = useState(() => /* @__PURE__ */ new Set());
16032
- const visible = images.slice(0, 6);
16033
- const imageViews = visible.flatMap((image) => {
16034
- const imageView = getImageView(image, failedImageUrls);
16035
- return imageView ? [imageView] : [];
16036
- });
16037
- const lightboxItems = imageViews.map((view, index) => ({
16038
- kind: "remote",
16039
- id: `image-result-${index}-${view.image.url}`,
16040
- title: view.label,
16041
- imageUrl: view.fullImageUrl,
16042
- thumbnailUrl: view.imageUrl
16043
- }));
16044
- const selectedId = selectedIndex === null ? null : lightboxItems[selectedIndex]?.id ?? null;
16045
- const isCompact = size === "compact";
16046
- if (imageViews.length === 0) return null;
16047
- function markImageFailed(url) {
16048
- setFailedImageUrls((current2) => {
16049
- if (current2.has(url)) return current2;
16050
- const next = new Set(current2);
16051
- next.add(url);
16052
- return next;
16053
- });
15926
+ function requestHumanPhaseFromPart(part) {
15927
+ const state = typeof part.state === "string" ? part.state : "";
15928
+ if (state === "approval-requested") return "pending";
15929
+ const approval = part.approval;
15930
+ if (approval && approval.approved === false) return "skipped";
15931
+ if (state === "output-error" || state === "output-denied" || typeof part.errorText === "string") {
15932
+ return "skipped";
16054
15933
  }
16055
- return /* @__PURE__ */ jsxs("div", { className: cn("not-prose mt-3 mb-5", className), children: [
16056
- /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center gap-2 text-xs font-medium text-muted-foreground", children: [
16057
- /* @__PURE__ */ jsx(Images1Icon, { className: "size-3.5" }),
16058
- /* @__PURE__ */ jsx("span", { children: label })
16059
- ] }),
16060
- /* @__PURE__ */ jsx(
16061
- "div",
16062
- {
16063
- className: cn(
16064
- "grid gap-2",
16065
- isCompact ? "grid-cols-[repeat(auto-fill,minmax(112px,160px))]" : "grid-cols-2 md:grid-cols-3"
16066
- ),
16067
- children: imageViews.map(({ image, imageUrl, label: imageLabel }, index) => /* @__PURE__ */ jsx(
16068
- "button",
16069
- {
16070
- type: "button",
16071
- "aria-label": `Preview image: ${imageLabel}`,
16072
- onClick: (event) => {
16073
- event.stopPropagation();
16074
- setSelectedIndex(index);
16075
- },
16076
- className: cn(
16077
- "group/image-result cursor-pointer overflow-hidden border border-border bg-surface-raised text-left text-foreground transition-colors hover:border-primary/50 hover:bg-surface-raised-hover focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50",
16078
- isCompact ? "rounded-md" : "rounded-lg"
16079
- ),
16080
- children: /* @__PURE__ */ jsx("div", { className: "aspect-[4/3] overflow-hidden bg-surface-inset", children: /* @__PURE__ */ jsx(
16081
- "img",
16082
- {
16083
- src: imageUrl,
16084
- alt: imageLabel,
16085
- loading: "lazy",
16086
- onError: () => markImageFailed(imageUrl),
16087
- className: "image-checker h-full w-full object-cover transition-transform [--image-checker-tile:8px] group-hover/image-result:scale-[1.02]"
16088
- }
16089
- ) })
16090
- },
16091
- `${image.url}-${index}`
16092
- ))
16093
- }
16094
- ),
16095
- /* @__PURE__ */ jsx(
16096
- ChatImageLightbox,
16097
- {
16098
- items: lightboxItems,
16099
- selectedId,
16100
- onSelectedIdChange: (id) => {
16101
- if (id === null) {
16102
- setSelectedIndex(null);
16103
- return;
16104
- }
16105
- const index = lightboxItems.findIndex((item) => item.id === id);
16106
- setSelectedIndex(index >= 0 ? index : null);
16107
- },
16108
- onRemoteImageError: markImageFailed
16109
- }
16110
- )
16111
- ] });
15934
+ const output = part.output ?? part.result;
15935
+ if (output && typeof output === "object") {
15936
+ const record = output;
15937
+ if (record.skipped === true || record.denied === true || record.approved === false) {
15938
+ return "skipped";
15939
+ }
15940
+ }
15941
+ if (state === "output-available" || state === "result" || output != null) return "done";
15942
+ if (state === "input-available" || state === "input-streaming" || state === "call" || state === "partial-call") {
15943
+ return "pending";
15944
+ }
15945
+ return "done";
15946
+ }
15947
+ function requestHumanThreadPhase(part, pendingStatus) {
15948
+ if (pendingStatus === "pending") return "pending";
15949
+ if (pendingStatus === "abandoned") return "skipped";
15950
+ return requestHumanPhaseFromPart(part);
16112
15951
  }
16113
15952
  function GridSpinner({ className, ...props }) {
16114
15953
  return /* @__PURE__ */ jsxs(
@@ -17085,7 +16924,6 @@ var MessageStepsGroup = memo(function MessageStepsGroup2({
17085
16924
  isStreamingMessage,
17086
16925
  hasLaterStepsGroup = false,
17087
16926
  onToolApproval,
17088
- showInlineToolApprovals = true,
17089
16927
  conversationId,
17090
16928
  hasResponseImageGroup = false
17091
16929
  }) {
@@ -17173,21 +17011,6 @@ var MessageStepsGroup = memo(function MessageStepsGroup2({
17173
17011
  const input = getToolInput(imageGenStep.part);
17174
17012
  return input?.aspectRatio ? input.aspectRatio.replace(":", " / ") : "1 / 1";
17175
17013
  })();
17176
- const autoApprovedRef = useRef(/* @__PURE__ */ new Set());
17177
- useEffect(() => {
17178
- if (!showInlineToolApprovals || !onToolApproval) return;
17179
- for (const item of toolItems) {
17180
- if (!isApprovalRequested(item.part)) continue;
17181
- const approvalId = getApprovalId(item.part);
17182
- const toolName = getToolName(item.part);
17183
- if (approvalId && isToolAlwaysAllowed(toolName) && !autoApprovedRef.current.has(approvalId)) {
17184
- autoApprovedRef.current.add(approvalId);
17185
- void onToolApproval(approvalId, true).catch(() => {
17186
- autoApprovedRef.current.delete(approvalId);
17187
- });
17188
- }
17189
- }
17190
- }, [toolItems, onToolApproval, showInlineToolApprovals]);
17191
17014
  const AUTO_CLOSE_DELAY = 1e3;
17192
17015
  const expandedByDefault = useChatOptInEnabled("expandedWorkDetails");
17193
17016
  const [collapsed, setCollapsed] = useState(!expandedByDefault);
@@ -17294,34 +17117,6 @@ var MessageStepsGroup = memo(function MessageStepsGroup2({
17294
17117
  );
17295
17118
  }
17296
17119
  if (isRequestHumanTool(getToolName(item.part))) return null;
17297
- if (showInlineToolApprovals && isApprovalRequested(item.part) && onToolApproval) {
17298
- const aid = getApprovalId(item.part);
17299
- if (aid && autoApprovedRef.current.has(aid)) {
17300
- return /* @__PURE__ */ jsx(
17301
- ToolCallItem,
17302
- {
17303
- toolName: getToolName(item.part),
17304
- state: "input-available",
17305
- input: getToolInput(item.part),
17306
- output: getToolOutput(item.part),
17307
- errorText: getToolErrorText(item.part),
17308
- isMcp: isMcpToolPart(item.part)
17309
- },
17310
- `${messageId}-tc-${item.index}`
17311
- );
17312
- }
17313
- return /* @__PURE__ */ jsx(
17314
- ToolApprovalCard,
17315
- {
17316
- part: item.part,
17317
- conversationId,
17318
- onApprove: (id) => onToolApproval(id, true),
17319
- onDeny: (id) => onToolApproval(id, false),
17320
- onAlwaysAllow: (toolName) => addAlwaysAllowedTool(toolName)
17321
- },
17322
- `${messageId}-approval-${item.index}`
17323
- );
17324
- }
17325
17120
  if (getToolName(item.part) === "skill") {
17326
17121
  return /* @__PURE__ */ jsx(
17327
17122
  SkillToolItem,
@@ -17465,19 +17260,6 @@ var MessageStepsGroup = memo(function MessageStepsGroup2({
17465
17260
  const partApprovalId = getApprovalId(item.part);
17466
17261
  return partApprovalId != null && row.id === partApprovalId;
17467
17262
  });
17468
- if (showInlineToolApprovals && isApprovalRequested(item.part) && onToolApproval) {
17469
- return /* @__PURE__ */ jsx(
17470
- ToolApprovalCard,
17471
- {
17472
- part: item.part,
17473
- conversationId,
17474
- onApprove: (id) => onToolApproval(id, true),
17475
- onDeny: (id) => onToolApproval(id, false),
17476
- onAlwaysAllow: (toolName) => addAlwaysAllowedTool(toolName)
17477
- },
17478
- `${messageId}-approval-${item.index}`
17479
- );
17480
- }
17481
17263
  const phase = requestHumanThreadPhase(item.part, pendingRow?.status);
17482
17264
  if (phase === "pending" && !desktop.data?.enabled) return null;
17483
17265
  const approvalId = pendingRow?.id ?? getApprovalId(item.part);