@tangle-network/agent-app 0.43.50 → 0.43.51

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,6 +1,22 @@
1
+ import {
2
+ ATTACHMENT_ACCEPT,
3
+ ATTACHMENT_MAX_COUNT,
4
+ MAX_ATTACHMENT_TOTAL_BYTES,
5
+ MAX_BINARY_ATTACHMENT_BYTES,
6
+ MAX_TEXT_ATTACHMENT_BYTES,
7
+ attachmentSizeErrorMessage,
8
+ attachmentTotalSizeErrorMessage,
9
+ checkAttachmentType,
10
+ sanitizeAttachmentFileName,
11
+ sniffBinary
12
+ } from "./chunk-3EKOSBYL.js";
1
13
  import {
2
14
  stepActivityFlowTrace
3
15
  } from "./chunk-2QI7XV2T.js";
16
+ import {
17
+ attachmentPartsFromMessageParts,
18
+ formatBytes
19
+ } from "./chunk-JGYOYY5D.js";
4
20
  import {
5
21
  cancelStatusFor,
6
22
  fieldAcceptsFreeText,
@@ -21,7 +37,7 @@ import {
21
37
  } from "./chunk-MCJSS6SM.js";
22
38
 
23
39
  // src/web-react/index.tsx
24
- import { useEffect as useEffect8, useMemo as useMemo7, useRef as useRef8, useState as useState12, memo } from "react";
40
+ import { useEffect as useEffect10, useMemo as useMemo8, useRef as useRef9, useState as useState14, memo } from "react";
25
41
 
26
42
  // src/web-react/smooth-text.ts
27
43
  import { useEffect, useRef, useState } from "react";
@@ -1145,6 +1161,176 @@ function DurableChatCards({
1145
1161
  }) });
1146
1162
  }
1147
1163
 
1164
+ // src/web-react/message-attachments.tsx
1165
+ import { useCallback, useEffect as useEffect5, useState as useState6 } from "react";
1166
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
1167
+ function FileGlyph({ className }) {
1168
+ return /* @__PURE__ */ jsxs6("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
1169
+ /* @__PURE__ */ jsx8("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
1170
+ /* @__PURE__ */ jsx8("path", { d: "M14 2v6h6" })
1171
+ ] });
1172
+ }
1173
+ function ImageGlyph({ className }) {
1174
+ return /* @__PURE__ */ jsxs6("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
1175
+ /* @__PURE__ */ jsx8("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
1176
+ /* @__PURE__ */ jsx8("circle", { cx: "9", cy: "9", r: "2" }),
1177
+ /* @__PURE__ */ jsx8("path", { d: "m21 15-5-5L5 21" })
1178
+ ] });
1179
+ }
1180
+ function WarningGlyph({ className }) {
1181
+ return /* @__PURE__ */ jsx8("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx8("path", { d: "M12 9v4m0 4h.01M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0Z" }) });
1182
+ }
1183
+ function iconForMediaType(mediaType) {
1184
+ return mediaType?.startsWith("image/") ? ImageGlyph : FileGlyph;
1185
+ }
1186
+ var attachmentFileCache = /* @__PURE__ */ new Map();
1187
+ function __resetAttachmentFileCacheForTests() {
1188
+ attachmentFileCache.clear();
1189
+ }
1190
+ async function defaultFetchFile(url) {
1191
+ return fetch(url, { credentials: "same-origin" });
1192
+ }
1193
+ async function fetchAttachmentFile(url, fetchFile) {
1194
+ try {
1195
+ const res = await fetchFile(url);
1196
+ if (!res.ok) return { ok: false, message: `Failed to load attachment (${res.status})` };
1197
+ return { ok: true, blob: await res.blob() };
1198
+ } catch (err) {
1199
+ return { ok: false, message: err instanceof Error && err.message ? err.message : "Network error loading attachment" };
1200
+ }
1201
+ }
1202
+ function loadAttachmentFile(url, fetchFile = defaultFetchFile) {
1203
+ const cached = attachmentFileCache.get(url);
1204
+ if (cached) return cached;
1205
+ const promise = fetchAttachmentFile(url, fetchFile);
1206
+ attachmentFileCache.set(url, promise);
1207
+ void promise.then((result) => {
1208
+ if (!result.ok && attachmentFileCache.get(url) === promise) attachmentFileCache.delete(url);
1209
+ });
1210
+ return promise;
1211
+ }
1212
+ function triggerAttachmentDownload(name, blob) {
1213
+ try {
1214
+ const url = URL.createObjectURL(blob);
1215
+ const link = document.createElement("a");
1216
+ link.href = url;
1217
+ link.download = name;
1218
+ document.body.appendChild(link);
1219
+ link.click();
1220
+ link.remove();
1221
+ URL.revokeObjectURL(url);
1222
+ return { ok: true };
1223
+ } catch (err) {
1224
+ return { ok: false, message: err instanceof Error && err.message ? err.message : "Failed to download attachment" };
1225
+ }
1226
+ }
1227
+ function useAttachmentObjectUrl(blob) {
1228
+ const [url, setUrl] = useState6(null);
1229
+ useEffect5(() => {
1230
+ if (!blob) {
1231
+ setUrl(null);
1232
+ return;
1233
+ }
1234
+ const objectUrl = URL.createObjectURL(blob);
1235
+ setUrl(objectUrl);
1236
+ return () => URL.revokeObjectURL(objectUrl);
1237
+ }, [blob]);
1238
+ return url;
1239
+ }
1240
+ function AttachmentThumbnailError({ name }) {
1241
+ return /* @__PURE__ */ jsxs6("span", { className: "inline-flex h-16 w-16 shrink-0 flex-col items-center justify-center gap-1 rounded-md border border-destructive/40 bg-destructive/10 px-1 text-center text-destructive", children: [
1242
+ /* @__PURE__ */ jsx8(WarningGlyph, { className: "h-4 w-4 shrink-0" }),
1243
+ /* @__PURE__ */ jsx8("span", { className: "line-clamp-2 text-[10px] leading-tight", children: name })
1244
+ ] });
1245
+ }
1246
+ function AttachmentThumbnail({ part, resolveFileUrl, fetchFile }) {
1247
+ const url = resolveFileUrl(part);
1248
+ const [result, setResult] = useState6(null);
1249
+ useEffect5(() => {
1250
+ let cancelled = false;
1251
+ setResult(null);
1252
+ loadAttachmentFile(url, fetchFile).then((next) => {
1253
+ if (!cancelled) setResult(next);
1254
+ });
1255
+ return () => {
1256
+ cancelled = true;
1257
+ };
1258
+ }, [url, fetchFile]);
1259
+ const objectUrl = useAttachmentObjectUrl(result?.ok ? result.blob : void 0);
1260
+ const handleClick = useCallback(() => {
1261
+ if (!objectUrl) return;
1262
+ window.open(objectUrl, "_blank", "noopener");
1263
+ }, [objectUrl]);
1264
+ if (!result) {
1265
+ return /* @__PURE__ */ jsx8("span", { className: "inline-block h-16 w-16 shrink-0 animate-pulse rounded-md bg-muted", "aria-hidden": true });
1266
+ }
1267
+ if (!result.ok || !objectUrl) {
1268
+ return /* @__PURE__ */ jsx8(AttachmentThumbnailError, { name: part.name });
1269
+ }
1270
+ return /* @__PURE__ */ jsx8(
1271
+ "button",
1272
+ {
1273
+ type: "button",
1274
+ onClick: handleClick,
1275
+ "aria-label": `Open ${part.name}`,
1276
+ className: "h-16 w-16 shrink-0 overflow-hidden rounded-md border border-border focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
1277
+ children: /* @__PURE__ */ jsx8("img", { src: objectUrl, alt: part.name, className: "h-16 w-16 object-cover" })
1278
+ }
1279
+ );
1280
+ }
1281
+ function AttachmentChip({ part, resolveFileUrl, fetchFile }) {
1282
+ const [status, setStatus] = useState6("idle");
1283
+ const [errorMessage, setErrorMessage] = useState6(null);
1284
+ const handleClick = useCallback(() => {
1285
+ if (status === "loading") return;
1286
+ setStatus("loading");
1287
+ setErrorMessage(null);
1288
+ const url = resolveFileUrl(part);
1289
+ void loadAttachmentFile(url, fetchFile).then((result) => {
1290
+ if (!result.ok) {
1291
+ setStatus("error");
1292
+ setErrorMessage(result.message);
1293
+ return;
1294
+ }
1295
+ const download = triggerAttachmentDownload(part.name, result.blob);
1296
+ if (!download.ok) {
1297
+ setStatus("error");
1298
+ setErrorMessage(download.message);
1299
+ return;
1300
+ }
1301
+ setStatus("idle");
1302
+ });
1303
+ }, [status, resolveFileUrl, part, fetchFile]);
1304
+ const Icon = status === "error" ? WarningGlyph : iconForMediaType(part.mediaType);
1305
+ const className = [
1306
+ "inline-flex items-center gap-1 rounded-md border px-2 py-0.5 text-[11px]",
1307
+ status === "error" ? "border-destructive/40 bg-destructive/10 text-destructive" : "border-border bg-muted/60 text-muted-foreground"
1308
+ ].join(" ");
1309
+ return /* @__PURE__ */ jsxs6(
1310
+ "button",
1311
+ {
1312
+ type: "button",
1313
+ onClick: handleClick,
1314
+ title: status === "error" ? errorMessage ?? void 0 : void 0,
1315
+ className,
1316
+ children: [
1317
+ /* @__PURE__ */ jsx8(Icon, { className: "h-3 w-3 shrink-0" }),
1318
+ part.name,
1319
+ typeof part.size === "number" && /* @__PURE__ */ jsxs6("span", { className: "text-muted-foreground/70", children: [
1320
+ "\xB7 ",
1321
+ formatBytes(part.size)
1322
+ ] })
1323
+ ]
1324
+ }
1325
+ );
1326
+ }
1327
+ function MessageAttachments({ parts, resolveFileUrl, justify = "end", fetchFile }) {
1328
+ if (parts.length === 0) return null;
1329
+ return /* @__PURE__ */ jsx8("div", { className: `flex flex-wrap gap-1.5 ${justify === "start" ? "justify-start" : "justify-end"}`, children: parts.map(
1330
+ (part) => part.type === "image" ? /* @__PURE__ */ jsx8(AttachmentThumbnail, { part, resolveFileUrl, fetchFile }, `${part.path}:${part.name}`) : /* @__PURE__ */ jsx8(AttachmentChip, { part, resolveFileUrl, fetchFile }, `${part.path}:${part.name}`)
1331
+ ) });
1332
+ }
1333
+
1148
1334
  // src/web-react/chat-stream.ts
1149
1335
  function dispatchChatStreamLine(line, cb) {
1150
1336
  let receivedContent = false;
@@ -1315,32 +1501,32 @@ async function streamChatTurn(opts) {
1315
1501
 
1316
1502
  // src/web-react/chat-composer.tsx
1317
1503
  import {
1318
- useCallback,
1319
- useEffect as useEffect5,
1504
+ useCallback as useCallback2,
1505
+ useEffect as useEffect6,
1320
1506
  useRef as useRef5,
1321
- useState as useState6
1507
+ useState as useState7
1322
1508
  } from "react";
1323
- import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
1509
+ import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
1324
1510
  function SendGlyph({ className }) {
1325
- return /* @__PURE__ */ jsx8("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx8("path", { d: "M22 2 11 13M22 2l-7 20-4-9-9-4 20-7z" }) });
1511
+ return /* @__PURE__ */ jsx9("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx9("path", { d: "M22 2 11 13M22 2l-7 20-4-9-9-4 20-7z" }) });
1326
1512
  }
1327
1513
  function StopGlyph({ className }) {
1328
- return /* @__PURE__ */ jsx8("svg", { className, viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, children: /* @__PURE__ */ jsx8("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" }) });
1514
+ return /* @__PURE__ */ jsx9("svg", { className, viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, children: /* @__PURE__ */ jsx9("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" }) });
1329
1515
  }
1330
1516
  function PaperclipGlyph({ className }) {
1331
- return /* @__PURE__ */ jsx8("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx8("path", { d: "m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48" }) });
1517
+ return /* @__PURE__ */ jsx9("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx9("path", { d: "m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48" }) });
1332
1518
  }
1333
1519
  function FolderGlyph({ className }) {
1334
- return /* @__PURE__ */ jsxs6("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
1335
- /* @__PURE__ */ jsx8("path", { d: "M4 20h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.93a2 2 0 0 1-1.66-.9l-.82-1.2A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2Z" }),
1336
- /* @__PURE__ */ jsx8("path", { d: "M12 10v6m-3-3h6" })
1520
+ return /* @__PURE__ */ jsxs7("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
1521
+ /* @__PURE__ */ jsx9("path", { d: "M4 20h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.93a2 2 0 0 1-1.66-.9l-.82-1.2A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2Z" }),
1522
+ /* @__PURE__ */ jsx9("path", { d: "M12 10v6m-3-3h6" })
1337
1523
  ] });
1338
1524
  }
1339
1525
  function CloseGlyph({ className }) {
1340
- return /* @__PURE__ */ jsx8("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx8("path", { d: "M18 6 6 18M6 6l12 12" }) });
1526
+ return /* @__PURE__ */ jsx9("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx9("path", { d: "M18 6 6 18M6 6l12 12" }) });
1341
1527
  }
1342
1528
  function UploadGlyph({ className }) {
1343
- return /* @__PURE__ */ jsx8("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx8("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M17 8l-5-5-5 5M12 3v12" }) });
1529
+ return /* @__PURE__ */ jsx9("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx9("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M17 8l-5-5-5 5M12 3v12" }) });
1344
1530
  }
1345
1531
  var MAX_HEIGHT = 168;
1346
1532
  function ChatComposer({
@@ -1369,21 +1555,21 @@ function ChatComposer({
1369
1555
  className
1370
1556
  }) {
1371
1557
  const isControlled = value !== void 0;
1372
- const [internal, setInternal] = useState6(initialValue ?? "");
1558
+ const [internal, setInternal] = useState7(initialValue ?? "");
1373
1559
  const text = isControlled ? value : internal;
1374
1560
  const textareaRef = useRef5(null);
1375
1561
  const fileInputRef = useRef5(null);
1376
1562
  const folderInputRef = useRef5(null);
1377
- const [dragOver, setDragOver] = useState6(false);
1563
+ const [dragOver, setDragOver] = useState7(false);
1378
1564
  const dragDepth = useRef5(0);
1379
- const setText = useCallback(
1565
+ const setText = useCallback2(
1380
1566
  (next) => {
1381
1567
  if (!isControlled) setInternal(next);
1382
1568
  onValueChange?.(next);
1383
1569
  },
1384
1570
  [isControlled, onValueChange]
1385
1571
  );
1386
- useEffect5(() => {
1572
+ useEffect6(() => {
1387
1573
  const el = textareaRef.current;
1388
1574
  if (!el) return;
1389
1575
  el.style.height = "auto";
@@ -1391,7 +1577,7 @@ function ChatComposer({
1391
1577
  }, [text]);
1392
1578
  const prevSeedRef = useRef5(null);
1393
1579
  const pendingCaretRef = useRef5(null);
1394
- useEffect5(() => {
1580
+ useEffect6(() => {
1395
1581
  const prev = prevSeedRef.current;
1396
1582
  prevSeedRef.current = seed ?? null;
1397
1583
  if (seed == null || seed === prev || isControlled) return;
@@ -1405,7 +1591,7 @@ function ChatComposer({
1405
1591
  pendingCaretRef.current = seed;
1406
1592
  }
1407
1593
  }, [seed, setText, onSeedApplied, isControlled]);
1408
- useEffect5(() => {
1594
+ useEffect6(() => {
1409
1595
  if (pendingCaretRef.current == null || pendingCaretRef.current !== text)
1410
1596
  return;
1411
1597
  pendingCaretRef.current = null;
@@ -1414,7 +1600,7 @@ function ChatComposer({
1414
1600
  el.focus();
1415
1601
  el.setSelectionRange(text.length, text.length);
1416
1602
  }, [text]);
1417
- useEffect5(() => {
1603
+ useEffect6(() => {
1418
1604
  if (!focusShortcut || disabled) return;
1419
1605
  function onKeyDown(e) {
1420
1606
  if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "l") {
@@ -1425,20 +1611,20 @@ function ChatComposer({
1425
1611
  document.addEventListener("keydown", onKeyDown);
1426
1612
  return () => document.removeEventListener("keydown", onKeyDown);
1427
1613
  }, [focusShortcut, disabled]);
1428
- const readyParts = pendingFiles.filter((f) => f.status === "ready" && f.part).map((f) => f.part);
1429
- const hasSendable = onSendParts ? text.trim().length > 0 || readyParts.length > 0 : text.trim().length > 0;
1614
+ const readyFileCount = pendingFiles.filter((f) => f.status === "ready").length;
1615
+ const hasSendable = text.trim().length > 0 || readyFileCount > 0;
1430
1616
  const canSend = hasSendable && !isStreaming && !disabled;
1431
- const send = useCallback(() => {
1617
+ const send = useCallback2(() => {
1432
1618
  const trimmed = text.trim();
1433
1619
  if (isStreaming || disabled) return;
1620
+ const readyFiles = pendingFiles.filter((f) => f.status === "ready");
1621
+ if (!trimmed && readyFiles.length === 0) return;
1434
1622
  if (onSendParts) {
1435
- const parts = pendingFiles.filter((f) => f.status === "ready" && f.part).map((f) => f.part);
1436
- if (!trimmed && parts.length === 0) return;
1623
+ const parts = readyFiles.filter((f) => f.part).map((f) => f.part);
1437
1624
  onSendParts(trimmed, parts);
1438
1625
  setText("");
1439
1626
  return;
1440
1627
  }
1441
- if (!trimmed) return;
1442
1628
  onSend?.(trimmed);
1443
1629
  setText("");
1444
1630
  }, [text, isStreaming, disabled, onSend, onSendParts, pendingFiles, setText]);
@@ -1457,13 +1643,13 @@ function ChatComposer({
1457
1643
  if (e.target.files?.length) (onAttachFolder ?? onAttach)?.(e.target.files);
1458
1644
  e.target.value = "";
1459
1645
  };
1460
- const handleDragEnter = useCallback((e) => {
1646
+ const handleDragEnter = useCallback2((e) => {
1461
1647
  e.preventDefault();
1462
1648
  e.stopPropagation();
1463
1649
  dragDepth.current++;
1464
1650
  if (e.dataTransfer?.types.includes("Files")) setDragOver(true);
1465
1651
  }, []);
1466
- const handleDragLeave = useCallback((e) => {
1652
+ const handleDragLeave = useCallback2((e) => {
1467
1653
  e.preventDefault();
1468
1654
  e.stopPropagation();
1469
1655
  dragDepth.current--;
@@ -1472,12 +1658,12 @@ function ChatComposer({
1472
1658
  setDragOver(false);
1473
1659
  }
1474
1660
  }, []);
1475
- const handleDragOver = useCallback((e) => {
1661
+ const handleDragOver = useCallback2((e) => {
1476
1662
  e.preventDefault();
1477
1663
  e.stopPropagation();
1478
1664
  if (e.dataTransfer) e.dataTransfer.dropEffect = "copy";
1479
1665
  }, []);
1480
- const handleDrop = useCallback(
1666
+ const handleDrop = useCallback2(
1481
1667
  (e) => {
1482
1668
  e.preventDefault();
1483
1669
  e.stopPropagation();
@@ -1492,7 +1678,7 @@ function ChatComposer({
1492
1678
  const fileChips = pendingFiles.filter((f) => f.kind !== "folder");
1493
1679
  const showFooter = controls != null && controlsPlacement === "footer";
1494
1680
  const showAbove = controls != null && controlsPlacement === "above";
1495
- return /* @__PURE__ */ jsxs6(
1681
+ return /* @__PURE__ */ jsxs7(
1496
1682
  "div",
1497
1683
  {
1498
1684
  className: `relative ${className ?? ""}`,
@@ -1501,42 +1687,42 @@ function ChatComposer({
1501
1687
  onDragOver: onAttach ? handleDragOver : void 0,
1502
1688
  onDrop: onAttach ? handleDrop : void 0,
1503
1689
  children: [
1504
- dragOver && /* @__PURE__ */ jsx8("div", { className: "pointer-events-none absolute inset-0 z-10 flex items-center justify-center rounded-2xl border-2 border-dashed border-primary/50 bg-card/95", children: /* @__PURE__ */ jsxs6("div", { className: "text-center", children: [
1505
- /* @__PURE__ */ jsx8("span", { className: "mx-auto mb-2 flex h-11 w-11 items-center justify-center rounded-xl bg-primary/10 text-primary", children: /* @__PURE__ */ jsx8(UploadGlyph, { className: "h-5 w-5" }) }),
1506
- /* @__PURE__ */ jsx8("p", { className: "text-sm font-semibold text-foreground", children: dropTitle }),
1507
- /* @__PURE__ */ jsx8("p", { className: "mt-0.5 text-xs text-muted-foreground", children: dropDescription })
1690
+ dragOver && /* @__PURE__ */ jsx9("div", { className: "pointer-events-none absolute inset-0 z-10 flex items-center justify-center rounded-2xl border-2 border-dashed border-primary/50 bg-card/95", children: /* @__PURE__ */ jsxs7("div", { className: "text-center", children: [
1691
+ /* @__PURE__ */ jsx9("span", { className: "mx-auto mb-2 flex h-11 w-11 items-center justify-center rounded-xl bg-primary/10 text-primary", children: /* @__PURE__ */ jsx9(UploadGlyph, { className: "h-5 w-5" }) }),
1692
+ /* @__PURE__ */ jsx9("p", { className: "text-sm font-semibold text-foreground", children: dropTitle }),
1693
+ /* @__PURE__ */ jsx9("p", { className: "mt-0.5 text-xs text-muted-foreground", children: dropDescription })
1508
1694
  ] }) }),
1509
- showAbove && /* @__PURE__ */ jsx8("div", { className: "mb-1.5 flex flex-wrap items-center gap-1.5 px-1", children: controls }),
1510
- pendingFiles.length > 0 && /* @__PURE__ */ jsx8("div", { className: "mb-2 flex flex-wrap gap-1.5", children: [...folderChips, ...fileChips].map((f) => /* @__PURE__ */ jsxs6(
1695
+ showAbove && /* @__PURE__ */ jsx9("div", { className: "mb-1.5 flex flex-wrap items-center gap-1.5 px-1", children: controls }),
1696
+ pendingFiles.length > 0 && /* @__PURE__ */ jsx9("div", { className: "mb-2 flex flex-wrap gap-1.5", children: [...folderChips, ...fileChips].map((f) => /* @__PURE__ */ jsxs7(
1511
1697
  "span",
1512
1698
  {
1513
1699
  className: `inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs ${f.status === "error" ? "border-destructive/40 text-destructive" : "border-border bg-muted/50 text-foreground"}`,
1514
1700
  children: [
1515
- f.kind === "folder" ? /* @__PURE__ */ jsx8(FolderGlyph, { className: "h-3 w-3 shrink-0" }) : /* @__PURE__ */ jsx8(PaperclipGlyph, { className: "h-3 w-3 shrink-0" }),
1516
- /* @__PURE__ */ jsx8("span", { className: "max-w-[150px] truncate", children: f.name }),
1517
- f.fileCount !== void 0 && /* @__PURE__ */ jsxs6("span", { className: "text-muted-foreground", children: [
1701
+ f.kind === "folder" ? /* @__PURE__ */ jsx9(FolderGlyph, { className: "h-3 w-3 shrink-0" }) : /* @__PURE__ */ jsx9(PaperclipGlyph, { className: "h-3 w-3 shrink-0" }),
1702
+ /* @__PURE__ */ jsx9("span", { className: "max-w-[150px] truncate", children: f.name }),
1703
+ f.fileCount !== void 0 && /* @__PURE__ */ jsxs7("span", { className: "text-muted-foreground", children: [
1518
1704
  "(",
1519
1705
  f.fileCount,
1520
1706
  ")"
1521
1707
  ] }),
1522
- f.status === "uploading" && /* @__PURE__ */ jsx8("span", { className: "h-3 w-3 animate-spin rounded-full border-2 border-primary border-t-transparent" }),
1523
- onRemoveFile && /* @__PURE__ */ jsx8(
1708
+ f.status === "uploading" && /* @__PURE__ */ jsx9("span", { className: "h-3 w-3 animate-spin rounded-full border-2 border-primary border-t-transparent" }),
1709
+ onRemoveFile && /* @__PURE__ */ jsx9(
1524
1710
  "button",
1525
1711
  {
1526
1712
  type: "button",
1527
1713
  "aria-label": `Remove ${f.name}`,
1528
1714
  onClick: () => onRemoveFile(f.id),
1529
1715
  className: "rounded p-0.5 text-muted-foreground transition hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
1530
- children: /* @__PURE__ */ jsx8(CloseGlyph, { className: "h-3 w-3" })
1716
+ children: /* @__PURE__ */ jsx9(CloseGlyph, { className: "h-3 w-3" })
1531
1717
  }
1532
1718
  )
1533
1719
  ]
1534
1720
  },
1535
1721
  f.id
1536
1722
  )) }),
1537
- /* @__PURE__ */ jsxs6("div", { className: "flex items-end gap-2 rounded-2xl border border-border bg-card px-2.5 py-2 transition focus-within:border-primary/40 focus-within:ring-2 focus-within:ring-primary/15", children: [
1538
- onAttach && /* @__PURE__ */ jsxs6(Fragment3, { children: [
1539
- /* @__PURE__ */ jsx8(
1723
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-end gap-2 rounded-2xl border border-border bg-card px-2.5 py-2 transition focus-within:border-primary/40 focus-within:ring-2 focus-within:ring-primary/15", children: [
1724
+ onAttach && /* @__PURE__ */ jsxs7(Fragment3, { children: [
1725
+ /* @__PURE__ */ jsx9(
1540
1726
  "button",
1541
1727
  {
1542
1728
  type: "button",
@@ -1545,13 +1731,13 @@ function ChatComposer({
1545
1731
  "aria-label": "Attach files",
1546
1732
  title: "Attach files",
1547
1733
  className: "mb-0.5 shrink-0 rounded-lg p-2 text-muted-foreground transition hover:bg-accent/40 hover:text-foreground disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
1548
- children: /* @__PURE__ */ jsx8(PaperclipGlyph, { className: "h-4 w-4" })
1734
+ children: /* @__PURE__ */ jsx9(PaperclipGlyph, { className: "h-4 w-4" })
1549
1735
  }
1550
1736
  ),
1551
- /* @__PURE__ */ jsx8("input", { ref: fileInputRef, type: "file", multiple: true, className: "hidden", accept, onChange: handleFileChange })
1737
+ /* @__PURE__ */ jsx9("input", { ref: fileInputRef, type: "file", multiple: true, className: "hidden", accept, onChange: handleFileChange })
1552
1738
  ] }),
1553
- onAttachFolder && /* @__PURE__ */ jsxs6(Fragment3, { children: [
1554
- /* @__PURE__ */ jsx8(
1739
+ onAttachFolder && /* @__PURE__ */ jsxs7(Fragment3, { children: [
1740
+ /* @__PURE__ */ jsx9(
1555
1741
  "button",
1556
1742
  {
1557
1743
  type: "button",
@@ -1560,10 +1746,10 @@ function ChatComposer({
1560
1746
  "aria-label": "Attach folder",
1561
1747
  title: "Attach folder",
1562
1748
  className: "mb-0.5 shrink-0 rounded-lg p-2 text-muted-foreground transition hover:bg-accent/40 hover:text-foreground disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
1563
- children: /* @__PURE__ */ jsx8(FolderGlyph, { className: "h-4 w-4" })
1749
+ children: /* @__PURE__ */ jsx9(FolderGlyph, { className: "h-4 w-4" })
1564
1750
  }
1565
1751
  ),
1566
- /* @__PURE__ */ jsx8(
1752
+ /* @__PURE__ */ jsx9(
1567
1753
  "input",
1568
1754
  {
1569
1755
  ref: folderInputRef,
@@ -1575,7 +1761,7 @@ function ChatComposer({
1575
1761
  }
1576
1762
  )
1577
1763
  ] }),
1578
- /* @__PURE__ */ jsx8(
1764
+ /* @__PURE__ */ jsx9(
1579
1765
  "textarea",
1580
1766
  {
1581
1767
  ref: textareaRef,
@@ -1589,8 +1775,8 @@ function ChatComposer({
1589
1775
  className: "max-h-[168px] min-h-[40px] flex-1 resize-none bg-transparent px-1.5 py-2 text-[15px] leading-6 text-foreground outline-none placeholder:text-muted-foreground disabled:opacity-50"
1590
1776
  }
1591
1777
  ),
1592
- showFooter && /* @__PURE__ */ jsx8("div", { className: "mb-0.5 flex shrink-0 items-center gap-1.5", children: controls }),
1593
- isStreaming ? /* @__PURE__ */ jsxs6(
1778
+ showFooter && /* @__PURE__ */ jsx9("div", { className: "mb-0.5 flex shrink-0 items-center gap-1.5", children: controls }),
1779
+ isStreaming ? /* @__PURE__ */ jsxs7(
1594
1780
  "button",
1595
1781
  {
1596
1782
  type: "button",
@@ -1598,11 +1784,11 @@ function ChatComposer({
1598
1784
  "aria-label": "Stop response",
1599
1785
  className: "mb-0.5 inline-flex shrink-0 items-center gap-1.5 rounded-full bg-destructive/15 px-3.5 py-2 text-sm font-medium text-destructive transition hover:bg-destructive/25 focus:outline-none focus-visible:ring-2 focus-visible:ring-destructive/50",
1600
1786
  children: [
1601
- /* @__PURE__ */ jsx8(StopGlyph, { className: "h-3.5 w-3.5" }),
1602
- /* @__PURE__ */ jsx8("span", { children: "Stop" })
1787
+ /* @__PURE__ */ jsx9(StopGlyph, { className: "h-3.5 w-3.5" }),
1788
+ /* @__PURE__ */ jsx9("span", { children: "Stop" })
1603
1789
  ]
1604
1790
  }
1605
- ) : /* @__PURE__ */ jsxs6(
1791
+ ) : /* @__PURE__ */ jsxs7(
1606
1792
  "button",
1607
1793
  {
1608
1794
  type: "button",
@@ -1611,16 +1797,16 @@ function ChatComposer({
1611
1797
  "aria-label": sendLabel,
1612
1798
  className: "mb-0.5 inline-flex shrink-0 items-center gap-1.5 rounded-full bg-primary px-3.5 py-2 text-sm font-medium text-primary-foreground shadow-sm transition hover:bg-primary/90 disabled:cursor-not-allowed disabled:opacity-40 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-card",
1613
1799
  children: [
1614
- /* @__PURE__ */ jsx8(SendGlyph, { className: "h-3.5 w-3.5" }),
1615
- /* @__PURE__ */ jsx8("span", { children: sendLabel })
1800
+ /* @__PURE__ */ jsx9(SendGlyph, { className: "h-3.5 w-3.5" }),
1801
+ /* @__PURE__ */ jsx9("span", { children: sendLabel })
1616
1802
  ]
1617
1803
  }
1618
1804
  )
1619
1805
  ] }),
1620
- focusShortcut && /* @__PURE__ */ jsx8("div", { className: "mt-1.5 flex justify-end px-1", children: /* @__PURE__ */ jsxs6("span", { className: "text-xs text-muted-foreground", children: [
1621
- /* @__PURE__ */ jsx8("kbd", { className: "rounded border border-border bg-background px-1 py-0.5 text-[10px]", children: "Cmd" }),
1622
- /* @__PURE__ */ jsx8("kbd", { className: "ml-0.5 rounded border border-border bg-background px-1 py-0.5 text-[10px]", children: "L" }),
1623
- /* @__PURE__ */ jsx8("span", { className: "ml-1", children: "to focus" })
1806
+ focusShortcut && /* @__PURE__ */ jsx9("div", { className: "mt-1.5 flex justify-end px-1", children: /* @__PURE__ */ jsxs7("span", { className: "text-xs text-muted-foreground", children: [
1807
+ /* @__PURE__ */ jsx9("kbd", { className: "rounded border border-border bg-background px-1 py-0.5 text-[10px]", children: "Cmd" }),
1808
+ /* @__PURE__ */ jsx9("kbd", { className: "ml-0.5 rounded border border-border bg-background px-1 py-0.5 text-[10px]", children: "L" }),
1809
+ /* @__PURE__ */ jsx9("span", { className: "ml-1", children: "to focus" })
1624
1810
  ] }) })
1625
1811
  ]
1626
1812
  }
@@ -1628,7 +1814,7 @@ function ChatComposer({
1628
1814
  }
1629
1815
 
1630
1816
  // src/web-react/durable-plan-flow.ts
1631
- import { useCallback as useCallback2, useEffect as useEffect6, useRef as useRef6, useState as useState7 } from "react";
1817
+ import { useCallback as useCallback3, useEffect as useEffect7, useRef as useRef6, useState as useState8 } from "react";
1632
1818
  var DurablePlanClientError = class extends Error {
1633
1819
  constructor(message, status, code, currentPlan) {
1634
1820
  super(message);
@@ -1715,14 +1901,14 @@ function createDurablePlanDecisionClient(options) {
1715
1901
  };
1716
1902
  }
1717
1903
  function useDurablePlanFlow(options) {
1718
- const [plan, setPlan] = useState7(options.plan);
1719
- const [deciding, setDeciding] = useState7(null);
1720
- const [restoring, setRestoring] = useState7(false);
1721
- const [error, setError] = useState7(null);
1904
+ const [plan, setPlan] = useState8(options.plan);
1905
+ const [deciding, setDeciding] = useState8(null);
1906
+ const [restoring, setRestoring] = useState8(false);
1907
+ const [error, setError] = useState8(null);
1722
1908
  const attachments = useRef6(/* @__PURE__ */ new Map());
1723
1909
  const decisionInFlight = useRef6(false);
1724
- useEffect6(() => setPlan(options.plan), [options.plan]);
1725
- const apply = useCallback2(async (result) => {
1910
+ useEffect7(() => setPlan(options.plan), [options.plan]);
1911
+ const apply = useCallback3(async (result) => {
1726
1912
  setPlan(result.plan);
1727
1913
  options.onUpdated?.(result.plan);
1728
1914
  const receipt = result.followUp;
@@ -1735,7 +1921,7 @@ function useDurablePlanFlow(options) {
1735
1921
  }
1736
1922
  await pending;
1737
1923
  }, [options.attachFollowUp, options.onUpdated]);
1738
- const decide = useCallback2(async (decision, feedback) => {
1924
+ const decide = useCallback3(async (decision, feedback) => {
1739
1925
  if (decisionInFlight.current) return null;
1740
1926
  decisionInFlight.current = true;
1741
1927
  setDeciding(decision);
@@ -1761,7 +1947,7 @@ function useDurablePlanFlow(options) {
1761
1947
  setDeciding(null);
1762
1948
  }
1763
1949
  }, [apply, options.client, options.onUpdated, plan.planId, plan.revision]);
1764
- const restore = useCallback2(async () => {
1950
+ const restore = useCallback3(async () => {
1765
1951
  setRestoring(true);
1766
1952
  setError(null);
1767
1953
  try {
@@ -1890,7 +2076,7 @@ function createDurableInteractionAnswerSubmitter(options) {
1890
2076
  }
1891
2077
 
1892
2078
  // src/web-react/use-chat-interactions.ts
1893
- import { useCallback as useCallback3, useMemo as useMemo4, useState as useState8 } from "react";
2079
+ import { useCallback as useCallback4, useMemo as useMemo4, useState as useState9 } from "react";
1894
2080
  function hasPendingContentDuplicate(list, interaction) {
1895
2081
  if (interaction.status !== "pending") return false;
1896
2082
  const signature = questionInteractionContentSignature(interaction);
@@ -1969,34 +2155,34 @@ function hydrateChatInteractions(list, persisted) {
1969
2155
  return persisted.reduce(upsertChatInteraction, list);
1970
2156
  }
1971
2157
  function useChatInteractions(options = {}) {
1972
- const [interactions, setInteractions] = useState8([]);
1973
- const upsert = useCallback3((interaction) => {
2158
+ const [interactions, setInteractions] = useState9([]);
2159
+ const upsert = useCallback4((interaction) => {
1974
2160
  setInteractions((prev) => upsertChatInteraction(prev, interaction));
1975
2161
  }, []);
1976
- const applyCancel = useCallback3((cancel) => {
2162
+ const applyCancel = useCallback4((cancel) => {
1977
2163
  setInteractions((prev) => cancelChatInteraction(prev, cancel));
1978
2164
  }, []);
1979
- const markResolved = useCallback3((id, status, answers) => {
2165
+ const markResolved = useCallback4((id, status, answers) => {
1980
2166
  setInteractions((prev) => resolveChatInteraction(prev, id, status, answers));
1981
2167
  }, []);
1982
- const restore = useCallback3((outstanding, restoreOptions) => {
2168
+ const restore = useCallback4((outstanding, restoreOptions) => {
1983
2169
  setInteractions((prev) => restoreChatInteractions(prev, outstanding, {
1984
2170
  mode: restoreOptions?.mode ?? options.mode
1985
2171
  }));
1986
2172
  }, [options.mode]);
1987
- const hydrate = useCallback3((persisted) => {
2173
+ const hydrate = useCallback4((persisted) => {
1988
2174
  setInteractions((prev) => hydrateChatInteractions(prev, persisted));
1989
2175
  }, []);
1990
- const terminalizePending = useCallback3((status) => {
2176
+ const terminalizePending = useCallback4((status) => {
1991
2177
  setInteractions((prev) => terminalizePendingChatInteractions(prev, status));
1992
2178
  }, []);
1993
- const reset = useCallback3(() => setInteractions([]), []);
2179
+ const reset = useCallback4(() => setInteractions([]), []);
1994
2180
  const pending = useMemo4(() => interactions.filter((item) => item.status === "pending"), [interactions]);
1995
2181
  return { interactions, pending, upsert, applyCancel, markResolved, restore, hydrate, terminalizePending, reset };
1996
2182
  }
1997
2183
 
1998
2184
  // src/web-react/use-file-mentions.ts
1999
- import { useCallback as useCallback4, useMemo as useMemo5, useRef as useRef7, useState as useState9 } from "react";
2185
+ import { useCallback as useCallback5, useMemo as useMemo5, useRef as useRef7, useState as useState10 } from "react";
2000
2186
  var FILE_MENTION_KIND = "file";
2001
2187
  function toMentionItem(file) {
2002
2188
  return { id: file.path, label: file.name, detail: file.path, kind: FILE_MENTION_KIND };
@@ -2054,12 +2240,12 @@ function useFileMentions(options) {
2054
2240
  emptyText = DEFAULT_MENTION_EMPTY_TEXT
2055
2241
  } = options;
2056
2242
  const fetchImpl = options.fetchImpl ?? fetch;
2057
- const [state, setState] = useState9({ kind: "idle" });
2243
+ const [state, setState] = useState10({ kind: "idle" });
2058
2244
  const stateRef = useRef7(state);
2059
2245
  stateRef.current = state;
2060
2246
  const inFlightRef = useRef7(null);
2061
- const [mentions, setMentions] = useState9([]);
2062
- const load = useCallback4(() => {
2247
+ const [mentions, setMentions] = useState10([]);
2248
+ const load = useCallback5(() => {
2063
2249
  if (inFlightRef.current) return inFlightRef.current;
2064
2250
  if (stateRef.current.kind === "idle") {
2065
2251
  stateRef.current = { kind: "loading" };
@@ -2091,10 +2277,10 @@ function useFileMentions(options) {
2091
2277
  inFlightRef.current = attempt;
2092
2278
  return attempt;
2093
2279
  }, [fetchImpl, indexUrl]);
2094
- const refresh = useCallback4(async () => {
2280
+ const refresh = useCallback5(async () => {
2095
2281
  await load();
2096
2282
  }, [load]);
2097
- const fetchItems = useCallback4(
2283
+ const fetchItems = useCallback5(
2098
2284
  async (query) => {
2099
2285
  let current = stateRef.current;
2100
2286
  if (current.kind === "idle" || current.kind === "loading") {
@@ -2109,10 +2295,10 @@ function useFileMentions(options) {
2109
2295
  },
2110
2296
  [load, limit, refreshAfterMs]
2111
2297
  );
2112
- const onMentionsChange = useCallback4((items) => {
2298
+ const onMentionsChange = useCallback5((items) => {
2113
2299
  setMentions(items.filter((item) => item.kind === void 0 || item.kind === FILE_MENTION_KIND).map(toFileMention));
2114
2300
  }, []);
2115
- const clearMentions = useCallback4(() => setMentions([]), []);
2301
+ const clearMentions = useCallback5(() => setMentions([]), []);
2116
2302
  const mention = useMemo5(
2117
2303
  () => ({
2118
2304
  fetchItems,
@@ -2166,9 +2352,284 @@ function segmentMentionContent(content, parts) {
2166
2352
  return { segments, matched };
2167
2353
  }
2168
2354
 
2355
+ // src/web-react/use-composer-attachments.ts
2356
+ import { useCallback as useCallback6, useEffect as useEffect8, useMemo as useMemo6, useRef as useRef8, useState as useState11 } from "react";
2357
+ function newId() {
2358
+ const cryptoObject = globalThis.crypto;
2359
+ if (typeof cryptoObject?.randomUUID === "function") return cryptoObject.randomUUID();
2360
+ return `att-${Date.now()}-${Math.random().toString(36).slice(2)}`;
2361
+ }
2362
+ function dedupeName(name, taken) {
2363
+ if (!taken.has(name)) return name;
2364
+ const dot = name.lastIndexOf(".");
2365
+ const base = dot > 0 ? name.slice(0, dot) : name;
2366
+ const ext = dot > 0 ? name.slice(dot) : "";
2367
+ let n = 2;
2368
+ let candidate = `${base}-${n}${ext}`;
2369
+ while (taken.has(candidate)) {
2370
+ n += 1;
2371
+ candidate = `${base}-${n}${ext}`;
2372
+ }
2373
+ return candidate;
2374
+ }
2375
+ function kindForMime(mime) {
2376
+ return mime.startsWith("image/") ? "image" : "file";
2377
+ }
2378
+ function isAcceptedFileType(file, accept) {
2379
+ const patterns = accept.split(",").map((p) => p.trim()).filter((p) => p.length > 0);
2380
+ if (patterns.length === 0) return true;
2381
+ const name = file.name.toLowerCase();
2382
+ const type = (file.type || "").toLowerCase();
2383
+ return patterns.some((pattern) => {
2384
+ const lower = pattern.toLowerCase();
2385
+ if (lower.startsWith(".")) return name.endsWith(lower);
2386
+ if (lower.endsWith("/*")) return type.startsWith(lower.slice(0, -1));
2387
+ return type === lower;
2388
+ });
2389
+ }
2390
+ async function parseUploadError(res) {
2391
+ const detail = await res.json().catch(() => null);
2392
+ if (detail && typeof detail === "object" && "error" in detail) {
2393
+ const error = detail.error;
2394
+ if (typeof error === "string" && error) return error;
2395
+ if (error && typeof error === "object" && "message" in error) {
2396
+ const message = error.message;
2397
+ if (typeof message === "string" && message) return message;
2398
+ }
2399
+ }
2400
+ return `Upload failed (${res.status})`;
2401
+ }
2402
+ var NO_UPLOAD_TARGET_MESSAGE = "No upload destination configured (pass uploadUrl or buildUploadRequest)";
2403
+ function useComposerAttachments(options) {
2404
+ const optionsRef = useRef8(options);
2405
+ optionsRef.current = options;
2406
+ const [staged, setStagedState] = useState11([]);
2407
+ const stagedRef = useRef8([]);
2408
+ const controllersRef = useRef8(/* @__PURE__ */ new Map());
2409
+ const setStaged = useCallback6(
2410
+ (updater) => {
2411
+ const next = typeof updater === "function" ? updater(stagedRef.current) : updater;
2412
+ stagedRef.current = next;
2413
+ setStagedState(next);
2414
+ },
2415
+ []
2416
+ );
2417
+ const upload = useCallback6(
2418
+ async (id, file, name) => {
2419
+ const opts = optionsRef.current;
2420
+ setStaged(
2421
+ (prev) => prev.map((s) => s.id === id ? { ...s, status: "uploading", errorMessage: void 0 } : s)
2422
+ );
2423
+ const controller = new AbortController();
2424
+ controllersRef.current.set(id, controller);
2425
+ const form = new FormData();
2426
+ form.append("file", file, name);
2427
+ const request = opts.buildUploadRequest ? opts.buildUploadRequest({ file, name, form }) : opts.uploadUrl ? { url: opts.uploadUrl } : null;
2428
+ if (!request) {
2429
+ setStaged(
2430
+ (prev) => prev.map(
2431
+ (s) => s.id === id ? { ...s, status: "error", errorMessage: NO_UPLOAD_TARGET_MESSAGE } : s
2432
+ )
2433
+ );
2434
+ opts.onError?.(NO_UPLOAD_TARGET_MESSAGE);
2435
+ controllersRef.current.delete(id);
2436
+ return;
2437
+ }
2438
+ try {
2439
+ const res = await fetch(request.url, {
2440
+ method: "POST",
2441
+ credentials: "same-origin",
2442
+ ...request.init,
2443
+ body: form,
2444
+ signal: controller.signal
2445
+ });
2446
+ if (!res.ok) {
2447
+ const message = await parseUploadError(res);
2448
+ setStaged(
2449
+ (prev) => prev.map((s) => s.id === id ? { ...s, status: "error", errorMessage: message } : s)
2450
+ );
2451
+ opts.onError?.(message);
2452
+ return;
2453
+ }
2454
+ const data = await res.json();
2455
+ const uploaded = data.files?.[0];
2456
+ if (!uploaded) {
2457
+ const message = "Upload returned no file";
2458
+ setStaged(
2459
+ (prev) => prev.map((s) => s.id === id ? { ...s, status: "error", errorMessage: message } : s)
2460
+ );
2461
+ opts.onError?.(message);
2462
+ return;
2463
+ }
2464
+ setStaged(
2465
+ (prev) => prev.map((s) => s.id === id ? { ...s, status: "ready", reference: uploaded } : s)
2466
+ );
2467
+ } catch (err) {
2468
+ if (err.name === "AbortError") return;
2469
+ const message = err instanceof Error && err.message ? err.message : "Upload failed \u2014 check your connection";
2470
+ setStaged(
2471
+ (prev) => prev.map((s) => s.id === id ? { ...s, status: "error", errorMessage: message } : s)
2472
+ );
2473
+ opts.onError?.(message);
2474
+ } finally {
2475
+ controllersRef.current.delete(id);
2476
+ }
2477
+ },
2478
+ [setStaged]
2479
+ );
2480
+ const addFiles = useCallback6(
2481
+ async (files) => {
2482
+ const opts = optionsRef.current;
2483
+ const enabled2 = opts.enabled ?? true;
2484
+ if (!enabled2) {
2485
+ opts.onReject?.("Attachments are disabled");
2486
+ return;
2487
+ }
2488
+ const accept = opts.accept ?? ATTACHMENT_ACCEPT;
2489
+ const maxCount = opts.limits?.maxCount ?? ATTACHMENT_MAX_COUNT;
2490
+ const maxBinaryBytes = opts.limits?.maxBinaryBytes ?? MAX_BINARY_ATTACHMENT_BYTES;
2491
+ const maxTextBytes = opts.limits?.maxTextBytes ?? MAX_TEXT_ATTACHMENT_BYTES;
2492
+ const maxTotalBytes = opts.limits?.maxTotalBytes ?? MAX_ATTACHMENT_TOTAL_BYTES;
2493
+ const allowedKinds = opts.allowedKinds ?? ["image", "file"];
2494
+ const list = Array.isArray(files) ? files : Array.from(files);
2495
+ const currentCount = stagedRef.current.length;
2496
+ const countAccepted = [];
2497
+ for (const file of list) {
2498
+ if (!isAcceptedFileType(file, accept)) {
2499
+ opts.onReject?.(`"${file.name}" is not an accepted file type (${accept}).`, file);
2500
+ continue;
2501
+ }
2502
+ if (currentCount + countAccepted.length >= maxCount) {
2503
+ opts.onReject?.(`"${file.name}" was not added \u2014 the ${maxCount}-file limit is already reached.`, file);
2504
+ continue;
2505
+ }
2506
+ countAccepted.push(file);
2507
+ }
2508
+ const sizeAccepted = [];
2509
+ for (const file of countAccepted) {
2510
+ const bytes = new Uint8Array(await file.arrayBuffer());
2511
+ const sniff = sniffBinary(bytes);
2512
+ const typeCheck = checkAttachmentType(file.name, sniff);
2513
+ if (!typeCheck.succeeded) {
2514
+ opts.onReject?.(typeCheck.message, file);
2515
+ continue;
2516
+ }
2517
+ const limit = sniff.binary ? maxBinaryBytes : maxTextBytes;
2518
+ if (file.size > limit) {
2519
+ opts.onReject?.(attachmentSizeErrorMessage(file.name, file.size, limit), file);
2520
+ continue;
2521
+ }
2522
+ const mediaType = sniff.mime ?? file.type ?? "";
2523
+ const kind = kindForMime(mediaType);
2524
+ if (!allowedKinds.includes(kind)) {
2525
+ opts.onReject?.(`"${file.name}" is a ${kind} attachment, which isn't accepted here`, file);
2526
+ continue;
2527
+ }
2528
+ sizeAccepted.push(file);
2529
+ }
2530
+ const accepted = [];
2531
+ let totalBytes = stagedRef.current.reduce((total, s) => total + s.size, 0);
2532
+ for (const file of sizeAccepted) {
2533
+ const nextTotalBytes = totalBytes + file.size;
2534
+ if (nextTotalBytes > maxTotalBytes) {
2535
+ opts.onReject?.(attachmentTotalSizeErrorMessage(nextTotalBytes, maxTotalBytes), file);
2536
+ continue;
2537
+ }
2538
+ accepted.push(file);
2539
+ totalBytes = nextTotalBytes;
2540
+ }
2541
+ if (accepted.length === 0) return;
2542
+ const taken = new Set(stagedRef.current.map((s) => s.name));
2543
+ const entries = accepted.map((file) => {
2544
+ const name = dedupeName(sanitizeAttachmentFileName(file.name), taken);
2545
+ taken.add(name);
2546
+ return {
2547
+ id: newId(),
2548
+ file,
2549
+ name,
2550
+ size: file.size,
2551
+ status: "pending",
2552
+ previewUrl: file.type.startsWith("image/") ? URL.createObjectURL(file) : void 0
2553
+ };
2554
+ });
2555
+ setStaged((prev) => [...prev, ...entries]);
2556
+ for (const entry of entries) void upload(entry.id, entry.file, entry.name);
2557
+ },
2558
+ [setStaged, upload]
2559
+ );
2560
+ const retry = useCallback6(
2561
+ (id) => {
2562
+ const entry = stagedRef.current.find((s) => s.id === id);
2563
+ if (!entry) return;
2564
+ void upload(entry.id, entry.file, entry.name);
2565
+ },
2566
+ [upload]
2567
+ );
2568
+ const removeAttachment = useCallback6(
2569
+ (id) => {
2570
+ controllersRef.current.get(id)?.abort();
2571
+ controllersRef.current.delete(id);
2572
+ const entry = stagedRef.current.find((s) => s.id === id);
2573
+ if (entry?.previewUrl) URL.revokeObjectURL(entry.previewUrl);
2574
+ setStaged((prev) => prev.filter((s) => s.id !== id));
2575
+ },
2576
+ [setStaged]
2577
+ );
2578
+ const clear = useCallback6(() => {
2579
+ for (const controller of controllersRef.current.values()) controller.abort();
2580
+ controllersRef.current.clear();
2581
+ for (const entry of stagedRef.current) {
2582
+ if (entry.previewUrl) URL.revokeObjectURL(entry.previewUrl);
2583
+ }
2584
+ setStaged([]);
2585
+ }, [setStaged]);
2586
+ useEffect8(
2587
+ () => () => {
2588
+ for (const controller of controllersRef.current.values()) controller.abort();
2589
+ controllersRef.current.clear();
2590
+ for (const entry of stagedRef.current) {
2591
+ if (entry.previewUrl) URL.revokeObjectURL(entry.previewUrl);
2592
+ }
2593
+ },
2594
+ []
2595
+ );
2596
+ const composerFiles = useMemo6(
2597
+ () => staged.map((s) => ({
2598
+ id: s.id,
2599
+ name: s.name,
2600
+ size: s.size,
2601
+ kind: "file",
2602
+ status: s.status
2603
+ })),
2604
+ [staged]
2605
+ );
2606
+ const references = useMemo6(
2607
+ () => staged.filter((s) => s.status === "ready" && !!s.reference).map((s) => s.reference),
2608
+ [staged]
2609
+ );
2610
+ const hasPending = useMemo6(
2611
+ () => staged.some((s) => s.status === "pending" || s.status === "uploading"),
2612
+ [staged]
2613
+ );
2614
+ const hasError = useMemo6(() => staged.some((s) => s.status === "error"), [staged]);
2615
+ const enabled = options.enabled ?? true;
2616
+ const blockReason = !enabled ? "Attachments are disabled" : hasPending ? "Attachments are still uploading" : hasError ? "Remove failed attachments to send" : null;
2617
+ return {
2618
+ composerFiles,
2619
+ references,
2620
+ addFiles,
2621
+ retry,
2622
+ removeAttachment,
2623
+ clear,
2624
+ hasPending,
2625
+ hasError,
2626
+ blockReason
2627
+ };
2628
+ }
2629
+
2169
2630
  // src/web-react/mission-activity.tsx
2170
- import { useCallback as useCallback5, useEffect as useEffect7, useState as useState10 } from "react";
2171
- import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
2631
+ import { useCallback as useCallback7, useEffect as useEffect9, useState as useState12 } from "react";
2632
+ import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
2172
2633
  var LIVE_STATUSES = /* @__PURE__ */ new Set(["pending", "running"]);
2173
2634
  var OK_STATUSES = /* @__PURE__ */ new Set(["completed", "done", "succeeded"]);
2174
2635
  var ERROR_STATUSES = /* @__PURE__ */ new Set(["failed", "error", "cancelled", "aborted"]);
@@ -2215,20 +2676,20 @@ function waterfallLayout(trace) {
2215
2676
  });
2216
2677
  }
2217
2678
  function ChevronGlyph({ className }) {
2218
- return /* @__PURE__ */ jsx9("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx9("path", { d: "m6 9 6 6 6-6" }) });
2679
+ return /* @__PURE__ */ jsx10("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx10("path", { d: "m6 9 6 6 6-6" }) });
2219
2680
  }
2220
2681
  function RefreshGlyph({ className }) {
2221
- return /* @__PURE__ */ jsx9("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx9("path", { d: "M21 12a9 9 0 1 1-2.64-6.36M21 3v6h-6" }) });
2682
+ return /* @__PURE__ */ jsx10("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx10("path", { d: "M21 12a9 9 0 1 1-2.64-6.36M21 3v6h-6" }) });
2222
2683
  }
2223
2684
  function CopyGlyph({ className }) {
2224
- return /* @__PURE__ */ jsxs7("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
2225
- /* @__PURE__ */ jsx9("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2" }),
2226
- /* @__PURE__ */ jsx9("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
2685
+ return /* @__PURE__ */ jsxs8("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
2686
+ /* @__PURE__ */ jsx10("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2" }),
2687
+ /* @__PURE__ */ jsx10("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
2227
2688
  ] });
2228
2689
  }
2229
2690
  function TraceIdCopy({ traceId }) {
2230
- const [copied, setCopied] = useState10(false);
2231
- const copy = useCallback5(() => {
2691
+ const [copied, setCopied] = useState12(false);
2692
+ const copy = useCallback7(() => {
2232
2693
  void navigator.clipboard?.writeText(traceId).then(
2233
2694
  () => {
2234
2695
  setCopied(true);
@@ -2238,7 +2699,7 @@ function TraceIdCopy({ traceId }) {
2238
2699
  }
2239
2700
  );
2240
2701
  }, [traceId]);
2241
- return /* @__PURE__ */ jsxs7(
2702
+ return /* @__PURE__ */ jsxs8(
2242
2703
  "button",
2243
2704
  {
2244
2705
  type: "button",
@@ -2247,23 +2708,23 @@ function TraceIdCopy({ traceId }) {
2247
2708
  "aria-label": "Copy trace id",
2248
2709
  className: "inline-flex min-w-0 items-center gap-1.5 rounded text-left font-mono text-muted-foreground transition hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-card",
2249
2710
  children: [
2250
- /* @__PURE__ */ jsx9("span", { className: "truncate", children: traceId }),
2251
- /* @__PURE__ */ jsx9(CopyGlyph, { className: "h-3 w-3 shrink-0" }),
2252
- copied && /* @__PURE__ */ jsx9("span", { className: "shrink-0 not-italic text-success", children: "copied" })
2711
+ /* @__PURE__ */ jsx10("span", { className: "truncate", children: traceId }),
2712
+ /* @__PURE__ */ jsx10(CopyGlyph, { className: "h-3 w-3 shrink-0" }),
2713
+ copied && /* @__PURE__ */ jsx10("span", { className: "shrink-0 not-italic text-success", children: "copied" })
2253
2714
  ]
2254
2715
  }
2255
2716
  );
2256
2717
  }
2257
2718
  function StatusDot({ tone }) {
2258
- return /* @__PURE__ */ jsxs7("span", { className: "inline-flex items-center", children: [
2259
- /* @__PURE__ */ jsx9(
2719
+ return /* @__PURE__ */ jsxs8("span", { className: "inline-flex items-center", children: [
2720
+ /* @__PURE__ */ jsx10(
2260
2721
  "span",
2261
2722
  {
2262
2723
  "aria-hidden": true,
2263
2724
  className: `h-2 w-2 shrink-0 rounded-full ${tone === "live" ? "animate-pulse bg-warning" : tone === "ok" ? "bg-success" : tone === "error" ? "bg-destructive" : "bg-muted-foreground/40"}`
2264
2725
  }
2265
2726
  ),
2266
- /* @__PURE__ */ jsx9("span", { className: "sr-only", children: tone })
2727
+ /* @__PURE__ */ jsx10("span", { className: "sr-only", children: tone })
2267
2728
  ] });
2268
2729
  }
2269
2730
  var BAR_CLASS = {
@@ -2275,19 +2736,19 @@ function FlowWaterfall({ trace }) {
2275
2736
  const rows = waterfallLayout(trace);
2276
2737
  if (rows.length === 0) return null;
2277
2738
  const cost = formatActivityCost(trace.costUsd);
2278
- return /* @__PURE__ */ jsxs7("div", { className: "space-y-1", children: [
2279
- rows.map((row, i) => /* @__PURE__ */ jsxs7("div", { className: "grid grid-cols-[minmax(0,2fr)_minmax(0,3fr)_auto] items-center gap-2", children: [
2280
- /* @__PURE__ */ jsx9("span", { className: "truncate font-mono text-[11px] text-muted-foreground", title: row.name, children: row.name }),
2281
- /* @__PURE__ */ jsx9("div", { className: "relative h-2 rounded-sm bg-muted/40", children: /* @__PURE__ */ jsx9(
2739
+ return /* @__PURE__ */ jsxs8("div", { className: "space-y-1", children: [
2740
+ rows.map((row, i) => /* @__PURE__ */ jsxs8("div", { className: "grid grid-cols-[minmax(0,2fr)_minmax(0,3fr)_auto] items-center gap-2", children: [
2741
+ /* @__PURE__ */ jsx10("span", { className: "truncate font-mono text-[11px] text-muted-foreground", title: row.name, children: row.name }),
2742
+ /* @__PURE__ */ jsx10("div", { className: "relative h-2 rounded-sm bg-muted/40", children: /* @__PURE__ */ jsx10(
2282
2743
  "div",
2283
2744
  {
2284
2745
  className: `absolute inset-y-0 rounded-sm ${row.ok ? BAR_CLASS[row.kind] : "bg-destructive/80"} ${row.approx ? "opacity-70" : ""}`,
2285
2746
  style: { left: `${row.offsetPct}%`, width: `${row.widthPct}%` }
2286
2747
  }
2287
2748
  ) }),
2288
- /* @__PURE__ */ jsx9("span", { className: "shrink-0 font-mono text-[10px] tabular-nums text-muted-foreground/70", children: row.durationLabel })
2749
+ /* @__PURE__ */ jsx10("span", { className: "shrink-0 font-mono text-[10px] tabular-nums text-muted-foreground/70", children: row.durationLabel })
2289
2750
  ] }, i)),
2290
- /* @__PURE__ */ jsxs7("p", { className: "pt-0.5 text-right font-mono text-[10px] tabular-nums text-muted-foreground/60", children: [
2751
+ /* @__PURE__ */ jsxs8("p", { className: "pt-0.5 text-right font-mono text-[10px] tabular-nums text-muted-foreground/60", children: [
2291
2752
  (trace.totalMs / 1e3).toFixed(1),
2292
2753
  "s",
2293
2754
  cost ? ` \xB7 ${cost}` : ""
@@ -2295,43 +2756,43 @@ function FlowWaterfall({ trace }) {
2295
2756
  ] });
2296
2757
  }
2297
2758
  function MissionActivityLane({ activity, startedAt, nowMs }) {
2298
- const [expanded, setExpanded] = useState10(false);
2759
+ const [expanded, setExpanded] = useState12(false);
2299
2760
  if (activity.length === 0) return null;
2300
- return /* @__PURE__ */ jsxs7("div", { className: "mt-1 border-l border-border/50 pl-3", children: [
2761
+ return /* @__PURE__ */ jsxs8("div", { className: "mt-1 border-l border-border/50 pl-3", children: [
2301
2762
  activity.map((run) => {
2302
2763
  const tone = activityTone(run.status);
2303
2764
  const cost = formatActivityCost(run.costUsd);
2304
2765
  const duration = formatActivityDuration(run.durationMs);
2305
- return /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2 py-1 text-xs", children: [
2306
- /* @__PURE__ */ jsx9(StatusDot, { tone }),
2307
- /* @__PURE__ */ jsxs7("span", { className: "min-w-0 flex-1 truncate", children: [
2308
- /* @__PURE__ */ jsx9("span", { className: "font-medium", children: run.tool }),
2309
- /* @__PURE__ */ jsxs7("span", { className: "text-muted-foreground", children: [
2766
+ return /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2 py-1 text-xs", children: [
2767
+ /* @__PURE__ */ jsx10(StatusDot, { tone }),
2768
+ /* @__PURE__ */ jsxs8("span", { className: "min-w-0 flex-1 truncate", children: [
2769
+ /* @__PURE__ */ jsx10("span", { className: "font-medium", children: run.tool }),
2770
+ /* @__PURE__ */ jsxs8("span", { className: "text-muted-foreground", children: [
2310
2771
  " \u2014 ",
2311
2772
  run.detail
2312
2773
  ] })
2313
2774
  ] }),
2314
- tone === "live" && (run.iteration !== void 0 || run.phase !== void 0) && /* @__PURE__ */ jsx9("span", { className: "shrink-0 rounded-full bg-warning/10 px-1.5 py-0.5 font-mono text-[10px] text-warning", children: [run.iteration !== void 0 ? `iter ${run.iteration}` : null, run.phase ?? null].filter(Boolean).join(" \xB7 ") }),
2315
- /* @__PURE__ */ jsxs7("span", { className: "flex shrink-0 items-center gap-1.5 font-mono text-[10px] tabular-nums text-muted-foreground/70", children: [
2316
- tone !== "live" && tone !== "ok" && /* @__PURE__ */ jsx9("span", { children: run.status }),
2317
- cost && /* @__PURE__ */ jsx9("span", { children: cost }),
2318
- duration && /* @__PURE__ */ jsx9("span", { children: duration })
2775
+ tone === "live" && (run.iteration !== void 0 || run.phase !== void 0) && /* @__PURE__ */ jsx10("span", { className: "shrink-0 rounded-full bg-warning/10 px-1.5 py-0.5 font-mono text-[10px] text-warning", children: [run.iteration !== void 0 ? `iter ${run.iteration}` : null, run.phase ?? null].filter(Boolean).join(" \xB7 ") }),
2776
+ /* @__PURE__ */ jsxs8("span", { className: "flex shrink-0 items-center gap-1.5 font-mono text-[10px] tabular-nums text-muted-foreground/70", children: [
2777
+ tone !== "live" && tone !== "ok" && /* @__PURE__ */ jsx10("span", { children: run.status }),
2778
+ cost && /* @__PURE__ */ jsx10("span", { children: cost }),
2779
+ duration && /* @__PURE__ */ jsx10("span", { children: duration })
2319
2780
  ] })
2320
2781
  ] }, run.taskId);
2321
2782
  }),
2322
- /* @__PURE__ */ jsxs7(
2783
+ /* @__PURE__ */ jsxs8(
2323
2784
  "button",
2324
2785
  {
2325
2786
  type: "button",
2326
2787
  onClick: () => setExpanded((v) => !v),
2327
2788
  className: "flex items-center gap-1 py-0.5 text-[10px] font-medium text-muted-foreground/70 transition hover:text-foreground",
2328
2789
  children: [
2329
- /* @__PURE__ */ jsx9(ChevronGlyph, { className: `h-3 w-3 transition-transform ${expanded ? "rotate-180" : ""}` }),
2790
+ /* @__PURE__ */ jsx10(ChevronGlyph, { className: `h-3 w-3 transition-transform ${expanded ? "rotate-180" : ""}` }),
2330
2791
  "timeline"
2331
2792
  ]
2332
2793
  }
2333
2794
  ),
2334
- expanded && /* @__PURE__ */ jsx9("div", { className: "rounded-md border border-border/50 bg-muted/10 p-2", children: /* @__PURE__ */ jsx9(
2795
+ expanded && /* @__PURE__ */ jsx10("div", { className: "rounded-md border border-border/50 bg-muted/10 p-2", children: /* @__PURE__ */ jsx10(
2335
2796
  FlowWaterfall,
2336
2797
  {
2337
2798
  trace: stepActivityFlowTrace(activity, {
@@ -2346,45 +2807,45 @@ function ActivityRow({
2346
2807
  record,
2347
2808
  renderMissionRef
2348
2809
  }) {
2349
- const [open, setOpen] = useState10(false);
2810
+ const [open, setOpen] = useState12(false);
2350
2811
  const tone = activityTone(record.status);
2351
2812
  const cost = formatActivityCost(record.costUsd);
2352
2813
  const duration = formatActivityDuration(record.durationMs);
2353
- return /* @__PURE__ */ jsxs7("div", { className: "rounded-lg border border-border/60 bg-card", children: [
2354
- /* @__PURE__ */ jsxs7("button", { type: "button", onClick: () => setOpen((v) => !v), className: "flex w-full items-center gap-2.5 px-3 py-2 text-left text-sm", children: [
2355
- /* @__PURE__ */ jsx9(StatusDot, { tone }),
2356
- /* @__PURE__ */ jsxs7("span", { className: "min-w-0 flex-1 truncate", children: [
2357
- /* @__PURE__ */ jsx9("span", { className: "font-medium", children: record.tool }),
2358
- /* @__PURE__ */ jsxs7("span", { className: "text-muted-foreground", children: [
2814
+ return /* @__PURE__ */ jsxs8("div", { className: "rounded-lg border border-border/60 bg-card", children: [
2815
+ /* @__PURE__ */ jsxs8("button", { type: "button", onClick: () => setOpen((v) => !v), className: "flex w-full items-center gap-2.5 px-3 py-2 text-left text-sm", children: [
2816
+ /* @__PURE__ */ jsx10(StatusDot, { tone }),
2817
+ /* @__PURE__ */ jsxs8("span", { className: "min-w-0 flex-1 truncate", children: [
2818
+ /* @__PURE__ */ jsx10("span", { className: "font-medium", children: record.tool }),
2819
+ /* @__PURE__ */ jsxs8("span", { className: "text-muted-foreground", children: [
2359
2820
  " \u2014 ",
2360
2821
  record.detail
2361
2822
  ] })
2362
2823
  ] }),
2363
- tone === "live" && (record.iteration !== void 0 || record.phase !== void 0) && /* @__PURE__ */ jsx9("span", { className: "shrink-0 rounded-full bg-warning/10 px-2 py-0.5 font-mono text-[10px] text-warning", children: [record.iteration !== void 0 ? `iter ${record.iteration}` : null, record.phase ?? null].filter(Boolean).join(" \xB7 ") }),
2364
- /* @__PURE__ */ jsx9(
2824
+ tone === "live" && (record.iteration !== void 0 || record.phase !== void 0) && /* @__PURE__ */ jsx10("span", { className: "shrink-0 rounded-full bg-warning/10 px-2 py-0.5 font-mono text-[10px] text-warning", children: [record.iteration !== void 0 ? `iter ${record.iteration}` : null, record.phase ?? null].filter(Boolean).join(" \xB7 ") }),
2825
+ /* @__PURE__ */ jsx10(
2365
2826
  "span",
2366
2827
  {
2367
2828
  className: `shrink-0 rounded-full px-2 py-0.5 text-[10px] font-medium ${tone === "ok" ? "bg-success/10 text-success" : tone === "error" ? "bg-destructive/10 text-destructive" : tone === "live" ? "bg-warning/10 text-warning" : "bg-muted/60 text-muted-foreground"}`,
2368
2829
  children: record.status
2369
2830
  }
2370
2831
  ),
2371
- cost && /* @__PURE__ */ jsx9("span", { className: "shrink-0 font-mono text-[11px] tabular-nums text-muted-foreground", children: cost }),
2372
- /* @__PURE__ */ jsx9(ChevronGlyph, { className: `h-3 w-3 shrink-0 text-muted-foreground transition-transform ${open ? "rotate-180" : ""}` })
2832
+ cost && /* @__PURE__ */ jsx10("span", { className: "shrink-0 font-mono text-[11px] tabular-nums text-muted-foreground", children: cost }),
2833
+ /* @__PURE__ */ jsx10(ChevronGlyph, { className: `h-3 w-3 shrink-0 text-muted-foreground transition-transform ${open ? "rotate-180" : ""}` })
2373
2834
  ] }),
2374
- open && /* @__PURE__ */ jsxs7("div", { className: "space-y-2.5 border-t border-border/40 px-3 py-2.5", children: [
2375
- record.durationMs !== void 0 && /* @__PURE__ */ jsx9("div", { className: "rounded-md border border-border/50 bg-muted/10 p-2", children: /* @__PURE__ */ jsx9(FlowWaterfall, { trace: stepActivityFlowTrace([record]) }) }),
2376
- /* @__PURE__ */ jsxs7("dl", { className: "grid grid-cols-[auto_1fr] gap-x-3 gap-y-1 font-mono text-[11px]", children: [
2377
- /* @__PURE__ */ jsx9("dt", { className: "text-muted-foreground/60", children: "task" }),
2378
- /* @__PURE__ */ jsx9("dd", { className: "truncate text-muted-foreground", children: record.taskId }),
2379
- /* @__PURE__ */ jsx9("dt", { className: "text-muted-foreground/60", children: "started" }),
2380
- /* @__PURE__ */ jsx9("dd", { className: "text-muted-foreground", children: new Date(record.startedAt).toLocaleString() }),
2381
- duration && /* @__PURE__ */ jsxs7(Fragment4, { children: [
2382
- /* @__PURE__ */ jsx9("dt", { className: "text-muted-foreground/60", children: "duration" }),
2383
- /* @__PURE__ */ jsx9("dd", { className: "text-muted-foreground", children: duration })
2835
+ open && /* @__PURE__ */ jsxs8("div", { className: "space-y-2.5 border-t border-border/40 px-3 py-2.5", children: [
2836
+ record.durationMs !== void 0 && /* @__PURE__ */ jsx10("div", { className: "rounded-md border border-border/50 bg-muted/10 p-2", children: /* @__PURE__ */ jsx10(FlowWaterfall, { trace: stepActivityFlowTrace([record]) }) }),
2837
+ /* @__PURE__ */ jsxs8("dl", { className: "grid grid-cols-[auto_1fr] gap-x-3 gap-y-1 font-mono text-[11px]", children: [
2838
+ /* @__PURE__ */ jsx10("dt", { className: "text-muted-foreground/60", children: "task" }),
2839
+ /* @__PURE__ */ jsx10("dd", { className: "truncate text-muted-foreground", children: record.taskId }),
2840
+ /* @__PURE__ */ jsx10("dt", { className: "text-muted-foreground/60", children: "started" }),
2841
+ /* @__PURE__ */ jsx10("dd", { className: "text-muted-foreground", children: new Date(record.startedAt).toLocaleString() }),
2842
+ duration && /* @__PURE__ */ jsxs8(Fragment4, { children: [
2843
+ /* @__PURE__ */ jsx10("dt", { className: "text-muted-foreground/60", children: "duration" }),
2844
+ /* @__PURE__ */ jsx10("dd", { className: "text-muted-foreground", children: duration })
2384
2845
  ] }),
2385
- record.traceId && /* @__PURE__ */ jsxs7(Fragment4, { children: [
2386
- /* @__PURE__ */ jsx9("dt", { className: "text-muted-foreground/60", children: "trace" }),
2387
- /* @__PURE__ */ jsx9("dd", { className: "min-w-0", children: /* @__PURE__ */ jsx9(TraceIdCopy, { traceId: record.traceId }) })
2846
+ record.traceId && /* @__PURE__ */ jsxs8(Fragment4, { children: [
2847
+ /* @__PURE__ */ jsx10("dt", { className: "text-muted-foreground/60", children: "trace" }),
2848
+ /* @__PURE__ */ jsx10("dd", { className: "min-w-0", children: /* @__PURE__ */ jsx10(TraceIdCopy, { traceId: record.traceId }) })
2388
2849
  ] })
2389
2850
  ] }),
2390
2851
  record.missionRef && renderMissionRef?.(record.missionRef, record)
@@ -2392,11 +2853,11 @@ function ActivityRow({
2392
2853
  ] });
2393
2854
  }
2394
2855
  function AgentActivityPanel({ fetchActivity, renderMissionRef, title = "Agent activity", emptyLabel = "No agent runs yet." }) {
2395
- const [rows, setRows] = useState10([]);
2396
- const [cursor, setCursor] = useState10(void 0);
2397
- const [loading, setLoading] = useState10(false);
2398
- const [error, setError] = useState10(null);
2399
- const load = useCallback5(
2856
+ const [rows, setRows] = useState12([]);
2857
+ const [cursor, setCursor] = useState12(void 0);
2858
+ const [loading, setLoading] = useState12(false);
2859
+ const [error, setError] = useState12(null);
2860
+ const load = useCallback7(
2400
2861
  async (from) => {
2401
2862
  setLoading(true);
2402
2863
  setError(null);
@@ -2412,13 +2873,13 @@ function AgentActivityPanel({ fetchActivity, renderMissionRef, title = "Agent ac
2412
2873
  },
2413
2874
  [fetchActivity]
2414
2875
  );
2415
- useEffect7(() => {
2876
+ useEffect9(() => {
2416
2877
  void load();
2417
2878
  }, [load]);
2418
- return /* @__PURE__ */ jsxs7("div", { className: "space-y-2", children: [
2419
- /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2", children: [
2420
- /* @__PURE__ */ jsx9("h2", { className: "flex-1 text-sm font-semibold", children: title }),
2421
- /* @__PURE__ */ jsx9(
2879
+ return /* @__PURE__ */ jsxs8("div", { className: "space-y-2", children: [
2880
+ /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2", children: [
2881
+ /* @__PURE__ */ jsx10("h2", { className: "flex-1 text-sm font-semibold", children: title }),
2882
+ /* @__PURE__ */ jsx10(
2422
2883
  "button",
2423
2884
  {
2424
2885
  type: "button",
@@ -2426,14 +2887,14 @@ function AgentActivityPanel({ fetchActivity, renderMissionRef, title = "Agent ac
2426
2887
  disabled: loading,
2427
2888
  "aria-label": "Refresh",
2428
2889
  className: "rounded-md p-1.5 text-muted-foreground transition hover:bg-accent/30 hover:text-foreground disabled:opacity-50",
2429
- children: /* @__PURE__ */ jsx9(RefreshGlyph, { className: `h-3.5 w-3.5 ${loading ? "animate-spin" : ""}` })
2890
+ children: /* @__PURE__ */ jsx10(RefreshGlyph, { className: `h-3.5 w-3.5 ${loading ? "animate-spin" : ""}` })
2430
2891
  }
2431
2892
  )
2432
2893
  ] }),
2433
- error && /* @__PURE__ */ jsx9("p", { role: "alert", className: "rounded-md border border-destructive/40 bg-destructive/5 px-3 py-2 text-xs text-destructive", children: error }),
2434
- !error && rows.length === 0 && !loading && /* @__PURE__ */ jsx9("p", { className: "px-1 text-sm text-muted-foreground", children: emptyLabel }),
2435
- /* @__PURE__ */ jsx9("div", { className: "space-y-1.5", children: rows.map((record) => /* @__PURE__ */ jsx9(ActivityRow, { record, renderMissionRef }, record.taskId)) }),
2436
- cursor && /* @__PURE__ */ jsx9(
2894
+ error && /* @__PURE__ */ jsx10("p", { role: "alert", className: "rounded-md border border-destructive/40 bg-destructive/5 px-3 py-2 text-xs text-destructive", children: error }),
2895
+ !error && rows.length === 0 && !loading && /* @__PURE__ */ jsx10("p", { className: "px-1 text-sm text-muted-foreground", children: emptyLabel }),
2896
+ /* @__PURE__ */ jsx10("div", { className: "space-y-1.5", children: rows.map((record) => /* @__PURE__ */ jsx10(ActivityRow, { record, renderMissionRef }, record.taskId)) }),
2897
+ cursor && /* @__PURE__ */ jsx10(
2437
2898
  "button",
2438
2899
  {
2439
2900
  type: "button",
@@ -2447,9 +2908,9 @@ function AgentActivityPanel({ fetchActivity, renderMissionRef, title = "Agent ac
2447
2908
  }
2448
2909
 
2449
2910
  // src/web-react/seat-paywall.tsx
2450
- import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
2911
+ import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
2451
2912
  function CheckGlyph3() {
2452
- return /* @__PURE__ */ jsx10(
2913
+ return /* @__PURE__ */ jsx11(
2453
2914
  "svg",
2454
2915
  {
2455
2916
  className: "h-4 w-4 shrink-0 text-primary",
@@ -2460,14 +2921,14 @@ function CheckGlyph3() {
2460
2921
  strokeLinecap: "round",
2461
2922
  strokeLinejoin: "round",
2462
2923
  "aria-hidden": true,
2463
- children: /* @__PURE__ */ jsx10("path", { d: "M20 6 9 17l-5-5" })
2924
+ children: /* @__PURE__ */ jsx11("path", { d: "M20 6 9 17l-5-5" })
2464
2925
  }
2465
2926
  );
2466
2927
  }
2467
2928
  function Benefit({ children }) {
2468
- return /* @__PURE__ */ jsxs8("li", { className: "flex items-start gap-2.5 text-sm text-foreground", children: [
2469
- /* @__PURE__ */ jsx10("span", { className: "mt-0.5", children: /* @__PURE__ */ jsx10(CheckGlyph3, {}) }),
2470
- /* @__PURE__ */ jsx10("span", { children })
2929
+ return /* @__PURE__ */ jsxs9("li", { className: "flex items-start gap-2.5 text-sm text-foreground", children: [
2930
+ /* @__PURE__ */ jsx11("span", { className: "mt-0.5", children: /* @__PURE__ */ jsx11(CheckGlyph3, {}) }),
2931
+ /* @__PURE__ */ jsx11("span", { children })
2471
2932
  ] });
2472
2933
  }
2473
2934
  function SeatPaywall({
@@ -2481,30 +2942,30 @@ function SeatPaywall({
2481
2942
  footnote
2482
2943
  }) {
2483
2944
  const { pending, run } = usePending();
2484
- return /* @__PURE__ */ jsx10("div", { className: "flex min-h-[60vh] w-full items-center justify-center p-6", children: /* @__PURE__ */ jsxs8("div", { className: "w-full max-w-md rounded-2xl border border-border bg-card p-8 shadow-sm", children: [
2485
- /* @__PURE__ */ jsx10("p", { className: "text-xs font-medium uppercase tracking-wide text-muted-foreground", children: product }),
2486
- /* @__PURE__ */ jsxs8("h1", { className: "mt-2 text-2xl font-semibold tracking-tight text-foreground", children: [
2945
+ return /* @__PURE__ */ jsx11("div", { className: "flex min-h-[60vh] w-full items-center justify-center p-6", children: /* @__PURE__ */ jsxs9("div", { className: "w-full max-w-md rounded-2xl border border-border bg-card p-8 shadow-sm", children: [
2946
+ /* @__PURE__ */ jsx11("p", { className: "text-xs font-medium uppercase tracking-wide text-muted-foreground", children: product }),
2947
+ /* @__PURE__ */ jsxs9("h1", { className: "mt-2 text-2xl font-semibold tracking-tight text-foreground", children: [
2487
2948
  "Unlock ",
2488
2949
  product
2489
2950
  ] }),
2490
- tagline && /* @__PURE__ */ jsx10("p", { className: "mt-2 text-sm text-muted-foreground", children: tagline }),
2491
- /* @__PURE__ */ jsxs8("div", { className: "mt-6 flex items-baseline gap-1.5", children: [
2492
- /* @__PURE__ */ jsxs8("span", { className: "text-3xl font-semibold text-foreground", children: [
2951
+ tagline && /* @__PURE__ */ jsx11("p", { className: "mt-2 text-sm text-muted-foreground", children: tagline }),
2952
+ /* @__PURE__ */ jsxs9("div", { className: "mt-6 flex items-baseline gap-1.5", children: [
2953
+ /* @__PURE__ */ jsxs9("span", { className: "text-3xl font-semibold text-foreground", children: [
2493
2954
  "$",
2494
2955
  priceUsd
2495
2956
  ] }),
2496
- /* @__PURE__ */ jsx10("span", { className: "text-sm text-muted-foreground", children: "/mo" })
2957
+ /* @__PURE__ */ jsx11("span", { className: "text-sm text-muted-foreground", children: "/mo" })
2497
2958
  ] }),
2498
- /* @__PURE__ */ jsxs8("p", { className: "mt-1 text-sm text-muted-foreground", children: [
2959
+ /* @__PURE__ */ jsxs9("p", { className: "mt-1 text-sm text-muted-foreground", children: [
2499
2960
  "Includes $",
2500
2961
  includedUsageUsd,
2501
2962
  "/mo of AI usage"
2502
2963
  ] }),
2503
- /* @__PURE__ */ jsx10("ul", { className: "mt-6 space-y-2.5", children: (benefits ?? [
2964
+ /* @__PURE__ */ jsx11("ul", { className: "mt-6 space-y-2.5", children: (benefits ?? [
2504
2965
  `Full access to ${product}`,
2505
2966
  `$${includedUsageUsd}/mo of AI usage included, every month`
2506
- ]).map((benefit, i) => /* @__PURE__ */ jsx10(Benefit, { children: benefit }, i)) }),
2507
- /* @__PURE__ */ jsx10(
2967
+ ]).map((benefit, i) => /* @__PURE__ */ jsx11(Benefit, { children: benefit }, i)) }),
2968
+ /* @__PURE__ */ jsx11(
2508
2969
  "button",
2509
2970
  {
2510
2971
  type: "button",
@@ -2514,13 +2975,13 @@ function SeatPaywall({
2514
2975
  children: pending ? "Opening checkout\u2026" : ctaLabel ?? `Unlock ${product}`
2515
2976
  }
2516
2977
  ),
2517
- footnote && /* @__PURE__ */ jsx10("p", { className: "mt-3 text-center text-xs text-muted-foreground/70", children: footnote })
2978
+ footnote && /* @__PURE__ */ jsx11("p", { className: "mt-3 text-center text-xs text-muted-foreground/70", children: footnote })
2518
2979
  ] }) });
2519
2980
  }
2520
2981
 
2521
2982
  // src/web-react/agent-session-controls.tsx
2522
- import { useMemo as useMemo6, useState as useState11 } from "react";
2523
- import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
2983
+ import { useMemo as useMemo7, useState as useState13 } from "react";
2984
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
2524
2985
  var HARNESS_LABELS = {
2525
2986
  opencode: "OpenCode (any model)",
2526
2987
  "claude-code": "Claude Code (Anthropic)",
@@ -2540,12 +3001,12 @@ function harnessLabel(h) {
2540
3001
  return HARNESS_LABELS[h] ?? h;
2541
3002
  }
2542
3003
  function ChevronDown2({ className }) {
2543
- return /* @__PURE__ */ jsx11("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx11("path", { d: "m6 9 6 6 6-6" }) });
3004
+ return /* @__PURE__ */ jsx12("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx12("path", { d: "m6 9 6 6 6-6" }) });
2544
3005
  }
2545
3006
  function GearGlyph({ className }) {
2546
- return /* @__PURE__ */ jsxs9("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
2547
- /* @__PURE__ */ jsx11("circle", { cx: "12", cy: "12", r: "3" }),
2548
- /* @__PURE__ */ jsx11("path", { d: "M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1Z" })
3007
+ return /* @__PURE__ */ jsxs10("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
3008
+ /* @__PURE__ */ jsx12("circle", { cx: "12", cy: "12", r: "3" }),
3009
+ /* @__PURE__ */ jsx12("path", { d: "M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1Z" })
2549
3010
  ] });
2550
3011
  }
2551
3012
  var FOCUS_RING = "focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background";
@@ -2554,11 +3015,11 @@ function HarnessPicker({
2554
3015
  onChange,
2555
3016
  available
2556
3017
  }) {
2557
- const [open, setOpen] = useState11(false);
3018
+ const [open, setOpen] = useState13(false);
2558
3019
  const { containerRef, triggerProps } = usePopover(open, setOpen);
2559
3020
  const options = available ?? Object.keys(HARNESS_LABELS);
2560
- return /* @__PURE__ */ jsxs9("div", { ref: containerRef, className: "relative inline-flex", children: [
2561
- /* @__PURE__ */ jsxs9(
3021
+ return /* @__PURE__ */ jsxs10("div", { ref: containerRef, className: "relative inline-flex", children: [
3022
+ /* @__PURE__ */ jsxs10(
2562
3023
  "button",
2563
3024
  {
2564
3025
  type: "button",
@@ -2567,12 +3028,12 @@ function HarnessPicker({
2567
3028
  title: "Agent backend",
2568
3029
  className: `inline-flex w-full items-center justify-between gap-1.5 rounded-lg border border-border bg-card px-3 py-1.5 text-sm font-medium text-foreground transition hover:bg-accent/30 ${FOCUS_RING}`,
2569
3030
  children: [
2570
- /* @__PURE__ */ jsx11("span", { className: "truncate", children: harnessLabel(value) }),
2571
- /* @__PURE__ */ jsx11(ChevronDown2, { className: "h-3.5 w-3.5 text-muted-foreground" })
3031
+ /* @__PURE__ */ jsx12("span", { className: "truncate", children: harnessLabel(value) }),
3032
+ /* @__PURE__ */ jsx12(ChevronDown2, { className: "h-3.5 w-3.5 text-muted-foreground" })
2572
3033
  ]
2573
3034
  }
2574
3035
  ),
2575
- open && /* @__PURE__ */ jsx11("div", { role: "menu", className: "absolute bottom-full left-0 z-50 mb-2 max-h-64 w-full min-w-[220px] overflow-y-auto rounded-xl border border-border bg-card p-1 shadow-lg", children: options.map((h) => /* @__PURE__ */ jsx11(
3036
+ open && /* @__PURE__ */ jsx12("div", { role: "menu", className: "absolute bottom-full left-0 z-50 mb-2 max-h-64 w-full min-w-[220px] overflow-y-auto rounded-xl border border-border bg-card p-1 shadow-lg", children: options.map((h) => /* @__PURE__ */ jsx12(
2576
3037
  "button",
2577
3038
  {
2578
3039
  type: "button",
@@ -2591,7 +3052,7 @@ function HarnessPicker({
2591
3052
  }
2592
3053
  function useCoherentHandlers(props) {
2593
3054
  const { model, models, harness, onModelChange, onHarnessChange } = props;
2594
- const canonicalIds = useMemo6(() => models.map((m) => m.id), [models]);
3055
+ const canonicalIds = useMemo7(() => models.map((m) => m.id), [models]);
2595
3056
  const onModel = (next) => {
2596
3057
  onModelChange(next);
2597
3058
  const nextHarness = snapHarnessToModel(harness, next);
@@ -2619,11 +3080,11 @@ function AgentSessionControls(props) {
2619
3080
  className
2620
3081
  } = props;
2621
3082
  const { onModel, onHarness } = useCoherentHandlers(props);
2622
- const [open, setOpen] = useState11(false);
3083
+ const [open, setOpen] = useState13(false);
2623
3084
  const { containerRef: popoverRef, triggerProps } = usePopover(open, setOpen);
2624
3085
  const selectedModel = models.find((m) => m.id === model);
2625
3086
  const showEffort = selectedModel?.supportsReasoning ?? true;
2626
- const modelPicker = /* @__PURE__ */ jsx11(
3087
+ const modelPicker = /* @__PURE__ */ jsx12(
2627
3088
  ModelPicker,
2628
3089
  {
2629
3090
  value: model,
@@ -2634,17 +3095,17 @@ function AgentSessionControls(props) {
2634
3095
  }
2635
3096
  );
2636
3097
  if (layout === "inline") {
2637
- return /* @__PURE__ */ jsxs9("div", { className: `flex items-center gap-1.5 ${className ?? ""}`, children: [
3098
+ return /* @__PURE__ */ jsxs10("div", { className: `flex items-center gap-1.5 ${className ?? ""}`, children: [
2638
3099
  modelPicker,
2639
- showHarness && /* @__PURE__ */ jsx11(HarnessPicker, { value: harness, onChange: onHarness, available: availableHarnesses }),
2640
- showEffort && /* @__PURE__ */ jsx11(EffortPicker, { value: effort, onChange: onEffortChange })
3100
+ showHarness && /* @__PURE__ */ jsx12(HarnessPicker, { value: harness, onChange: onHarness, available: availableHarnesses }),
3101
+ showEffort && /* @__PURE__ */ jsx12(EffortPicker, { value: effort, onChange: onEffortChange })
2641
3102
  ] });
2642
3103
  }
2643
3104
  const hasAdvanced = showHarness || showEffort;
2644
- return /* @__PURE__ */ jsxs9("div", { className: `flex items-center gap-1.5 ${className ?? ""}`, children: [
3105
+ return /* @__PURE__ */ jsxs10("div", { className: `flex items-center gap-1.5 ${className ?? ""}`, children: [
2645
3106
  modelPicker,
2646
- hasAdvanced && /* @__PURE__ */ jsxs9("div", { ref: popoverRef, className: "relative inline-flex", children: [
2647
- /* @__PURE__ */ jsx11(
3107
+ hasAdvanced && /* @__PURE__ */ jsxs10("div", { ref: popoverRef, className: "relative inline-flex", children: [
3108
+ /* @__PURE__ */ jsx12(
2648
3109
  "button",
2649
3110
  {
2650
3111
  type: "button",
@@ -2653,19 +3114,19 @@ function AgentSessionControls(props) {
2653
3114
  title: "Model settings \u2014 pick the agent backend and how hard it thinks",
2654
3115
  className: `flex h-8 w-8 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted hover:text-foreground data-[state=open]:bg-muted ${FOCUS_RING}`,
2655
3116
  "data-state": open ? "open" : "closed",
2656
- children: /* @__PURE__ */ jsx11(GearGlyph, { className: "h-4 w-4" })
3117
+ children: /* @__PURE__ */ jsx12(GearGlyph, { className: "h-4 w-4" })
2657
3118
  }
2658
3119
  ),
2659
- open && /* @__PURE__ */ jsxs9("div", { className: "absolute bottom-full left-0 z-50 mb-2 w-72 space-y-3 rounded-xl border border-border bg-card p-3 shadow-lg", children: [
2660
- showHarness && /* @__PURE__ */ jsxs9("div", { className: "space-y-1.5", children: [
2661
- /* @__PURE__ */ jsx11("p", { className: "text-xs font-medium text-foreground", children: "Agent backend" }),
2662
- /* @__PURE__ */ jsx11(HarnessPicker, { value: harness, onChange: onHarness, available: availableHarnesses }),
2663
- /* @__PURE__ */ jsx11("p", { className: "text-[11px] leading-snug text-muted-foreground", children: "The engine that runs the agent. Switching it keeps your model choice compatible." })
3120
+ open && /* @__PURE__ */ jsxs10("div", { className: "absolute bottom-full left-0 z-50 mb-2 w-72 space-y-3 rounded-xl border border-border bg-card p-3 shadow-lg", children: [
3121
+ showHarness && /* @__PURE__ */ jsxs10("div", { className: "space-y-1.5", children: [
3122
+ /* @__PURE__ */ jsx12("p", { className: "text-xs font-medium text-foreground", children: "Agent backend" }),
3123
+ /* @__PURE__ */ jsx12(HarnessPicker, { value: harness, onChange: onHarness, available: availableHarnesses }),
3124
+ /* @__PURE__ */ jsx12("p", { className: "text-[11px] leading-snug text-muted-foreground", children: "The engine that runs the agent. Switching it keeps your model choice compatible." })
2664
3125
  ] }),
2665
- showEffort && /* @__PURE__ */ jsxs9("div", { className: "space-y-1.5", children: [
2666
- /* @__PURE__ */ jsx11("p", { className: "text-xs font-medium text-foreground", children: "Thinking" }),
2667
- /* @__PURE__ */ jsx11(EffortPicker, { value: effort, onChange: onEffortChange, label: "" }),
2668
- /* @__PURE__ */ jsx11("p", { className: "text-[11px] leading-snug text-muted-foreground", children: "How hard the agent thinks before answering. Higher is slower but more thorough." })
3126
+ showEffort && /* @__PURE__ */ jsxs10("div", { className: "space-y-1.5", children: [
3127
+ /* @__PURE__ */ jsx12("p", { className: "text-xs font-medium text-foreground", children: "Thinking" }),
3128
+ /* @__PURE__ */ jsx12(EffortPicker, { value: effort, onChange: onEffortChange, label: "" }),
3129
+ /* @__PURE__ */ jsx12("p", { className: "text-[11px] leading-snug text-muted-foreground", children: "How hard the agent thinks before answering. Higher is slower but more thorough." })
2669
3130
  ] })
2670
3131
  ] })
2671
3132
  ] })
@@ -2673,7 +3134,7 @@ function AgentSessionControls(props) {
2673
3134
  }
2674
3135
 
2675
3136
  // src/web-react/index.tsx
2676
- import { Fragment as Fragment5, jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
3137
+ import { Fragment as Fragment5, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
2677
3138
  function formatModelCost(msg, models) {
2678
3139
  if (msg.promptTokens == null && msg.completionTokens == null) return null;
2679
3140
  const pricing = models.find((m) => m.id === msg.modelUsed)?.pricing;
@@ -2687,41 +3148,41 @@ function formatTokensPerSecond(msg) {
2687
3148
  return `${Math.round(msg.completionTokens / (msg.durationMs / 1e3))} tok/s`;
2688
3149
  }
2689
3150
  function RunDrillIn({ run, onClose }) {
2690
- return /* @__PURE__ */ jsxs10("div", { className: "fixed inset-y-0 right-0 z-50 flex w-[480px] max-w-full flex-col border-l border-border bg-card shadow-xl", children: [
2691
- /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2 border-b border-border px-4 py-3", children: [
2692
- /* @__PURE__ */ jsx12(
3151
+ return /* @__PURE__ */ jsxs11("div", { className: "fixed inset-y-0 right-0 z-50 flex w-[480px] max-w-full flex-col border-l border-border bg-card shadow-xl", children: [
3152
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2 border-b border-border px-4 py-3", children: [
3153
+ /* @__PURE__ */ jsx13(
2693
3154
  "span",
2694
3155
  {
2695
3156
  className: `h-2 w-2 shrink-0 rounded-full ${run.status === "running" ? "bg-warning" : run.status === "error" ? "bg-destructive" : "bg-success"}`
2696
3157
  }
2697
3158
  ),
2698
- /* @__PURE__ */ jsxs10("div", { className: "min-w-0 flex-1", children: [
2699
- /* @__PURE__ */ jsx12("p", { className: "truncate text-sm font-semibold", children: run.title }),
2700
- /* @__PURE__ */ jsx12("p", { className: "truncate font-mono text-[11px] text-muted-foreground", children: run.toolName })
3159
+ /* @__PURE__ */ jsxs11("div", { className: "min-w-0 flex-1", children: [
3160
+ /* @__PURE__ */ jsx13("p", { className: "truncate text-sm font-semibold", children: run.title }),
3161
+ /* @__PURE__ */ jsx13("p", { className: "truncate font-mono text-[11px] text-muted-foreground", children: run.toolName })
2701
3162
  ] }),
2702
- /* @__PURE__ */ jsx12(
3163
+ /* @__PURE__ */ jsx13(
2703
3164
  "button",
2704
3165
  {
2705
3166
  type: "button",
2706
3167
  onClick: onClose,
2707
3168
  "aria-label": "Close",
2708
3169
  className: "rounded-md p-1.5 text-muted-foreground transition hover:bg-accent/30 hover:text-foreground",
2709
- children: /* @__PURE__ */ jsx12("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx12("path", { d: "M18 6 6 18M6 6l12 12" }) })
3170
+ children: /* @__PURE__ */ jsx13("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx13("path", { d: "M18 6 6 18M6 6l12 12" }) })
2710
3171
  }
2711
3172
  )
2712
3173
  ] }),
2713
- /* @__PURE__ */ jsxs10("div", { className: "flex-1 space-y-3 overflow-y-auto p-4", children: [
2714
- run.steps.length === 0 && /* @__PURE__ */ jsx12("p", { className: "text-sm text-muted-foreground", children: "No steps recorded yet." }),
2715
- run.steps.map((step, i) => /* @__PURE__ */ jsxs10("div", { className: "rounded-lg border border-border/60 bg-background", children: [
2716
- /* @__PURE__ */ jsxs10("div", { className: "flex items-baseline gap-2 border-b border-border/40 px-3 py-1.5", children: [
2717
- /* @__PURE__ */ jsx12("span", { className: `font-mono text-[11px] ${step.status === "error" ? "text-destructive" : "text-muted-foreground"}`, children: step.status === "error" ? "\u2717" : "$" }),
2718
- /* @__PURE__ */ jsx12("code", { className: "min-w-0 flex-1 truncate font-mono text-xs", children: step.label }),
2719
- /* @__PURE__ */ jsx12("span", { className: "shrink-0 text-[10px] text-muted-foreground", children: new Date(step.at).toLocaleTimeString() })
3174
+ /* @__PURE__ */ jsxs11("div", { className: "flex-1 space-y-3 overflow-y-auto p-4", children: [
3175
+ run.steps.length === 0 && /* @__PURE__ */ jsx13("p", { className: "text-sm text-muted-foreground", children: "No steps recorded yet." }),
3176
+ run.steps.map((step, i) => /* @__PURE__ */ jsxs11("div", { className: "rounded-lg border border-border/60 bg-background", children: [
3177
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-baseline gap-2 border-b border-border/40 px-3 py-1.5", children: [
3178
+ /* @__PURE__ */ jsx13("span", { className: `font-mono text-[11px] ${step.status === "error" ? "text-destructive" : "text-muted-foreground"}`, children: step.status === "error" ? "\u2717" : "$" }),
3179
+ /* @__PURE__ */ jsx13("code", { className: "min-w-0 flex-1 truncate font-mono text-xs", children: step.label }),
3180
+ /* @__PURE__ */ jsx13("span", { className: "shrink-0 text-[10px] text-muted-foreground", children: new Date(step.at).toLocaleTimeString() })
2720
3181
  ] }),
2721
- step.detail && /* @__PURE__ */ jsx12("pre", { className: "max-h-48 overflow-auto whitespace-pre-wrap px-3 py-2 font-mono text-[11px] leading-relaxed text-muted-foreground", children: step.detail })
3182
+ step.detail && /* @__PURE__ */ jsx13("pre", { className: "max-h-48 overflow-auto whitespace-pre-wrap px-3 py-2 font-mono text-[11px] leading-relaxed text-muted-foreground", children: step.detail })
2722
3183
  ] }, i))
2723
3184
  ] }),
2724
- /* @__PURE__ */ jsx12("p", { className: "border-t border-border px-4 py-2 text-[11px] text-muted-foreground", children: "Readonly drill-in. Follow up in the main chat." })
3185
+ /* @__PURE__ */ jsx13("p", { className: "border-t border-border px-4 py-2 text-[11px] text-muted-foreground", children: "Readonly drill-in. Follow up in the main chat." })
2725
3186
  ] });
2726
3187
  }
2727
3188
  function pendingApprovalOf(call) {
@@ -2735,20 +3196,20 @@ function ChatEmptyState({
2735
3196
  subline = "Describe the outcome you want. The agent works through it step by step, and pauses for your approval before anything irreversible.",
2736
3197
  doors
2737
3198
  }) {
2738
- return /* @__PURE__ */ jsxs10("div", { className: "mx-auto flex w-full max-w-2xl flex-col items-center px-6 py-12 text-center sm:py-20", children: [
2739
- /* @__PURE__ */ jsx12("span", { className: "mb-5 inline-flex h-14 w-14 items-center justify-center rounded-2xl bg-primary/10 ring-1 ring-primary/15", children: /* @__PURE__ */ jsx12(BrandMark, { size: 32, className: "shrink-0" }) }),
2740
- /* @__PURE__ */ jsx12("p", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-muted-foreground", children: productName }),
2741
- /* @__PURE__ */ jsx12("h2", { className: "mt-1.5 text-balance text-2xl font-semibold leading-tight text-foreground sm:text-[28px]", children: headline }),
2742
- subline && /* @__PURE__ */ jsx12("p", { className: "mt-3 max-w-md text-[15px] leading-relaxed text-muted-foreground", children: subline }),
2743
- doors && doors.length > 0 && /* @__PURE__ */ jsx12("div", { className: "mt-7 grid w-full gap-2.5 sm:grid-cols-3", children: doors.slice(0, 3).map((door, i) => /* @__PURE__ */ jsxs10(
3199
+ return /* @__PURE__ */ jsxs11("div", { className: "mx-auto flex w-full max-w-2xl flex-col items-center px-6 py-12 text-center sm:py-20", children: [
3200
+ /* @__PURE__ */ jsx13("span", { className: "mb-5 inline-flex h-14 w-14 items-center justify-center rounded-2xl bg-primary/10 ring-1 ring-primary/15", children: /* @__PURE__ */ jsx13(BrandMark, { size: 32, className: "shrink-0" }) }),
3201
+ /* @__PURE__ */ jsx13("p", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-muted-foreground", children: productName }),
3202
+ /* @__PURE__ */ jsx13("h2", { className: "mt-1.5 text-balance text-2xl font-semibold leading-tight text-foreground sm:text-[28px]", children: headline }),
3203
+ subline && /* @__PURE__ */ jsx13("p", { className: "mt-3 max-w-md text-[15px] leading-relaxed text-muted-foreground", children: subline }),
3204
+ doors && doors.length > 0 && /* @__PURE__ */ jsx13("div", { className: "mt-7 grid w-full gap-2.5 sm:grid-cols-3", children: doors.slice(0, 3).map((door, i) => /* @__PURE__ */ jsxs11(
2744
3205
  "button",
2745
3206
  {
2746
3207
  type: "button",
2747
3208
  onClick: door.onSelect,
2748
3209
  className: "group flex min-h-[44px] flex-col items-start rounded-xl border border-border bg-card px-4 py-3 text-left transition hover:border-primary/40 hover:bg-accent/30 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
2749
3210
  children: [
2750
- /* @__PURE__ */ jsx12("span", { className: "text-sm font-semibold text-foreground", children: door.label }),
2751
- door.description && /* @__PURE__ */ jsx12("span", { className: "mt-0.5 text-[12px] leading-snug text-muted-foreground", children: door.description })
3211
+ /* @__PURE__ */ jsx13("span", { className: "text-sm font-semibold text-foreground", children: door.label }),
3212
+ door.description && /* @__PURE__ */ jsx13("span", { className: "mt-0.5 text-[12px] leading-snug text-muted-foreground", children: door.description })
2752
3213
  ]
2753
3214
  },
2754
3215
  i
@@ -2757,26 +3218,26 @@ function ChatEmptyState({
2757
3218
  }
2758
3219
  function ToolGlyph({ name, className }) {
2759
3220
  if (name.startsWith("sandbox_")) {
2760
- return /* @__PURE__ */ jsxs10("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
2761
- /* @__PURE__ */ jsx12("polyline", { points: "4 17 10 11 4 5" }),
2762
- /* @__PURE__ */ jsx12("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
3221
+ return /* @__PURE__ */ jsxs11("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
3222
+ /* @__PURE__ */ jsx13("polyline", { points: "4 17 10 11 4 5" }),
3223
+ /* @__PURE__ */ jsx13("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
2763
3224
  ] });
2764
3225
  }
2765
3226
  if (name === "submit_proposal") {
2766
- return /* @__PURE__ */ jsxs10("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
2767
- /* @__PURE__ */ jsx12("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
2768
- /* @__PURE__ */ jsx12("path", { d: "M14 2v6h6M9 15l2 2 4-4" })
3227
+ return /* @__PURE__ */ jsxs11("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
3228
+ /* @__PURE__ */ jsx13("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
3229
+ /* @__PURE__ */ jsx13("path", { d: "M14 2v6h6M9 15l2 2 4-4" })
2769
3230
  ] });
2770
3231
  }
2771
3232
  if (name === "schedule_followup") {
2772
- return /* @__PURE__ */ jsxs10("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": true, children: [
2773
- /* @__PURE__ */ jsx12("circle", { cx: "12", cy: "12", r: "9" }),
2774
- /* @__PURE__ */ jsx12("path", { d: "M12 7v5l3 3" })
3233
+ return /* @__PURE__ */ jsxs11("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": true, children: [
3234
+ /* @__PURE__ */ jsx13("circle", { cx: "12", cy: "12", r: "9" }),
3235
+ /* @__PURE__ */ jsx13("path", { d: "M12 7v5l3 3" })
2775
3236
  ] });
2776
3237
  }
2777
- return /* @__PURE__ */ jsxs10("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
2778
- /* @__PURE__ */ jsx12("path", { d: "M12 3v3m0 12v3M3 12h3m12 0h3" }),
2779
- /* @__PURE__ */ jsx12("circle", { cx: "12", cy: "12", r: "4" })
3238
+ return /* @__PURE__ */ jsxs11("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
3239
+ /* @__PURE__ */ jsx13("path", { d: "M12 3v3m0 12v3M3 12h3m12 0h3" }),
3240
+ /* @__PURE__ */ jsx13("circle", { cx: "12", cy: "12", r: "4" })
2780
3241
  ] });
2781
3242
  }
2782
3243
  function toolOutcomeOf(call) {
@@ -2844,40 +3305,40 @@ function truncate(v, max = 240) {
2844
3305
  function KvRows({ data }) {
2845
3306
  const entries = Object.entries(data).filter(([, v]) => v !== void 0 && v !== null && v !== "");
2846
3307
  if (!entries.length) return null;
2847
- return /* @__PURE__ */ jsx12("dl", { className: "grid grid-cols-[auto_1fr] gap-x-3 gap-y-1", children: entries.map(([k, v]) => /* @__PURE__ */ jsxs10("div", { className: "contents", children: [
2848
- /* @__PURE__ */ jsx12("dt", { className: "font-mono text-[11px] text-muted-foreground", children: k }),
2849
- /* @__PURE__ */ jsx12("dd", { className: "min-w-0 whitespace-pre-wrap break-words font-mono text-[11px] text-muted-foreground", children: truncate(v) })
3308
+ return /* @__PURE__ */ jsx13("dl", { className: "grid grid-cols-[auto_1fr] gap-x-3 gap-y-1", children: entries.map(([k, v]) => /* @__PURE__ */ jsxs11("div", { className: "contents", children: [
3309
+ /* @__PURE__ */ jsx13("dt", { className: "font-mono text-[11px] text-muted-foreground", children: k }),
3310
+ /* @__PURE__ */ jsx13("dd", { className: "min-w-0 whitespace-pre-wrap break-words font-mono text-[11px] text-muted-foreground", children: truncate(v) })
2850
3311
  ] }, k)) });
2851
3312
  }
2852
3313
  function ShellDetail({ call }) {
2853
3314
  const outcome = toolOutcomeOf(call);
2854
3315
  const r = outcome?.result ?? {};
2855
- return /* @__PURE__ */ jsxs10("div", { className: "overflow-hidden rounded-md bg-zinc-900 font-mono text-[11px] leading-relaxed", children: [
2856
- /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2 px-3 pt-2 text-zinc-400", children: [
2857
- /* @__PURE__ */ jsx12("span", { className: "select-none text-zinc-500", children: "$" }),
2858
- /* @__PURE__ */ jsx12("span", { className: "min-w-0 flex-1 truncate text-zinc-200", children: String(call.args?.command ?? "") }),
2859
- r.exitCode != null && /* @__PURE__ */ jsxs10("span", { className: r.exitCode === 0 ? "text-success" : "text-destructive", children: [
3316
+ return /* @__PURE__ */ jsxs11("div", { className: "overflow-hidden rounded-md bg-zinc-900 font-mono text-[11px] leading-relaxed", children: [
3317
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2 px-3 pt-2 text-zinc-400", children: [
3318
+ /* @__PURE__ */ jsx13("span", { className: "select-none text-zinc-500", children: "$" }),
3319
+ /* @__PURE__ */ jsx13("span", { className: "min-w-0 flex-1 truncate text-zinc-200", children: String(call.args?.command ?? "") }),
3320
+ r.exitCode != null && /* @__PURE__ */ jsxs11("span", { className: r.exitCode === 0 ? "text-success" : "text-destructive", children: [
2860
3321
  "exit ",
2861
3322
  r.exitCode
2862
3323
  ] })
2863
3324
  ] }),
2864
- /* @__PURE__ */ jsx12("pre", { className: "max-h-56 overflow-auto whitespace-pre-wrap px-3 pb-2.5 pt-1.5 text-zinc-300", children: outcome?.ok === false ? outcome.message ?? "failed" : [r.stdout, r.stderr].filter(Boolean).join("\n") || "(no output)" })
3325
+ /* @__PURE__ */ jsx13("pre", { className: "max-h-56 overflow-auto whitespace-pre-wrap px-3 pb-2.5 pt-1.5 text-zinc-300", children: outcome?.ok === false ? outcome.message ?? "failed" : [r.stdout, r.stderr].filter(Boolean).join("\n") || "(no output)" })
2865
3326
  ] });
2866
3327
  }
2867
3328
  function DefaultToolDetail({ call }) {
2868
3329
  const result = call.result;
2869
3330
  const envelope = typeof result === "object" && result !== null ? result : null;
2870
- return /* @__PURE__ */ jsxs10("div", { className: "space-y-2", children: [
2871
- call.args && Object.keys(call.args).length > 0 && /* @__PURE__ */ jsxs10("div", { children: [
2872
- /* @__PURE__ */ jsx12("p", { className: "mb-1 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground", children: "Called with" }),
2873
- /* @__PURE__ */ jsx12(KvRows, { data: call.args })
3331
+ return /* @__PURE__ */ jsxs11("div", { className: "space-y-2", children: [
3332
+ call.args && Object.keys(call.args).length > 0 && /* @__PURE__ */ jsxs11("div", { children: [
3333
+ /* @__PURE__ */ jsx13("p", { className: "mb-1 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground", children: "Called with" }),
3334
+ /* @__PURE__ */ jsx13(KvRows, { data: call.args })
2874
3335
  ] }),
2875
- envelope ? /* @__PURE__ */ jsxs10("div", { children: [
2876
- /* @__PURE__ */ jsx12("p", { className: "mb-1 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground", children: envelope.ok === false ? "Failed" : "Result" }),
2877
- envelope.ok === false ? /* @__PURE__ */ jsx12("p", { className: "text-xs text-destructive", children: envelope.message ?? "Tool failed" }) : envelope.result && typeof envelope.result === "object" ? /* @__PURE__ */ jsx12(KvRows, { data: envelope.result }) : envelope.result != null ? /* @__PURE__ */ jsx12("p", { className: "font-mono text-[11px] text-muted-foreground", children: truncate(envelope.result) }) : null
2878
- ] }) : result != null ? /* @__PURE__ */ jsxs10("div", { children: [
2879
- /* @__PURE__ */ jsx12("p", { className: "mb-1 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground", children: "Result" }),
2880
- /* @__PURE__ */ jsx12("p", { className: "font-mono text-[11px] text-muted-foreground", children: truncate(result) })
3336
+ envelope ? /* @__PURE__ */ jsxs11("div", { children: [
3337
+ /* @__PURE__ */ jsx13("p", { className: "mb-1 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground", children: envelope.ok === false ? "Failed" : "Result" }),
3338
+ envelope.ok === false ? /* @__PURE__ */ jsx13("p", { className: "text-xs text-destructive", children: envelope.message ?? "Tool failed" }) : envelope.result && typeof envelope.result === "object" ? /* @__PURE__ */ jsx13(KvRows, { data: envelope.result }) : envelope.result != null ? /* @__PURE__ */ jsx13("p", { className: "font-mono text-[11px] text-muted-foreground", children: truncate(envelope.result) }) : null
3339
+ ] }) : result != null ? /* @__PURE__ */ jsxs11("div", { children: [
3340
+ /* @__PURE__ */ jsx13("p", { className: "mb-1 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground", children: "Result" }),
3341
+ /* @__PURE__ */ jsx13("p", { className: "font-mono text-[11px] text-muted-foreground", children: truncate(result) })
2881
3342
  ] }) : null
2882
3343
  ] });
2883
3344
  }
@@ -2888,23 +3349,23 @@ function ProposalCard({
2888
3349
  approval,
2889
3350
  renderers
2890
3351
  }) {
2891
- const [expanded, setExpanded] = useState12(false);
3352
+ const [expanded, setExpanded] = useState14(false);
2892
3353
  const { summary, meta } = proposalPreview(call);
2893
3354
  const custom = renderers?.[call.name]?.(call, message);
2894
3355
  const { pending: deciding, run: decide } = usePending();
2895
- return /* @__PURE__ */ jsxs10("div", { className: "w-full max-w-full rounded-xl border border-warning/50 bg-warning/[0.06] text-sm shadow-sm ring-1 ring-warning/10", children: [
2896
- /* @__PURE__ */ jsxs10("div", { className: "flex items-start gap-2.5 px-4 pt-3.5", children: [
2897
- /* @__PURE__ */ jsx12("span", { className: "mt-0.5 inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-warning/15 text-warning", children: /* @__PURE__ */ jsx12(ToolGlyph, { name: call.name, className: "h-3.5 w-3.5" }) }),
2898
- /* @__PURE__ */ jsxs10("div", { className: "min-w-0 flex-1", children: [
2899
- /* @__PURE__ */ jsx12("p", { className: "text-[10px] font-semibold uppercase tracking-wider text-warning-foreground", children: "Needs your approval" }),
2900
- /* @__PURE__ */ jsx12("p", { className: "mt-0.5 text-[15px] font-semibold leading-snug text-foreground", children: friendlyToolTitle(call) }),
2901
- summary && /* @__PURE__ */ jsx12("p", { className: "mt-1 text-[13px] leading-relaxed text-muted-foreground", children: summary }),
2902
- meta.length > 0 && /* @__PURE__ */ jsx12("div", { className: "mt-1.5 flex flex-wrap items-center gap-1.5", children: meta.map((m, i) => /* @__PURE__ */ jsx12("span", { className: "rounded-full bg-muted/60 px-2 py-0.5 text-[11px] font-medium text-muted-foreground", children: m }, i)) })
3356
+ return /* @__PURE__ */ jsxs11("div", { className: "w-full max-w-full rounded-xl border border-warning/50 bg-warning/[0.06] text-sm shadow-sm ring-1 ring-warning/10", children: [
3357
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-start gap-2.5 px-4 pt-3.5", children: [
3358
+ /* @__PURE__ */ jsx13("span", { className: "mt-0.5 inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-warning/15 text-warning", children: /* @__PURE__ */ jsx13(ToolGlyph, { name: call.name, className: "h-3.5 w-3.5" }) }),
3359
+ /* @__PURE__ */ jsxs11("div", { className: "min-w-0 flex-1", children: [
3360
+ /* @__PURE__ */ jsx13("p", { className: "text-[10px] font-semibold uppercase tracking-wider text-warning-foreground", children: "Needs your approval" }),
3361
+ /* @__PURE__ */ jsx13("p", { className: "mt-0.5 text-[15px] font-semibold leading-snug text-foreground", children: friendlyToolTitle(call) }),
3362
+ summary && /* @__PURE__ */ jsx13("p", { className: "mt-1 text-[13px] leading-relaxed text-muted-foreground", children: summary }),
3363
+ meta.length > 0 && /* @__PURE__ */ jsx13("div", { className: "mt-1.5 flex flex-wrap items-center gap-1.5", children: meta.map((m, i) => /* @__PURE__ */ jsx13("span", { className: "rounded-full bg-muted/60 px-2 py-0.5 text-[11px] font-medium text-muted-foreground", children: m }, i)) })
2903
3364
  ] })
2904
3365
  ] }),
2905
- /* @__PURE__ */ jsxs10("div", { className: "flex flex-wrap items-center gap-2 px-4 pb-3.5 pt-3", children: [
2906
- approval ? /* @__PURE__ */ jsxs10(Fragment5, { children: [
2907
- /* @__PURE__ */ jsx12(
3366
+ /* @__PURE__ */ jsxs11("div", { className: "flex flex-wrap items-center gap-2 px-4 pb-3.5 pt-3", children: [
3367
+ approval ? /* @__PURE__ */ jsxs11(Fragment5, { children: [
3368
+ /* @__PURE__ */ jsx13(
2908
3369
  "button",
2909
3370
  {
2910
3371
  type: "button",
@@ -2914,7 +3375,7 @@ function ProposalCard({
2914
3375
  children: "Approve & run"
2915
3376
  }
2916
3377
  ),
2917
- /* @__PURE__ */ jsx12(
3378
+ /* @__PURE__ */ jsx13(
2918
3379
  "button",
2919
3380
  {
2920
3381
  type: "button",
@@ -2924,8 +3385,8 @@ function ProposalCard({
2924
3385
  children: "Reject"
2925
3386
  }
2926
3387
  )
2927
- ] }) : /* @__PURE__ */ jsx12("span", { className: "text-[12px] font-medium text-muted-foreground", children: "Awaiting approval\u2026" }),
2928
- /* @__PURE__ */ jsxs10(
3388
+ ] }) : /* @__PURE__ */ jsx13("span", { className: "text-[12px] font-medium text-muted-foreground", children: "Awaiting approval\u2026" }),
3389
+ /* @__PURE__ */ jsxs11(
2929
3390
  "button",
2930
3391
  {
2931
3392
  type: "button",
@@ -2934,23 +3395,23 @@ function ProposalCard({
2934
3395
  className: "ml-auto inline-flex items-center gap-1 rounded text-[12px] font-medium text-muted-foreground transition hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
2935
3396
  children: [
2936
3397
  expanded ? "Hide details" : "View details",
2937
- /* @__PURE__ */ jsx12(ChevronDown, { className: `h-3 w-3 transition-transform ${expanded ? "rotate-180" : ""}` })
3398
+ /* @__PURE__ */ jsx13(ChevronDown, { className: `h-3 w-3 transition-transform ${expanded ? "rotate-180" : ""}` })
2938
3399
  ]
2939
3400
  }
2940
3401
  )
2941
3402
  ] }),
2942
- expanded && /* @__PURE__ */ jsx12("div", { className: "border-t border-warning/20 px-4 py-3 text-xs", children: custom ?? /* @__PURE__ */ jsx12(DefaultToolDetail, { call }) })
3403
+ expanded && /* @__PURE__ */ jsx13("div", { className: "border-t border-warning/20 px-4 py-3 text-xs", children: custom ?? /* @__PURE__ */ jsx13(DefaultToolDetail, { call }) })
2943
3404
  ] });
2944
3405
  }
2945
3406
  function FollowupCard({ call }) {
2946
3407
  const a = call.args ?? {};
2947
3408
  const when = typeof a.when === "string" ? a.when : typeof a.at === "string" ? a.at : typeof a.schedule === "string" ? a.schedule : null;
2948
- return /* @__PURE__ */ jsxs10("div", { className: "w-fit min-w-[260px] max-w-full rounded-lg border border-border/60 border-l-2 border-l-primary/60 bg-muted/20 px-3 py-2 text-sm", children: [
2949
- /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2", children: [
2950
- /* @__PURE__ */ jsx12(ToolGlyph, { name: call.name, className: "h-3.5 w-3.5 shrink-0 text-primary/80" }),
2951
- /* @__PURE__ */ jsx12("span", { className: "min-w-0 flex-1 truncate font-medium text-foreground", children: friendlyToolTitle(call) })
3409
+ return /* @__PURE__ */ jsxs11("div", { className: "w-fit min-w-[260px] max-w-full rounded-lg border border-border/60 border-l-2 border-l-primary/60 bg-muted/20 px-3 py-2 text-sm", children: [
3410
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2", children: [
3411
+ /* @__PURE__ */ jsx13(ToolGlyph, { name: call.name, className: "h-3.5 w-3.5 shrink-0 text-primary/80" }),
3412
+ /* @__PURE__ */ jsx13("span", { className: "min-w-0 flex-1 truncate font-medium text-foreground", children: friendlyToolTitle(call) })
2952
3413
  ] }),
2953
- when && /* @__PURE__ */ jsx12("p", { className: "mt-0.5 pl-[22px] text-[12px] text-muted-foreground", children: when })
3414
+ when && /* @__PURE__ */ jsx13("p", { className: "mt-0.5 pl-[22px] text-[12px] text-muted-foreground", children: when })
2954
3415
  ] });
2955
3416
  }
2956
3417
  function ToolCallCard({
@@ -2960,13 +3421,13 @@ function ToolCallCard({
2960
3421
  onOpenRun,
2961
3422
  renderers
2962
3423
  }) {
2963
- const [expanded, setExpanded] = useState12(false);
3424
+ const [expanded, setExpanded] = useState14(false);
2964
3425
  const pending = call.status === "done" ? pendingApprovalOf(call) : null;
2965
3426
  const kind = blockKindOf(call);
2966
3427
  const failed = call.status === "error" || toolOutcomeOf(call)?.ok === false;
2967
3428
  const custom = renderers?.[call.name]?.(call, message);
2968
3429
  if (pending) {
2969
- return /* @__PURE__ */ jsx12(
3430
+ return /* @__PURE__ */ jsx13(
2970
3431
  ProposalCard,
2971
3432
  {
2972
3433
  call,
@@ -2978,16 +3439,16 @@ function ToolCallCard({
2978
3439
  );
2979
3440
  }
2980
3441
  if (kind === "followup" && !failed) {
2981
- return /* @__PURE__ */ jsx12(FollowupCard, { call });
3442
+ return /* @__PURE__ */ jsx13(FollowupCard, { call });
2982
3443
  }
2983
3444
  const isCommand = kind === "command";
2984
- return /* @__PURE__ */ jsxs10(
3445
+ return /* @__PURE__ */ jsxs11(
2985
3446
  "div",
2986
3447
  {
2987
3448
  className: `w-fit min-w-[280px] max-w-full rounded-lg border text-xs transition ${failed ? "border-destructive/40 bg-destructive/5" : "border-border/60 bg-muted/20"}`,
2988
3449
  children: [
2989
- /* @__PURE__ */ jsxs10("div", { className: "flex w-full items-center gap-2 px-3 py-2", children: [
2990
- /* @__PURE__ */ jsxs10(
3450
+ /* @__PURE__ */ jsxs11("div", { className: "flex w-full items-center gap-2 px-3 py-2", children: [
3451
+ /* @__PURE__ */ jsxs11(
2991
3452
  "button",
2992
3453
  {
2993
3454
  type: "button",
@@ -2995,14 +3456,14 @@ function ToolCallCard({
2995
3456
  "aria-expanded": expanded,
2996
3457
  className: "flex min-w-0 flex-1 items-center gap-2 rounded text-left focus:outline-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
2997
3458
  children: [
2998
- /* @__PURE__ */ jsx12(
3459
+ /* @__PURE__ */ jsx13(
2999
3460
  "span",
3000
3461
  {
3001
3462
  className: `h-2 w-2 shrink-0 rounded-full ${call.status === "running" ? "animate-pulse bg-warning" : failed ? "bg-destructive" : "bg-success"}`
3002
3463
  }
3003
3464
  ),
3004
- /* @__PURE__ */ jsx12(ToolGlyph, { name: call.name, className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
3005
- /* @__PURE__ */ jsx12(
3465
+ /* @__PURE__ */ jsx13(ToolGlyph, { name: call.name, className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
3466
+ /* @__PURE__ */ jsx13(
3006
3467
  "span",
3007
3468
  {
3008
3469
  className: `min-w-0 flex-1 truncate ${isCommand ? "font-mono text-[12px] tracking-tight text-foreground/90" : "font-medium"}`,
@@ -3012,8 +3473,8 @@ function ToolCallCard({
3012
3473
  ]
3013
3474
  }
3014
3475
  ),
3015
- /* @__PURE__ */ jsx12("span", { className: "shrink-0 text-[11px] text-muted-foreground", children: call.status === "running" ? "running\u2026" : failed ? "failed" : "done" }),
3016
- /* @__PURE__ */ jsx12(
3476
+ /* @__PURE__ */ jsx13("span", { className: "shrink-0 text-[11px] text-muted-foreground", children: call.status === "running" ? "running\u2026" : failed ? "failed" : "done" }),
3477
+ /* @__PURE__ */ jsx13(
3017
3478
  "button",
3018
3479
  {
3019
3480
  type: "button",
@@ -3021,13 +3482,13 @@ function ToolCallCard({
3021
3482
  "aria-label": expanded ? "Collapse details" : "Expand details",
3022
3483
  "aria-expanded": expanded,
3023
3484
  className: "shrink-0 rounded p-0.5 focus:outline-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
3024
- children: /* @__PURE__ */ jsx12(ChevronDown, { className: `h-3 w-3 text-muted-foreground transition-transform ${expanded ? "rotate-180" : ""}` })
3485
+ children: /* @__PURE__ */ jsx13(ChevronDown, { className: `h-3 w-3 text-muted-foreground transition-transform ${expanded ? "rotate-180" : ""}` })
3025
3486
  }
3026
3487
  )
3027
3488
  ] }),
3028
- expanded && /* @__PURE__ */ jsxs10("div", { className: "border-t border-border/40 px-3 py-2.5", children: [
3029
- custom ?? (call.name === "sandbox_run_command" ? /* @__PURE__ */ jsx12(ShellDetail, { call }) : /* @__PURE__ */ jsx12(DefaultToolDetail, { call })),
3030
- onOpenRun && call.name.startsWith("sandbox_") && /* @__PURE__ */ jsx12(
3489
+ expanded && /* @__PURE__ */ jsxs11("div", { className: "border-t border-border/40 px-3 py-2.5", children: [
3490
+ custom ?? (call.name === "sandbox_run_command" ? /* @__PURE__ */ jsx13(ShellDetail, { call }) : /* @__PURE__ */ jsx13(DefaultToolDetail, { call })),
3491
+ onOpenRun && call.name.startsWith("sandbox_") && /* @__PURE__ */ jsx13(
3031
3492
  "button",
3032
3493
  {
3033
3494
  type: "button",
@@ -3042,7 +3503,7 @@ function ToolCallCard({
3042
3503
  );
3043
3504
  }
3044
3505
  function StreamingCaret() {
3045
- return /* @__PURE__ */ jsx12(
3506
+ return /* @__PURE__ */ jsx13(
3046
3507
  "span",
3047
3508
  {
3048
3509
  className: "ml-0.5 inline-block h-[1.1em] w-[3px] translate-y-[2px] animate-pulse rounded-sm bg-foreground/70",
@@ -3057,11 +3518,11 @@ function SegmentText({
3057
3518
  renderBody
3058
3519
  }) {
3059
3520
  const text = useSmoothText(content, streaming);
3060
- const body = useMemo7(() => renderBody(text), [renderBody, text]);
3521
+ const body = useMemo8(() => renderBody(text), [renderBody, text]);
3061
3522
  if (!content.trim() && !showCaret) return null;
3062
- return /* @__PURE__ */ jsxs10("div", { className: "text-base leading-[1.75]", children: [
3523
+ return /* @__PURE__ */ jsxs11("div", { className: "text-base leading-[1.75]", children: [
3063
3524
  body,
3064
- showCaret && /* @__PURE__ */ jsx12(StreamingCaret, {})
3525
+ showCaret && /* @__PURE__ */ jsx13(StreamingCaret, {})
3065
3526
  ] });
3066
3527
  }
3067
3528
  var COLLAPSE_TOOL_RUN_AT = 3;
@@ -3084,7 +3545,7 @@ function SegmentedBody({
3084
3545
  const leftoverToolCalls = (msg.toolCalls ?? []).filter(
3085
3546
  (tc) => !segmentToolIds.has(tc.id)
3086
3547
  );
3087
- const renderToolCard = (call) => /* @__PURE__ */ jsx12(
3548
+ const renderToolCard = (call) => /* @__PURE__ */ jsx13(
3088
3549
  ToolCallCard,
3089
3550
  {
3090
3551
  call,
@@ -3107,9 +3568,9 @@ function SegmentedBody({
3107
3568
  else groups.push({ kind: "tools", index: i, calls: [seg.call] });
3108
3569
  }
3109
3570
  }
3110
- return /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-2", children: [
3571
+ return /* @__PURE__ */ jsxs11("div", { className: "flex flex-col gap-2", children: [
3111
3572
  groups.map(
3112
- (g) => g.kind === "text" ? /* @__PURE__ */ jsx12(
3573
+ (g) => g.kind === "text" ? /* @__PURE__ */ jsx13(
3113
3574
  SegmentText,
3114
3575
  {
3115
3576
  content: g.content,
@@ -3118,24 +3579,24 @@ function SegmentedBody({
3118
3579
  renderBody
3119
3580
  },
3120
3581
  `text-${g.index}`
3121
- ) : !streaming && g.calls.length >= COLLAPSE_TOOL_RUN_AT && !g.calls.some(isImportantTool) ? /* @__PURE__ */ jsxs10(
3582
+ ) : !streaming && g.calls.length >= COLLAPSE_TOOL_RUN_AT && !g.calls.some(isImportantTool) ? /* @__PURE__ */ jsxs11(
3122
3583
  "details",
3123
3584
  {
3124
3585
  className: "rounded-lg border-l-2 border-border/70 bg-muted/20 px-3 py-2",
3125
3586
  children: [
3126
- /* @__PURE__ */ jsxs10("summary", { className: "cursor-pointer select-none text-xs font-medium text-muted-foreground", children: [
3587
+ /* @__PURE__ */ jsxs11("summary", { className: "cursor-pointer select-none text-xs font-medium text-muted-foreground", children: [
3127
3588
  "Worked through ",
3128
3589
  g.calls.length,
3129
3590
  " steps"
3130
3591
  ] }),
3131
- /* @__PURE__ */ jsx12("div", { className: "mt-2 flex flex-col gap-2", children: g.calls.map(renderToolCard) })
3592
+ /* @__PURE__ */ jsx13("div", { className: "mt-2 flex flex-col gap-2", children: g.calls.map(renderToolCard) })
3132
3593
  ]
3133
3594
  },
3134
3595
  `tools-${g.index}`
3135
- ) : /* @__PURE__ */ jsx12("div", { className: "flex flex-col gap-2", children: g.calls.map(renderToolCard) }, `tools-${g.index}`)
3596
+ ) : /* @__PURE__ */ jsx13("div", { className: "flex flex-col gap-2", children: g.calls.map(renderToolCard) }, `tools-${g.index}`)
3136
3597
  ),
3137
3598
  leftoverToolCalls.map(renderToolCard),
3138
- streaming && segments[lastIndex]?.kind === "tool" && /* @__PURE__ */ jsx12(StreamingCaret, {})
3599
+ streaming && segments[lastIndex]?.kind === "tool" && /* @__PURE__ */ jsx13(StreamingCaret, {})
3139
3600
  ] });
3140
3601
  }
3141
3602
  function AssistantMessageImpl({
@@ -3148,44 +3609,45 @@ function AssistantMessageImpl({
3148
3609
  onToolCallClick,
3149
3610
  toolRenderers,
3150
3611
  renderExtras,
3151
- durableCards
3612
+ durableCards,
3613
+ resolveAttachmentUrl
3152
3614
  }) {
3153
3615
  const content = useSmoothText(msg.content, streaming);
3154
3616
  const reasoning = useSmoothText(msg.reasoning ?? "", streaming);
3155
- const body = useMemo7(() => renderBody(content), [renderBody, content]);
3617
+ const body = useMemo8(() => renderBody(content), [renderBody, content]);
3156
3618
  const segments = msg.segments;
3157
3619
  const hasAnswerText = content !== "" || (segments?.some((s) => s.kind === "text" && s.content.trim() !== "") ?? false);
3158
- const reasoningScrollRef = useRef8(null);
3159
- const thinkStartRef = useRef8(null);
3160
- const thinkMsRef = useRef8(null);
3620
+ const reasoningScrollRef = useRef9(null);
3621
+ const thinkStartRef = useRef9(null);
3622
+ const thinkMsRef = useRef9(null);
3161
3623
  if (streaming && reasoning && !hasAnswerText && thinkStartRef.current === null) {
3162
3624
  thinkStartRef.current = performance.now();
3163
3625
  }
3164
3626
  if (hasAnswerText && thinkStartRef.current !== null && thinkMsRef.current === null) {
3165
3627
  thinkMsRef.current = performance.now() - thinkStartRef.current;
3166
3628
  }
3167
- useEffect8(() => {
3629
+ useEffect10(() => {
3168
3630
  const el = reasoningScrollRef.current;
3169
3631
  if (el && streaming && !hasAnswerText) el.scrollTop = el.scrollHeight;
3170
3632
  }, [reasoning, streaming, hasAnswerText]);
3171
3633
  const thinkingSeconds = useThinkingSeconds(
3172
3634
  streaming && !!reasoning && !hasAnswerText
3173
3635
  );
3174
- return /* @__PURE__ */ jsxs10("div", { className: "mx-auto w-full max-w-3xl px-6 py-3", children: [
3175
- /* @__PURE__ */ jsxs10("div", { className: "mb-1 flex items-baseline gap-2 text-[11px] tracking-wide text-muted-foreground", children: [
3176
- /* @__PURE__ */ jsx12("span", { className: "font-semibold uppercase", children: agentLabel }),
3177
- msg.modelUsed && /* @__PURE__ */ jsx12("span", { className: "font-mono normal-case", children: msg.modelUsed }),
3178
- formatTokensPerSecond(msg) && /* @__PURE__ */ jsx12("span", { children: formatTokensPerSecond(msg) }),
3179
- formatModelCost(msg, models) && /* @__PURE__ */ jsx12("span", { children: formatModelCost(msg, models) })
3636
+ return /* @__PURE__ */ jsxs11("div", { className: "mx-auto w-full max-w-3xl px-6 py-3", children: [
3637
+ /* @__PURE__ */ jsxs11("div", { className: "mb-1 flex items-baseline gap-2 text-[11px] tracking-wide text-muted-foreground", children: [
3638
+ /* @__PURE__ */ jsx13("span", { className: "font-semibold uppercase", children: agentLabel }),
3639
+ msg.modelUsed && /* @__PURE__ */ jsx13("span", { className: "font-mono normal-case", children: msg.modelUsed }),
3640
+ formatTokensPerSecond(msg) && /* @__PURE__ */ jsx13("span", { children: formatTokensPerSecond(msg) }),
3641
+ formatModelCost(msg, models) && /* @__PURE__ */ jsx13("span", { children: formatModelCost(msg, models) })
3180
3642
  ] }),
3181
- reasoning && /* @__PURE__ */ jsxs10("details", { className: "mb-2 rounded-lg border-l-2 border-border/70 bg-muted/20 px-3 py-2", open: !hasAnswerText, children: [
3182
- /* @__PURE__ */ jsx12("summary", { className: "cursor-pointer select-none text-xs font-medium text-muted-foreground", children: !hasAnswerText ? /* @__PURE__ */ jsxs10("span", { className: "animate-pulse", children: [
3643
+ reasoning && /* @__PURE__ */ jsxs11("details", { className: "mb-2 rounded-lg border-l-2 border-border/70 bg-muted/20 px-3 py-2", open: !hasAnswerText, children: [
3644
+ /* @__PURE__ */ jsx13("summary", { className: "cursor-pointer select-none text-xs font-medium text-muted-foreground", children: !hasAnswerText ? /* @__PURE__ */ jsxs11("span", { className: "animate-pulse", children: [
3183
3645
  "Thinking",
3184
3646
  thinkingSeconds >= 3 ? ` \xB7 ${thinkingSeconds}s` : "\u2026"
3185
3647
  ] }) : thinkMsRef.current != null ? `Thought for ${Math.max(1, Math.round(thinkMsRef.current / 1e3))}s` : "Thought process" }),
3186
- /* @__PURE__ */ jsx12("div", { ref: reasoningScrollRef, className: "mt-2 max-h-48 overflow-y-auto whitespace-pre-wrap text-[13px] leading-relaxed text-muted-foreground", children: reasoning })
3648
+ /* @__PURE__ */ jsx13("div", { ref: reasoningScrollRef, className: "mt-2 max-h-48 overflow-y-auto whitespace-pre-wrap text-[13px] leading-relaxed text-muted-foreground", children: reasoning })
3187
3649
  ] }),
3188
- segments && segments.length > 0 ? /* @__PURE__ */ jsx12(
3650
+ segments && segments.length > 0 ? /* @__PURE__ */ jsx13(
3189
3651
  SegmentedBody,
3190
3652
  {
3191
3653
  segments,
@@ -3196,12 +3658,12 @@ function AssistantMessageImpl({
3196
3658
  onToolCallClick,
3197
3659
  toolRenderers
3198
3660
  }
3199
- ) : /* @__PURE__ */ jsxs10(Fragment5, { children: [
3200
- /* @__PURE__ */ jsxs10("div", { className: "text-base leading-[1.75]", children: [
3661
+ ) : /* @__PURE__ */ jsxs11(Fragment5, { children: [
3662
+ /* @__PURE__ */ jsxs11("div", { className: "text-base leading-[1.75]", children: [
3201
3663
  body,
3202
- streaming && content && !msg.toolCalls?.length && /* @__PURE__ */ jsx12(StreamingCaret, {})
3664
+ streaming && content && !msg.toolCalls?.length && /* @__PURE__ */ jsx13(StreamingCaret, {})
3203
3665
  ] }),
3204
- msg.toolCalls && msg.toolCalls.length > 0 && /* @__PURE__ */ jsx12("div", { className: "mt-2 flex flex-col gap-1.5", children: msg.toolCalls.map((tc) => /* @__PURE__ */ jsx12(
3666
+ msg.toolCalls && msg.toolCalls.length > 0 && /* @__PURE__ */ jsx13("div", { className: "mt-2 flex flex-col gap-1.5", children: msg.toolCalls.map((tc) => /* @__PURE__ */ jsx13(
3205
3667
  ToolCallCard,
3206
3668
  {
3207
3669
  call: tc,
@@ -3213,7 +3675,7 @@ function AssistantMessageImpl({
3213
3675
  tc.id
3214
3676
  )) })
3215
3677
  ] }),
3216
- durableCards && msg.parts && /* @__PURE__ */ jsx12(
3678
+ durableCards && msg.parts && /* @__PURE__ */ jsx13(
3217
3679
  DurableChatCards,
3218
3680
  {
3219
3681
  ...durableCards,
@@ -3222,13 +3684,21 @@ function AssistantMessageImpl({
3222
3684
  className: "mt-3"
3223
3685
  }
3224
3686
  ),
3225
- renderExtras?.(msg)
3687
+ renderExtras?.(msg),
3688
+ resolveAttachmentUrl && attachmentPartsFromMessageParts(msg.parts).length > 0 && /* @__PURE__ */ jsx13("div", { className: "mt-2", children: /* @__PURE__ */ jsx13(
3689
+ MessageAttachments,
3690
+ {
3691
+ parts: attachmentPartsFromMessageParts(msg.parts),
3692
+ resolveFileUrl: resolveAttachmentUrl,
3693
+ justify: "start"
3694
+ }
3695
+ ) })
3226
3696
  ] });
3227
3697
  }
3228
3698
  var AssistantMessage = memo(AssistantMessageImpl);
3229
3699
  function useThinkingSeconds(active) {
3230
- const [seconds, setSeconds] = useState12(0);
3231
- useEffect8(() => {
3700
+ const [seconds, setSeconds] = useState14(0);
3701
+ useEffect10(() => {
3232
3702
  if (!active) return;
3233
3703
  setSeconds(0);
3234
3704
  const id = setInterval(() => setSeconds((s) => s + 1), 1e3);
@@ -3238,23 +3708,23 @@ function useThinkingSeconds(active) {
3238
3708
  }
3239
3709
  function ThinkingRow({ agentLabel }) {
3240
3710
  const seconds = useThinkingSeconds(true);
3241
- return /* @__PURE__ */ jsxs10("div", { className: "mx-auto w-full max-w-3xl px-6 py-3", children: [
3242
- /* @__PURE__ */ jsx12("p", { className: "mb-1 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: agentLabel }),
3243
- /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2 text-base text-muted-foreground", children: [
3244
- /* @__PURE__ */ jsx12("svg", { className: "h-4 w-4 animate-spin", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": true, children: /* @__PURE__ */ jsx12("path", { d: "M21 12a9 9 0 1 1-6.219-8.56", strokeLinecap: "round" }) }),
3711
+ return /* @__PURE__ */ jsxs11("div", { className: "mx-auto w-full max-w-3xl px-6 py-3", children: [
3712
+ /* @__PURE__ */ jsx13("p", { className: "mb-1 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: agentLabel }),
3713
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2 text-base text-muted-foreground", children: [
3714
+ /* @__PURE__ */ jsx13("svg", { className: "h-4 w-4 animate-spin", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": true, children: /* @__PURE__ */ jsx13("path", { d: "M21 12a9 9 0 1 1-6.219-8.56", strokeLinecap: "round" }) }),
3245
3715
  "Thinking",
3246
3716
  seconds >= 3 ? ` \xB7 ${seconds}s` : "..."
3247
3717
  ] })
3248
3718
  ] });
3249
3719
  }
3250
3720
  function StreamErrorRow({ message, onRetry }) {
3251
- return /* @__PURE__ */ jsx12("div", { className: "mx-auto w-full max-w-3xl px-6 py-3", children: /* @__PURE__ */ jsxs10("div", { role: "alert", className: "flex items-start gap-2.5 rounded-lg border border-destructive/40 bg-destructive/5 px-3 py-2.5 text-sm text-destructive", children: [
3252
- /* @__PURE__ */ jsxs10("svg", { className: "mt-0.5 h-4 w-4 shrink-0", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
3253
- /* @__PURE__ */ jsx12("circle", { cx: "12", cy: "12", r: "9" }),
3254
- /* @__PURE__ */ jsx12("path", { d: "M12 8v4m0 4h.01" })
3721
+ return /* @__PURE__ */ jsx13("div", { className: "mx-auto w-full max-w-3xl px-6 py-3", children: /* @__PURE__ */ jsxs11("div", { role: "alert", className: "flex items-start gap-2.5 rounded-lg border border-destructive/40 bg-destructive/5 px-3 py-2.5 text-sm text-destructive", children: [
3722
+ /* @__PURE__ */ jsxs11("svg", { className: "mt-0.5 h-4 w-4 shrink-0", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
3723
+ /* @__PURE__ */ jsx13("circle", { cx: "12", cy: "12", r: "9" }),
3724
+ /* @__PURE__ */ jsx13("path", { d: "M12 8v4m0 4h.01" })
3255
3725
  ] }),
3256
- /* @__PURE__ */ jsx12("span", { className: "min-w-0 flex-1 break-words", children: message }),
3257
- onRetry && /* @__PURE__ */ jsx12(
3726
+ /* @__PURE__ */ jsx13("span", { className: "min-w-0 flex-1 break-words", children: message }),
3727
+ onRetry && /* @__PURE__ */ jsx13(
3258
3728
  "button",
3259
3729
  {
3260
3730
  type: "button",
@@ -3281,27 +3751,36 @@ function ChatMessages({
3281
3751
  onRetry,
3282
3752
  renderEmpty,
3283
3753
  emptyState,
3284
- header
3754
+ header,
3755
+ resolveAttachmentUrl
3285
3756
  }) {
3286
- const renderBody = useMemo7(
3287
- () => renderMarkdown ?? ((content) => /* @__PURE__ */ jsx12("p", { className: "whitespace-pre-wrap", children: content })),
3757
+ const renderBody = useMemo8(
3758
+ () => renderMarkdown ?? ((content) => /* @__PURE__ */ jsx13("p", { className: "whitespace-pre-wrap", children: content })),
3288
3759
  [renderMarkdown]
3289
3760
  );
3290
3761
  const lastIsUser = messages[messages.length - 1]?.role === "user";
3291
3762
  if (messages.length === 0 && !loading && !error) {
3292
- const empty = renderEmpty ? renderEmpty() : /* @__PURE__ */ jsx12(ChatEmptyState, { ...emptyState });
3293
- return /* @__PURE__ */ jsxs10(Fragment5, { children: [
3763
+ const empty = renderEmpty ? renderEmpty() : /* @__PURE__ */ jsx13(ChatEmptyState, { ...emptyState });
3764
+ return /* @__PURE__ */ jsxs11(Fragment5, { children: [
3294
3765
  header,
3295
3766
  empty
3296
3767
  ] });
3297
3768
  }
3298
- return /* @__PURE__ */ jsxs10(Fragment5, { children: [
3769
+ return /* @__PURE__ */ jsxs11(Fragment5, { children: [
3299
3770
  header,
3300
3771
  messages.map(
3301
- (msg) => msg.role === "user" ? /* @__PURE__ */ jsx12("div", { className: "mx-auto w-full max-w-3xl px-6 py-3", children: /* @__PURE__ */ jsxs10("div", { className: "ml-auto w-fit max-w-[85%]", children: [
3302
- /* @__PURE__ */ jsx12("p", { className: "mb-1 text-right text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: userLabel }),
3303
- /* @__PURE__ */ jsx12("div", { className: "rounded-2xl rounded-tr-md bg-primary/10 px-4 py-2.5 text-base leading-relaxed", children: /* @__PURE__ */ jsx12("p", { className: "whitespace-pre-wrap", children: msg.content }) })
3304
- ] }) }, msg.id) : /* @__PURE__ */ jsx12(
3772
+ (msg) => msg.role === "user" ? /* @__PURE__ */ jsx13("div", { className: "mx-auto w-full max-w-3xl px-6 py-3", children: /* @__PURE__ */ jsxs11("div", { className: "ml-auto w-fit max-w-[85%]", children: [
3773
+ /* @__PURE__ */ jsx13("p", { className: "mb-1 text-right text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: userLabel }),
3774
+ /* @__PURE__ */ jsx13("div", { className: "rounded-2xl rounded-tr-md bg-primary/10 px-4 py-2.5 text-base leading-relaxed", children: /* @__PURE__ */ jsx13("p", { className: "whitespace-pre-wrap", children: msg.content }) }),
3775
+ resolveAttachmentUrl && attachmentPartsFromMessageParts(msg.parts).length > 0 && /* @__PURE__ */ jsx13("div", { className: "mt-1.5", children: /* @__PURE__ */ jsx13(
3776
+ MessageAttachments,
3777
+ {
3778
+ parts: attachmentPartsFromMessageParts(msg.parts),
3779
+ resolveFileUrl: resolveAttachmentUrl,
3780
+ justify: "end"
3781
+ }
3782
+ ) })
3783
+ ] }) }, msg.id) : /* @__PURE__ */ jsx13(
3305
3784
  AssistantMessage,
3306
3785
  {
3307
3786
  msg,
@@ -3313,13 +3792,14 @@ function ChatMessages({
3313
3792
  onToolCallClick,
3314
3793
  toolRenderers,
3315
3794
  renderExtras,
3316
- durableCards
3795
+ durableCards,
3796
+ resolveAttachmentUrl
3317
3797
  },
3318
3798
  msg.id
3319
3799
  )
3320
3800
  ),
3321
- loading && lastIsUser && /* @__PURE__ */ jsx12(ThinkingRow, { agentLabel }),
3322
- error && !loading && /* @__PURE__ */ jsx12(StreamErrorRow, { message: error, onRetry })
3801
+ loading && lastIsUser && /* @__PURE__ */ jsx13(ThinkingRow, { agentLabel }),
3802
+ error && !loading && /* @__PURE__ */ jsx13(StreamErrorRow, { message: error, onRetry })
3323
3803
  ] });
3324
3804
  }
3325
3805
 
@@ -3352,6 +3832,10 @@ export {
3352
3832
  InteractionPlanCard,
3353
3833
  durableChatCardsFromParts,
3354
3834
  DurableChatCards,
3835
+ __resetAttachmentFileCacheForTests,
3836
+ loadAttachmentFile,
3837
+ triggerAttachmentDownload,
3838
+ MessageAttachments,
3355
3839
  dispatchChatStreamLine,
3356
3840
  consumeChatStream,
3357
3841
  streamChatTurn,
@@ -3376,6 +3860,7 @@ export {
3376
3860
  DEFAULT_MENTION_EMPTY_TEXT,
3377
3861
  useFileMentions,
3378
3862
  segmentMentionContent,
3863
+ useComposerAttachments,
3379
3864
  activityTone,
3380
3865
  formatActivityCost,
3381
3866
  formatActivityDuration,
@@ -3394,4 +3879,4 @@ export {
3394
3879
  useThinkingSeconds,
3395
3880
  ChatMessages
3396
3881
  };
3397
- //# sourceMappingURL=chunk-O6H2WD3I.js.map
3882
+ //# sourceMappingURL=chunk-EIG7ZQW2.js.map