@dudousxd/nestjs-agent-react 0.17.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -42,6 +42,7 @@ __export(src_exports, {
42
42
  MessageList: () => MessageList,
43
43
  QuotaBlockedError: () => QuotaBlockedError,
44
44
  SPEECH_LANGUAGES: () => SPEECH_LANGUAGES,
45
+ acceptsFile: () => acceptsFile,
45
46
  applyCompletion: () => applyCompletion,
46
47
  approvalCountdown: () => approvalCountdown,
47
48
  backgroundRunsFromThread: () => backgroundRunsFromThread,
@@ -52,7 +53,10 @@ __export(src_exports, {
52
53
  describeTimestamp: () => describeTimestamp,
53
54
  describeToolCall: () => describeToolCall,
54
55
  describeUsage: () => describeUsage,
56
+ dragHasFiles: () => dragHasFiles,
55
57
  extractMessageText: () => extractMessageText,
58
+ fileKind: () => fileKind,
59
+ filesFromClipboard: () => filesFromClipboard,
56
60
  fillTemplate: () => fillTemplate,
57
61
  filterAutocompleteItems: () => filterAutocompleteItems,
58
62
  findActiveTrigger: () => findActiveTrigger,
@@ -62,6 +66,7 @@ __export(src_exports, {
62
66
  inferResultView: () => inferResultView,
63
67
  isActionCall: () => isActionCall,
64
68
  loadStoredSpeechLang: () => loadStoredSpeechLang,
69
+ messageFiles: () => messageFiles,
65
70
  notifyThreads: () => notifyThreads,
66
71
  onThreadsEvent: () => onThreadsEvent,
67
72
  persistSpeechLang: () => persistSpeechLang,
@@ -77,6 +82,7 @@ __export(src_exports, {
77
82
  useAgentChat: () => useAgentChat,
78
83
  useAgents: () => useAgents,
79
84
  useApprovalCountdown: () => useApprovalCountdown,
85
+ useAttachments: () => useAttachments,
80
86
  useChatTranscript: () => useChatTranscript,
81
87
  useComposerAutocomplete: () => useComposerAutocomplete,
82
88
  useElapsed: () => useElapsed,
@@ -217,6 +223,9 @@ var AgentClient = class {
217
223
  * what a caller then rides on `sendMessage({ text }, { body: { attachments: [...] } })`.
218
224
  */
219
225
  async uploadAttachment(file, options = {}) {
226
+ if (options.onProgress !== void 0 && this.options.fetch === void 0 && typeof XMLHttpRequest !== "undefined") {
227
+ return this.uploadWithProgress(file, options);
228
+ }
220
229
  const formData = new FormData();
221
230
  formData.append("file", file);
222
231
  const response = await this.fetchImpl()(`${this.baseUrl()}/agent/attachments`, {
@@ -231,7 +240,47 @@ var AgentClient = class {
231
240
  signal: options.signal
232
241
  } : {}
233
242
  });
234
- return this.handleResponse(response, "POST", "/agent/attachments");
243
+ const attachment = await this.handleResponse(response, "POST", "/agent/attachments");
244
+ options.onProgress?.(1);
245
+ return attachment;
246
+ }
247
+ async uploadWithProgress(file, { signal, onProgress }) {
248
+ const headers = {
249
+ accept: "application/json",
250
+ ...await this.resolveHeaders()
251
+ };
252
+ const url = `${this.baseUrl()}/agent/attachments`;
253
+ return new Promise((resolve, reject) => {
254
+ const xhr = new XMLHttpRequest();
255
+ xhr.open("POST", url);
256
+ for (const [name, value] of Object.entries(headers)) xhr.setRequestHeader(name, value);
257
+ xhr.withCredentials = this.options.credentials === "include";
258
+ xhr.upload.onprogress = (event) => {
259
+ if (event.lengthComputable && event.total > 0) onProgress?.(event.loaded / event.total);
260
+ };
261
+ xhr.onload = () => {
262
+ if (xhr.status < 200 || xhr.status >= 300) {
263
+ reject(new AgentHttpError(xhr.status, "POST", "/agent/attachments", xhr.statusText));
264
+ return;
265
+ }
266
+ onProgress?.(1);
267
+ resolve(JSON.parse(xhr.responseText));
268
+ };
269
+ xhr.onerror = () => reject(new TypeError("Network error while uploading the attachment"));
270
+ xhr.onabort = () => reject(new DOMException("The upload was aborted", "AbortError"));
271
+ if (signal !== void 0) {
272
+ if (signal.aborted) {
273
+ reject(new DOMException("The upload was aborted", "AbortError"));
274
+ return;
275
+ }
276
+ signal.addEventListener("abort", () => xhr.abort(), {
277
+ once: true
278
+ });
279
+ }
280
+ const formData = new FormData();
281
+ formData.append("file", file);
282
+ xhr.send(formData);
283
+ });
235
284
  }
236
285
  promoteThread(id) {
237
286
  return this.request("POST", `/agent/threads/${encodeURIComponent(id)}/promote`);
@@ -1079,6 +1128,68 @@ function delay(ms, signal) {
1079
1128
  }
1080
1129
  __name(delay, "delay");
1081
1130
 
1131
+ // src/attachments/files.ts
1132
+ function fileKind(mediaType) {
1133
+ const type = mediaType.toLowerCase();
1134
+ if (type.startsWith("image/")) return "image";
1135
+ if (type === "application/pdf") return "pdf";
1136
+ if (type.startsWith("text/") || type === "application/json") return "text";
1137
+ if (type.startsWith("audio/")) return "audio";
1138
+ if (type.startsWith("video/")) return "video";
1139
+ return "other";
1140
+ }
1141
+ __name(fileKind, "fileKind");
1142
+ function messageFiles(message) {
1143
+ const files = [];
1144
+ for (const part of message.parts) {
1145
+ if (part.type !== "file") continue;
1146
+ const file = part;
1147
+ const filename = file.filename ?? null;
1148
+ const dot = filename?.lastIndexOf(".") ?? -1;
1149
+ const mediaId = file.providerMetadata?.agent?.mediaId;
1150
+ files.push({
1151
+ url: file.url,
1152
+ mediaType: file.mediaType,
1153
+ filename,
1154
+ kind: fileKind(file.mediaType),
1155
+ extension: filename !== null && dot > 0 ? filename.slice(dot + 1).toLowerCase() : null,
1156
+ mediaId: typeof mediaId === "string" ? mediaId : null
1157
+ });
1158
+ }
1159
+ return files;
1160
+ }
1161
+ __name(messageFiles, "messageFiles");
1162
+ function acceptsFile(accept, file) {
1163
+ const rules = (typeof accept === "string" ? accept.split(",") : accept ?? []).map((rule) => rule.trim().toLowerCase()).filter((rule) => rule.length > 0);
1164
+ if (rules.length === 0) return true;
1165
+ const type = file.type.toLowerCase();
1166
+ const name = file.name.toLowerCase();
1167
+ return rules.some((rule) => {
1168
+ if (rule.startsWith(".")) return name.endsWith(rule);
1169
+ if (rule.endsWith("/*")) return type.startsWith(rule.slice(0, -1));
1170
+ return type === rule;
1171
+ });
1172
+ }
1173
+ __name(acceptsFile, "acceptsFile");
1174
+ function filesFromClipboard(data) {
1175
+ if (!data) return [];
1176
+ const files = [];
1177
+ for (const item of Array.from(data.items ?? [])) {
1178
+ if (item.kind !== "file") continue;
1179
+ const file = item.getAsFile();
1180
+ if (file !== null) files.push(file);
1181
+ }
1182
+ return files.length > 0 ? files : Array.from(data.files ?? []);
1183
+ }
1184
+ __name(filesFromClipboard, "filesFromClipboard");
1185
+ function dragHasFiles(data) {
1186
+ return Array.from(data?.types ?? []).includes("Files");
1187
+ }
1188
+ __name(dragHasFiles, "dragHasFiles");
1189
+
1190
+ // src/attachments/use-attachments.ts
1191
+ var import_react2 = require("react");
1192
+
1082
1193
  // src/backend.ts
1083
1194
  var AgentBackendUnsupportedError = class extends Error {
1084
1195
  static {
@@ -1099,19 +1210,239 @@ function requireBackendMethod(backend, name) {
1099
1210
  }
1100
1211
  __name(requireBackendMethod, "requireBackendMethod");
1101
1212
 
1213
+ // src/attachments/use-attachments.ts
1214
+ var DEFAULT_REJECTION = {
1215
+ type: /* @__PURE__ */ __name((file) => `${file.name} is not a supported file type`, "type"),
1216
+ size: /* @__PURE__ */ __name((file) => `${file.name} is too large`, "size"),
1217
+ count: /* @__PURE__ */ __name(() => "Too many files", "count")
1218
+ };
1219
+ var sequence = 0;
1220
+ function useAttachments(options) {
1221
+ const latest = (0, import_react2.useRef)(options);
1222
+ latest.current = options;
1223
+ const [items, setItems] = (0, import_react2.useState)([]);
1224
+ const itemsRef = (0, import_react2.useRef)(items);
1225
+ itemsRef.current = items;
1226
+ const controllers = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
1227
+ const [dragDepth, setDragDepth] = (0, import_react2.useState)(0);
1228
+ const patch = (0, import_react2.useCallback)((id, change) => {
1229
+ setItems((current) => current.map((item) => item.id === id ? {
1230
+ ...item,
1231
+ ...change
1232
+ } : item));
1233
+ }, []);
1234
+ const start = (0, import_react2.useCallback)((item) => {
1235
+ const { upload, backend } = latest.current;
1236
+ const run = upload ?? (backend?.uploadAttachment !== void 0 ? backend.uploadAttachment.bind(backend) : void 0);
1237
+ if (run === void 0) {
1238
+ patch(item.id, {
1239
+ status: "error",
1240
+ error: new AgentBackendUnsupportedError("uploadAttachment").message
1241
+ });
1242
+ return;
1243
+ }
1244
+ const controller = new AbortController();
1245
+ controllers.current.set(item.id, controller);
1246
+ run(item.file, {
1247
+ signal: controller.signal,
1248
+ onProgress: /* @__PURE__ */ __name((fraction) => patch(item.id, {
1249
+ progress: Math.max(0, Math.min(1, fraction))
1250
+ }), "onProgress")
1251
+ }).then((attachment) => {
1252
+ if (controller.signal.aborted) return;
1253
+ controllers.current.delete(item.id);
1254
+ patch(item.id, {
1255
+ status: "ready",
1256
+ progress: 1,
1257
+ attachment
1258
+ });
1259
+ }, (error) => {
1260
+ if (controller.signal.aborted) return;
1261
+ controllers.current.delete(item.id);
1262
+ patch(item.id, {
1263
+ status: "error",
1264
+ error: error instanceof Error ? error.message : String(error)
1265
+ });
1266
+ });
1267
+ }, [
1268
+ patch
1269
+ ]);
1270
+ const add = (0, import_react2.useCallback)((files) => {
1271
+ if (files == null) return;
1272
+ const { accept: accept2, maxBytes, maxFiles, describeRejection } = latest.current;
1273
+ const describe = /* @__PURE__ */ __name((reason, file) => describeRejection?.(reason, file) ?? DEFAULT_REJECTION[reason](file), "describe");
1274
+ let room = maxFiles === void 0 ? Number.POSITIVE_INFINITY : maxFiles - itemsRef.current.filter((item) => item.status !== "rejected").length;
1275
+ const staged = [];
1276
+ for (const file of Array.from(files)) {
1277
+ sequence += 1;
1278
+ const base = {
1279
+ id: `att-${sequence}`,
1280
+ file,
1281
+ name: file.name,
1282
+ size: file.size,
1283
+ type: file.type,
1284
+ progress: 0
1285
+ };
1286
+ const reason = !acceptsFile(accept2, file) ? "type" : maxBytes !== void 0 && file.size > maxBytes ? "size" : room <= 0 ? "count" : void 0;
1287
+ if (reason !== void 0) {
1288
+ staged.push({
1289
+ ...base,
1290
+ status: "rejected",
1291
+ error: describe(reason, file)
1292
+ });
1293
+ continue;
1294
+ }
1295
+ room -= 1;
1296
+ const previewUrl = file.type.startsWith("image/") && typeof URL.createObjectURL === "function" ? URL.createObjectURL(file) : void 0;
1297
+ staged.push({
1298
+ ...base,
1299
+ status: "uploading",
1300
+ ...previewUrl ? {
1301
+ previewUrl
1302
+ } : {}
1303
+ });
1304
+ }
1305
+ if (staged.length === 0) return;
1306
+ setItems((current) => [
1307
+ ...current,
1308
+ ...staged
1309
+ ]);
1310
+ for (const item of staged) {
1311
+ if (item.status === "uploading") start(item);
1312
+ }
1313
+ }, [
1314
+ start
1315
+ ]);
1316
+ const release = (0, import_react2.useCallback)((item) => {
1317
+ if (item === void 0) return;
1318
+ controllers.current.get(item.id)?.abort();
1319
+ controllers.current.delete(item.id);
1320
+ if (item.previewUrl !== void 0 && typeof URL.revokeObjectURL === "function") {
1321
+ URL.revokeObjectURL(item.previewUrl);
1322
+ }
1323
+ }, []);
1324
+ const remove = (0, import_react2.useCallback)((id) => {
1325
+ release(itemsRef.current.find((item) => item.id === id));
1326
+ setItems((current) => current.filter((item) => item.id !== id));
1327
+ }, [
1328
+ release
1329
+ ]);
1330
+ const clear = (0, import_react2.useCallback)(() => {
1331
+ for (const item of itemsRef.current) release(item);
1332
+ setItems([]);
1333
+ }, [
1334
+ release
1335
+ ]);
1336
+ const retry = (0, import_react2.useCallback)((id) => {
1337
+ const item = itemsRef.current.find((candidate) => candidate.id === id);
1338
+ if (item === void 0 || item.status !== "error") return;
1339
+ const { error: _previous, ...rest } = item;
1340
+ const again = {
1341
+ ...rest,
1342
+ status: "uploading",
1343
+ progress: 0
1344
+ };
1345
+ setItems((current) => current.map((candidate) => candidate.id === id ? again : candidate));
1346
+ start(again);
1347
+ }, [
1348
+ start
1349
+ ]);
1350
+ (0, import_react2.useEffect)(() => () => {
1351
+ for (const item of itemsRef.current) release(item);
1352
+ }, [
1353
+ release
1354
+ ]);
1355
+ const onPaste = (0, import_react2.useCallback)((event) => {
1356
+ const files = filesFromClipboard(event.clipboardData);
1357
+ if (files.length === 0) return;
1358
+ event.preventDefault();
1359
+ add(files);
1360
+ }, [
1361
+ add
1362
+ ]);
1363
+ const dropZoneProps = (0, import_react2.useMemo)(() => ({
1364
+ onDragEnter: /* @__PURE__ */ __name((event) => {
1365
+ if (!dragHasFiles(event.dataTransfer)) return;
1366
+ event.preventDefault();
1367
+ setDragDepth((depth) => depth + 1);
1368
+ }, "onDragEnter"),
1369
+ onDragOver: /* @__PURE__ */ __name((event) => {
1370
+ if (dragHasFiles(event.dataTransfer)) event.preventDefault();
1371
+ }, "onDragOver"),
1372
+ onDragLeave: /* @__PURE__ */ __name((event) => {
1373
+ if (!dragHasFiles(event.dataTransfer)) return;
1374
+ setDragDepth((depth) => Math.max(0, depth - 1));
1375
+ }, "onDragLeave"),
1376
+ onDrop: /* @__PURE__ */ __name((event) => {
1377
+ if (!dragHasFiles(event.dataTransfer)) return;
1378
+ event.preventDefault();
1379
+ setDragDepth(0);
1380
+ add(event.dataTransfer?.files);
1381
+ }, "onDrop")
1382
+ }), [
1383
+ add
1384
+ ]);
1385
+ const accept = options.accept;
1386
+ const inputProps = (0, import_react2.useMemo)(() => ({
1387
+ type: "file",
1388
+ accept: typeof accept === "string" ? accept : accept?.join(","),
1389
+ multiple: options.maxFiles === void 0 || options.maxFiles > 1,
1390
+ onChange: /* @__PURE__ */ __name((event) => {
1391
+ const input = event.currentTarget;
1392
+ add(input.files);
1393
+ input.value = "";
1394
+ }, "onChange")
1395
+ }), [
1396
+ accept,
1397
+ options.maxFiles,
1398
+ add
1399
+ ]);
1400
+ return (0, import_react2.useMemo)(() => {
1401
+ const ready = items.filter((item) => item.status === "ready" && item.attachment !== void 0);
1402
+ const attachments = ready.map((item) => item.attachment);
1403
+ return {
1404
+ items,
1405
+ add,
1406
+ remove,
1407
+ retry,
1408
+ clear,
1409
+ isUploading: items.some((item) => item.status === "uploading"),
1410
+ attachments,
1411
+ refs: attachments.map((attachment) => ({
1412
+ mediaId: attachment.mediaId
1413
+ })),
1414
+ inputProps,
1415
+ dropZoneProps,
1416
+ isDragging: dragDepth > 0,
1417
+ onPaste
1418
+ };
1419
+ }, [
1420
+ items,
1421
+ add,
1422
+ remove,
1423
+ retry,
1424
+ clear,
1425
+ inputProps,
1426
+ dropZoneProps,
1427
+ dragDepth,
1428
+ onPaste
1429
+ ]);
1430
+ }
1431
+ __name(useAttachments, "useAttachments");
1432
+
1102
1433
  // src/catalog/use-agents.ts
1103
- var import_react3 = require("react");
1434
+ var import_react4 = require("react");
1104
1435
 
1105
1436
  // src/catalog/use-resource.ts
1106
- var import_react2 = require("react");
1437
+ var import_react3 = require("react");
1107
1438
  function useResource(load2, key, enabled) {
1108
- const [data, setData] = (0, import_react2.useState)(void 0);
1109
- const [isLoading, setIsLoading] = (0, import_react2.useState)(enabled);
1110
- const [error, setError] = (0, import_react2.useState)(null);
1111
- const loadRef = (0, import_react2.useRef)(load2);
1439
+ const [data, setData] = (0, import_react3.useState)(void 0);
1440
+ const [isLoading, setIsLoading] = (0, import_react3.useState)(enabled);
1441
+ const [error, setError] = (0, import_react3.useState)(null);
1442
+ const loadRef = (0, import_react3.useRef)(load2);
1112
1443
  loadRef.current = load2;
1113
- const generation = (0, import_react2.useRef)(0);
1114
- const refresh = (0, import_react2.useCallback)(async () => {
1444
+ const generation = (0, import_react3.useRef)(0);
1445
+ const refresh = (0, import_react3.useCallback)(async () => {
1115
1446
  const mine = ++generation.current;
1116
1447
  setIsLoading(true);
1117
1448
  try {
@@ -1126,7 +1457,7 @@ function useResource(load2, key, enabled) {
1126
1457
  if (mine === generation.current) setIsLoading(false);
1127
1458
  }
1128
1459
  }, []);
1129
- (0, import_react2.useEffect)(() => {
1460
+ (0, import_react3.useEffect)(() => {
1130
1461
  if (!enabled) return;
1131
1462
  void refresh();
1132
1463
  }, [
@@ -1147,7 +1478,7 @@ __name(useResource, "useResource");
1147
1478
  function useAgents(options) {
1148
1479
  const { backend, enabled = true } = options;
1149
1480
  const { data, isLoading, error, refresh } = useResource(() => requireBackendMethod(backend, "listAgents")(), "", enabled);
1150
- return (0, import_react3.useMemo)(() => {
1481
+ return (0, import_react4.useMemo)(() => {
1151
1482
  const agents = data ?? [];
1152
1483
  return {
1153
1484
  agents,
@@ -1166,11 +1497,11 @@ function useAgents(options) {
1166
1497
  __name(useAgents, "useAgents");
1167
1498
 
1168
1499
  // src/catalog/use-models.ts
1169
- var import_react4 = require("react");
1500
+ var import_react5 = require("react");
1170
1501
  function useModels(options) {
1171
1502
  const { backend, agent, enabled = true } = options;
1172
1503
  const { data, isLoading, error, refresh } = useResource(() => requireBackendMethod(backend, "listModels")(agent), `${agent ?? ""}`, enabled);
1173
- return (0, import_react4.useMemo)(() => {
1504
+ return (0, import_react5.useMemo)(() => {
1174
1505
  const providers = data?.providers ?? [];
1175
1506
  const models = providers.flatMap((provider) => provider.models.map((model) => ({
1176
1507
  ...model,
@@ -1200,7 +1531,7 @@ function useModels(options) {
1200
1531
  __name(useModels, "useModels");
1201
1532
 
1202
1533
  // src/quota/use-quota.ts
1203
- var import_react5 = require("react");
1534
+ var import_react6 = require("react");
1204
1535
 
1205
1536
  // src/threads/threads-events.ts
1206
1537
  var listeners = /* @__PURE__ */ new WeakMap();
@@ -1227,7 +1558,7 @@ __name(onThreadsEvent, "onThreadsEvent");
1227
1558
  function useQuota(options) {
1228
1559
  const { backend, enabled = true, pollMs } = options;
1229
1560
  const { data, isLoading, error, refresh } = useResource(() => requireBackendMethod(backend, "getQuota")(), "", enabled);
1230
- (0, import_react5.useEffect)(() => {
1561
+ (0, import_react6.useEffect)(() => {
1231
1562
  if (!enabled) return;
1232
1563
  return onThreadsEvent(backend, (event) => {
1233
1564
  if (event.type === "changed") void refresh();
@@ -1237,7 +1568,7 @@ function useQuota(options) {
1237
1568
  enabled,
1238
1569
  refresh
1239
1570
  ]);
1240
- (0, import_react5.useEffect)(() => {
1571
+ (0, import_react6.useEffect)(() => {
1241
1572
  if (!enabled || pollMs === void 0 || pollMs <= 0) return;
1242
1573
  const timer = setInterval(() => void refresh(), pollMs);
1243
1574
  return () => clearInterval(timer);
@@ -1246,7 +1577,7 @@ function useQuota(options) {
1246
1577
  pollMs,
1247
1578
  refresh
1248
1579
  ]);
1249
- return (0, import_react5.useMemo)(() => {
1580
+ return (0, import_react6.useMemo)(() => {
1250
1581
  const windows = data?.windows ?? [];
1251
1582
  return {
1252
1583
  report: data,
@@ -1268,27 +1599,27 @@ function useQuota(options) {
1268
1599
  __name(useQuota, "useQuota");
1269
1600
 
1270
1601
  // src/feedback/use-message-feedback.ts
1271
- var import_react6 = require("react");
1602
+ var import_react7 = require("react");
1272
1603
  function useMessageFeedback(options) {
1273
- const latest = (0, import_react6.useRef)(options);
1604
+ const latest = (0, import_react7.useRef)(options);
1274
1605
  latest.current = options;
1275
- const [local, setLocal] = (0, import_react6.useState)(/* @__PURE__ */ new Map());
1276
- const [pending, setPending] = (0, import_react6.useState)(/* @__PURE__ */ new Set());
1277
- const [error, setError] = (0, import_react6.useState)(null);
1278
- const localRef = (0, import_react6.useRef)(local);
1606
+ const [local, setLocal] = (0, import_react7.useState)(/* @__PURE__ */ new Map());
1607
+ const [pending, setPending] = (0, import_react7.useState)(/* @__PURE__ */ new Set());
1608
+ const [error, setError] = (0, import_react7.useState)(null);
1609
+ const localRef = (0, import_react7.useRef)(local);
1279
1610
  localRef.current = local;
1280
- const resolved = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
1281
- const feedbackOf = (0, import_react6.useCallback)((message) => {
1611
+ const resolved = (0, import_react7.useRef)(/* @__PURE__ */ new Map());
1612
+ const feedbackOf = (0, import_react7.useCallback)((message) => {
1282
1613
  const mine = local.get(message.id);
1283
1614
  if (mine !== void 0) return mine.feedback;
1284
1615
  return metadataOf(message).feedback ?? null;
1285
1616
  }, [
1286
1617
  local
1287
1618
  ]);
1288
- const isPending = (0, import_react6.useCallback)((message) => pending.has(message.id), [
1619
+ const isPending = (0, import_react7.useCallback)((message) => pending.has(message.id), [
1289
1620
  pending
1290
1621
  ]);
1291
- const storedIdFor = (0, import_react6.useCallback)(async (message) => {
1622
+ const storedIdFor = (0, import_react7.useCallback)(async (message) => {
1292
1623
  const runId = metadataOf(message).runId;
1293
1624
  if (runId === void 0) return message.id;
1294
1625
  const known = resolved.current.get(message.id);
@@ -1308,7 +1639,7 @@ function useMessageFeedback(options) {
1308
1639
  resolved.current.set(message.id, row.id);
1309
1640
  return row.id;
1310
1641
  }, []);
1311
- const rate = (0, import_react6.useCallback)(async (message, value, comment) => {
1642
+ const rate = (0, import_react7.useCallback)(async (message, value, comment) => {
1312
1643
  const setFeedback = requireBackendMethod(latest.current.backend, "setMessageFeedback");
1313
1644
  const had = localRef.current.get(message.id);
1314
1645
  const optimistic = value === null ? null : {
@@ -1354,7 +1685,7 @@ function useMessageFeedback(options) {
1354
1685
  }, [
1355
1686
  storedIdFor
1356
1687
  ]);
1357
- const toggle = (0, import_react6.useCallback)((message, value) => {
1688
+ const toggle = (0, import_react7.useCallback)((message, value) => {
1358
1689
  const mine = localRef.current.get(message.id);
1359
1690
  const current = mine !== void 0 ? mine.feedback : metadataOf(message).feedback ?? null;
1360
1691
  return rate(message, current?.value === value ? null : value);
@@ -1377,16 +1708,16 @@ function metadataOf(message) {
1377
1708
  __name(metadataOf, "metadataOf");
1378
1709
 
1379
1710
  // src/threads/use-threads.ts
1380
- var import_react7 = require("react");
1711
+ var import_react8 = require("react");
1381
1712
  function useThreads(options) {
1382
1713
  const { backend, enabled = true } = options;
1383
- const [threads, setThreads] = (0, import_react7.useState)([]);
1384
- const [isLoading, setIsLoading] = (0, import_react7.useState)(enabled);
1385
- const [error, setError] = (0, import_react7.useState)(null);
1386
- const threadsRef = (0, import_react7.useRef)(threads);
1714
+ const [threads, setThreads] = (0, import_react8.useState)([]);
1715
+ const [isLoading, setIsLoading] = (0, import_react8.useState)(enabled);
1716
+ const [error, setError] = (0, import_react8.useState)(null);
1717
+ const threadsRef = (0, import_react8.useRef)(threads);
1387
1718
  threadsRef.current = threads;
1388
- const generation = (0, import_react7.useRef)(0);
1389
- const refresh = (0, import_react7.useCallback)(async () => {
1719
+ const generation = (0, import_react8.useRef)(0);
1720
+ const refresh = (0, import_react8.useCallback)(async () => {
1390
1721
  const mine = ++generation.current;
1391
1722
  setIsLoading(true);
1392
1723
  try {
@@ -1403,7 +1734,7 @@ function useThreads(options) {
1403
1734
  }, [
1404
1735
  backend
1405
1736
  ]);
1406
- (0, import_react7.useEffect)(() => {
1737
+ (0, import_react8.useEffect)(() => {
1407
1738
  if (!enabled) return;
1408
1739
  void refresh();
1409
1740
  return onThreadsEvent(backend, (event) => {
@@ -1423,7 +1754,7 @@ function useThreads(options) {
1423
1754
  enabled,
1424
1755
  refresh
1425
1756
  ]);
1426
- const rename = (0, import_react7.useCallback)(async (id, title) => {
1757
+ const rename = (0, import_react8.useCallback)(async (id, title) => {
1427
1758
  const before = threadsRef.current;
1428
1759
  setThreads((current) => current.map((thread) => thread.id === id ? {
1429
1760
  ...thread,
@@ -1445,7 +1776,7 @@ function useThreads(options) {
1445
1776
  }, [
1446
1777
  backend
1447
1778
  ]);
1448
- const remove = (0, import_react7.useCallback)(async (id) => {
1779
+ const remove = (0, import_react8.useCallback)(async (id) => {
1449
1780
  const before = threadsRef.current;
1450
1781
  setThreads((current) => current.filter((thread) => thread.id !== id));
1451
1782
  try {
@@ -1520,10 +1851,10 @@ function backgroundRunsFromThread(messages) {
1520
1851
  __name(backgroundRunsFromThread, "backgroundRunsFromThread");
1521
1852
 
1522
1853
  // src/components/chat-input.tsx
1523
- var import_react9 = __toESM(require("react"), 1);
1854
+ var import_react10 = __toESM(require("react"), 1);
1524
1855
 
1525
1856
  // src/components/use-speech-recognition.ts
1526
- var import_react8 = require("react");
1857
+ var import_react9 = require("react");
1527
1858
  function getRecognitionCtor() {
1528
1859
  if (typeof window === "undefined") return null;
1529
1860
  const scope = window;
@@ -1531,15 +1862,15 @@ function getRecognitionCtor() {
1531
1862
  }
1532
1863
  __name(getRecognitionCtor, "getRecognitionCtor");
1533
1864
  function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTranscript }) {
1534
- const recognitionRef = (0, import_react8.useRef)(null);
1535
- const [isListening, setIsListening] = (0, import_react8.useState)(false);
1536
- const ctor = (0, import_react8.useMemo)(() => getRecognitionCtor(), []);
1865
+ const recognitionRef = (0, import_react9.useRef)(null);
1866
+ const [isListening, setIsListening] = (0, import_react9.useState)(false);
1867
+ const ctor = (0, import_react9.useMemo)(() => getRecognitionCtor(), []);
1537
1868
  const isSupported = ctor !== null;
1538
- const finalRef = (0, import_react8.useRef)(onFinalTranscript);
1539
- const interimRef = (0, import_react8.useRef)(onInterimTranscript);
1869
+ const finalRef = (0, import_react9.useRef)(onFinalTranscript);
1870
+ const interimRef = (0, import_react9.useRef)(onInterimTranscript);
1540
1871
  finalRef.current = onFinalTranscript;
1541
1872
  interimRef.current = onInterimTranscript;
1542
- (0, import_react8.useEffect)(() => {
1873
+ (0, import_react9.useEffect)(() => {
1543
1874
  if (!ctor) return;
1544
1875
  const recognition = new ctor();
1545
1876
  recognition.lang = lang;
@@ -1578,7 +1909,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
1578
1909
  ctor,
1579
1910
  lang
1580
1911
  ]);
1581
- const start = (0, import_react8.useCallback)(() => {
1912
+ const start = (0, import_react9.useCallback)(() => {
1582
1913
  const recognition = recognitionRef.current;
1583
1914
  if (!recognition) return;
1584
1915
  try {
@@ -1588,7 +1919,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
1588
1919
  console.warn("SpeechRecognition start failed", error);
1589
1920
  }
1590
1921
  }, []);
1591
- const stop = (0, import_react8.useCallback)(() => {
1922
+ const stop = (0, import_react9.useCallback)(() => {
1592
1923
  const recognition = recognitionRef.current;
1593
1924
  if (!recognition) return;
1594
1925
  try {
@@ -1607,8 +1938,8 @@ __name(useSpeechRecognition, "useSpeechRecognition");
1607
1938
 
1608
1939
  // src/components/chat-input.tsx
1609
1940
  function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = true, speechLang = "en-US", sendLabel = "Send", onStop, isStreaming = false, stopLabel = "Stop generating", className, textareaClassName, sendButtonClassName, micButtonClassName, stopButtonClassName, renderMic }) {
1610
- const [value, setValue] = (0, import_react9.useState)("");
1611
- const [interim, setInterim] = (0, import_react9.useState)("");
1941
+ const [value, setValue] = (0, import_react10.useState)("");
1942
+ const [interim, setInterim] = (0, import_react10.useState)("");
1612
1943
  const speech = useSpeechRecognition({
1613
1944
  lang: speechLang,
1614
1945
  onFinalTranscript: /* @__PURE__ */ __name((text) => {
@@ -1619,7 +1950,7 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
1619
1950
  }, "onFinalTranscript"),
1620
1951
  onInterimTranscript: /* @__PURE__ */ __name((text) => setInterim(text), "onInterimTranscript")
1621
1952
  });
1622
- (0, import_react9.useEffect)(() => {
1953
+ (0, import_react10.useEffect)(() => {
1623
1954
  if (!speech.isListening) setInterim("");
1624
1955
  }, [
1625
1956
  speech.isListening
@@ -1647,9 +1978,9 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
1647
1978
  const displayValue = speech.isListening && interim ? `${value}${value && !value.endsWith(" ") ? " " : ""}${interim}` : value;
1648
1979
  const showMic = enableSpeech && speech.isSupported;
1649
1980
  const showStop = isStreaming && onStop !== void 0;
1650
- return /* @__PURE__ */ import_react9.default.createElement("div", {
1981
+ return /* @__PURE__ */ import_react10.default.createElement("div", {
1651
1982
  className
1652
- }, /* @__PURE__ */ import_react9.default.createElement("textarea", {
1983
+ }, /* @__PURE__ */ import_react10.default.createElement("textarea", {
1653
1984
  className: textareaClassName,
1654
1985
  rows,
1655
1986
  value: displayValue,
@@ -1660,17 +1991,17 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
1660
1991
  }, "onChange"),
1661
1992
  onKeyDown: handleKeyDown,
1662
1993
  placeholder: placeholder ?? "Ask something (Enter to send, Shift+Enter for newline)"
1663
- }), /* @__PURE__ */ import_react9.default.createElement("button", {
1994
+ }), /* @__PURE__ */ import_react10.default.createElement("button", {
1664
1995
  type: "button",
1665
1996
  className: sendButtonClassName,
1666
1997
  onClick: submit,
1667
1998
  disabled: disabled || !value.trim()
1668
- }, sendLabel), showStop ? /* @__PURE__ */ import_react9.default.createElement("button", {
1999
+ }, sendLabel), showStop ? /* @__PURE__ */ import_react10.default.createElement("button", {
1669
2000
  type: "button",
1670
2001
  className: stopButtonClassName,
1671
2002
  onClick: /* @__PURE__ */ __name(() => void onStop(), "onClick"),
1672
2003
  "aria-label": "Stop generating"
1673
- }, stopLabel) : null, showMic ? /* @__PURE__ */ import_react9.default.createElement("button", {
2004
+ }, stopLabel) : null, showMic ? /* @__PURE__ */ import_react10.default.createElement("button", {
1674
2005
  type: "button",
1675
2006
  className: micButtonClassName,
1676
2007
  onClick: toggleMic,
@@ -1683,10 +2014,10 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
1683
2014
  __name(ChatInput, "ChatInput");
1684
2015
 
1685
2016
  // src/components/message-item.tsx
1686
- var import_react12 = __toESM(require("react"), 1);
2017
+ var import_react13 = __toESM(require("react"), 1);
1687
2018
 
1688
2019
  // src/transcript/use-chat-transcript.ts
1689
- var import_react11 = require("react");
2020
+ var import_react12 = require("react");
1690
2021
 
1691
2022
  // src/transcript/model.ts
1692
2023
  var import_nestjs_agent_core2 = require("@dudousxd/nestjs-agent-core");
@@ -2663,11 +2994,11 @@ function formatCostUsd(cost) {
2663
2994
  __name(formatCostUsd, "formatCostUsd");
2664
2995
 
2665
2996
  // src/transcript/use-stick-to-bottom.ts
2666
- var import_react10 = require("react");
2997
+ var import_react11 = require("react");
2667
2998
  function useStickToBottom({ contentKey, threshold = 32 }) {
2668
- const containerRef = (0, import_react10.useRef)(null);
2669
- const [isAtBottom, setIsAtBottom] = (0, import_react10.useState)(true);
2670
- const measure = (0, import_react10.useCallback)(() => {
2999
+ const containerRef = (0, import_react11.useRef)(null);
3000
+ const [isAtBottom, setIsAtBottom] = (0, import_react11.useState)(true);
3001
+ const measure = (0, import_react11.useCallback)(() => {
2671
3002
  const element = containerRef.current;
2672
3003
  if (!element) {
2673
3004
  return;
@@ -2677,7 +3008,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
2677
3008
  }, [
2678
3009
  threshold
2679
3010
  ]);
2680
- const scrollToBottom = (0, import_react10.useCallback)(() => {
3011
+ const scrollToBottom = (0, import_react11.useCallback)(() => {
2681
3012
  const element = containerRef.current;
2682
3013
  if (!element) {
2683
3014
  return;
@@ -2685,7 +3016,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
2685
3016
  element.scrollTop = element.scrollHeight;
2686
3017
  setIsAtBottom(true);
2687
3018
  }, []);
2688
- const setContainer = (0, import_react10.useCallback)((element) => {
3019
+ const setContainer = (0, import_react11.useCallback)((element) => {
2689
3020
  containerRef.current = element;
2690
3021
  if (element) {
2691
3022
  measure();
@@ -2693,7 +3024,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
2693
3024
  }, [
2694
3025
  measure
2695
3026
  ]);
2696
- (0, import_react10.useEffect)(() => {
3027
+ (0, import_react11.useEffect)(() => {
2697
3028
  if (isAtBottom) {
2698
3029
  scrollToBottom();
2699
3030
  }
@@ -2702,7 +3033,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
2702
3033
  isAtBottom,
2703
3034
  scrollToBottom
2704
3035
  ]);
2705
- const getContainerProps = (0, import_react10.useCallback)(() => ({
3036
+ const getContainerProps = (0, import_react11.useCallback)(() => ({
2706
3037
  ref: setContainer,
2707
3038
  onScroll: measure
2708
3039
  }), [
@@ -2724,14 +3055,14 @@ var DEFAULT_LOAD_EARLIER_STEP = 50;
2724
3055
  var DEFAULT_COPY_RESET_MS = 1500;
2725
3056
  function useChatTranscript(options) {
2726
3057
  const { messages, status, initialVisibleCount = DEFAULT_VISIBLE_COUNT, loadEarlierStep = DEFAULT_LOAD_EARLIER_STEP, followUps, stickToBottomThreshold } = options;
2727
- const latest = (0, import_react11.useRef)(options);
3058
+ const latest = (0, import_react12.useRef)(options);
2728
3059
  latest.current = options;
2729
- const [visibleCount, setVisibleCount] = (0, import_react11.useState)(initialVisibleCount);
2730
- const [stopRequested, setStopRequested] = (0, import_react11.useState)(false);
3060
+ const [visibleCount, setVisibleCount] = (0, import_react12.useState)(initialVisibleCount);
3061
+ const [stopRequested, setStopRequested] = (0, import_react12.useState)(false);
2731
3062
  const isStreaming = status === "streaming" || status === "reconnecting";
2732
3063
  const isBusy = status === "submitted" || isStreaming;
2733
3064
  const isEmpty = messages.length === 0;
2734
- (0, import_react11.useEffect)(() => {
3065
+ (0, import_react12.useEffect)(() => {
2735
3066
  if (!isBusy) {
2736
3067
  setStopRequested(false);
2737
3068
  }
@@ -2748,7 +3079,7 @@ function useChatTranscript(options) {
2748
3079
  streamingMessageId,
2749
3080
  options
2750
3081
  });
2751
- const stop = (0, import_react11.useCallback)(() => {
3082
+ const stop = (0, import_react12.useCallback)(() => {
2752
3083
  const current = latest.current;
2753
3084
  if (current.status !== "submitted" && current.status !== "streaming" && current.status !== "reconnecting") {
2754
3085
  return;
@@ -2756,8 +3087,8 @@ function useChatTranscript(options) {
2756
3087
  setStopRequested(true);
2757
3088
  void current.onStop?.();
2758
3089
  }, []);
2759
- const loadEarlier = (0, import_react11.useCallback)(() => setVisibleCount((count) => count + (latest.current.loadEarlierStep ?? DEFAULT_LOAD_EARLIER_STEP)), []);
2760
- const showAll = (0, import_react11.useCallback)(() => setVisibleCount(latest.current.messages.length), []);
3090
+ const loadEarlier = (0, import_react12.useCallback)(() => setVisibleCount((count) => count + (latest.current.loadEarlierStep ?? DEFAULT_LOAD_EARLIER_STEP)), []);
3091
+ const showAll = (0, import_react12.useCallback)(() => setVisibleCount(latest.current.messages.length), []);
2761
3092
  const scroll = useStickToBottom({
2762
3093
  contentKey: contentSignature(messages),
2763
3094
  ...stickToBottomThreshold !== void 0 ? {
@@ -2793,7 +3124,7 @@ function useChatTranscript(options) {
2793
3124
  __name(useChatTranscript, "useChatTranscript");
2794
3125
  function useTranscriptItem(options) {
2795
3126
  const { message, isStreaming = false } = options;
2796
- const messagesRef = (0, import_react11.useRef)([
3127
+ const messagesRef = (0, import_react12.useRef)([
2797
3128
  message
2798
3129
  ]);
2799
3130
  if (messagesRef.current[0] !== message) {
@@ -2811,13 +3142,13 @@ function useTranscriptItem(options) {
2811
3142
  }
2812
3143
  __name(useTranscriptItem, "useTranscriptItem");
2813
3144
  function useTranscriptItems({ messages, visibleStart, streamingMessageId, options }) {
2814
- const [drafts, setDrafts] = (0, import_react11.useState)(() => /* @__PURE__ */ new Map());
2815
- const [copied, setCopied] = (0, import_react11.useState)(() => /* @__PURE__ */ new Set());
2816
- const [openReasoning, setOpenReasoning] = (0, import_react11.useState)(() => /* @__PURE__ */ new Map());
2817
- const [picks, setPicks] = (0, import_react11.useState)(() => /* @__PURE__ */ new Map());
2818
- const [settling, setSettling] = (0, import_react11.useState)(() => /* @__PURE__ */ new Map());
2819
- const [settleErrors, setSettleErrors] = (0, import_react11.useState)(() => /* @__PURE__ */ new Map());
2820
- const latest = (0, import_react11.useRef)({
3145
+ const [drafts, setDrafts] = (0, import_react12.useState)(() => /* @__PURE__ */ new Map());
3146
+ const [copied, setCopied] = (0, import_react12.useState)(() => /* @__PURE__ */ new Set());
3147
+ const [openReasoning, setOpenReasoning] = (0, import_react12.useState)(() => /* @__PURE__ */ new Map());
3148
+ const [picks, setPicks] = (0, import_react12.useState)(() => /* @__PURE__ */ new Map());
3149
+ const [settling, setSettling] = (0, import_react12.useState)(() => /* @__PURE__ */ new Map());
3150
+ const [settleErrors, setSettleErrors] = (0, import_react12.useState)(() => /* @__PURE__ */ new Map());
3151
+ const latest = (0, import_react12.useRef)({
2821
3152
  options,
2822
3153
  drafts,
2823
3154
  picks,
@@ -2829,23 +3160,23 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2829
3160
  picks,
2830
3161
  messages
2831
3162
  };
2832
- const copyTimers = (0, import_react11.useRef)(/* @__PURE__ */ new Map());
2833
- (0, import_react11.useEffect)(() => () => {
3163
+ const copyTimers = (0, import_react12.useRef)(/* @__PURE__ */ new Map());
3164
+ (0, import_react12.useEffect)(() => () => {
2834
3165
  for (const timer of copyTimers.current.values()) {
2835
3166
  clearTimeout(timer);
2836
3167
  }
2837
3168
  copyTimers.current.clear();
2838
3169
  }, []);
2839
- const callbackCache = (0, import_react11.useRef)(/* @__PURE__ */ new Map());
2840
- const reasoningToggleCache = (0, import_react11.useRef)(/* @__PURE__ */ new Map());
2841
- const toggleReasoning = (0, import_react11.useCallback)((key, open) => {
3170
+ const callbackCache = (0, import_react12.useRef)(/* @__PURE__ */ new Map());
3171
+ const reasoningToggleCache = (0, import_react12.useRef)(/* @__PURE__ */ new Map());
3172
+ const toggleReasoning = (0, import_react12.useCallback)((key, open) => {
2842
3173
  setOpenReasoning((current) => {
2843
3174
  const next = new Map(current);
2844
3175
  next.set(key, open ?? !(current.get(key) ?? false));
2845
3176
  return next;
2846
3177
  });
2847
3178
  }, []);
2848
- const stableToggle = (0, import_react11.useCallback)((key) => {
3179
+ const stableToggle = (0, import_react12.useCallback)((key) => {
2849
3180
  let cached = reasoningToggleCache.current.get(key);
2850
3181
  if (!cached) {
2851
3182
  cached = /* @__PURE__ */ __name((open) => toggleReasoning(key, open), "cached");
@@ -2855,10 +3186,10 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2855
3186
  }, [
2856
3187
  toggleReasoning
2857
3188
  ]);
2858
- const pick = (0, import_react11.useCallback)((toolCallId, questionId, values) => {
3189
+ const pick = (0, import_react12.useCallback)((toolCallId, questionId, values) => {
2859
3190
  setPicks((current) => new Map(current).set(pickKey(toolCallId, questionId), values));
2860
3191
  }, []);
2861
- const settle = (0, import_react11.useCallback)((toolCallId, action, send) => {
3192
+ const settle = (0, import_react12.useCallback)((toolCallId, action, send) => {
2862
3193
  setSettleErrors((current) => {
2863
3194
  if (!current.has(toolCallId)) {
2864
3195
  return current;
@@ -2882,7 +3213,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2882
3213
  failed(error);
2883
3214
  }
2884
3215
  }, []);
2885
- const answer = (0, import_react11.useCallback)((toolCallId) => {
3216
+ const answer = (0, import_react12.useCallback)((toolCallId) => {
2886
3217
  const current = latest.current;
2887
3218
  const onAnswer = current.options.onAnswer;
2888
3219
  if (!onAnswer) {
@@ -2892,7 +3223,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2892
3223
  }, [
2893
3224
  settle
2894
3225
  ]);
2895
- const skip = (0, import_react11.useCallback)((toolCallId) => {
3226
+ const skip = (0, import_react12.useCallback)((toolCallId) => {
2896
3227
  const onSkip = latest.current.options.onSkip;
2897
3228
  if (onSkip) {
2898
3229
  settle(toolCallId, "skip", () => onSkip(toolCallId));
@@ -2900,7 +3231,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2900
3231
  }, [
2901
3232
  settle
2902
3233
  ]);
2903
- const approve = (0, import_react11.useCallback)((toolCallId, options2) => {
3234
+ const approve = (0, import_react12.useCallback)((toolCallId, options2) => {
2904
3235
  const onApprove = latest.current.options.onApprove;
2905
3236
  if (onApprove) {
2906
3237
  settle(toolCallId, "approve", () => options2 === void 0 ? onApprove(toolCallId) : onApprove(toolCallId, options2));
@@ -2908,7 +3239,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2908
3239
  }, [
2909
3240
  settle
2910
3241
  ]);
2911
- const reject = (0, import_react11.useCallback)((toolCallId) => {
3242
+ const reject = (0, import_react12.useCallback)((toolCallId) => {
2912
3243
  const onReject = latest.current.options.onReject;
2913
3244
  if (onReject) {
2914
3245
  settle(toolCallId, "reject", () => onReject(toolCallId));
@@ -2916,7 +3247,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2916
3247
  }, [
2917
3248
  settle
2918
3249
  ]);
2919
- const callbacksFor = (0, import_react11.useCallback)((id) => {
3250
+ const callbacksFor = (0, import_react12.useCallback)((id) => {
2920
3251
  const cached = callbackCache.current.get(id);
2921
3252
  if (cached) {
2922
3253
  return cached;
@@ -3158,7 +3489,7 @@ function MessageItem({ message, isStreaming = false, editable, onEditSubmit, reg
3158
3489
  getCreatedAt: /* @__PURE__ */ __name(() => createdAt, "getCreatedAt")
3159
3490
  } : {}
3160
3491
  });
3161
- return /* @__PURE__ */ import_react12.default.createElement(MessageItemView, {
3492
+ return /* @__PURE__ */ import_react13.default.createElement(MessageItemView, {
3162
3493
  item,
3163
3494
  ...slots
3164
3495
  });
@@ -3166,51 +3497,51 @@ function MessageItem({ message, isStreaming = false, editable, onEditSubmit, reg
3166
3497
  __name(MessageItem, "MessageItem");
3167
3498
  function MessageItemView({ item, ...slots }) {
3168
3499
  const { classNames } = slots;
3169
- const actionRow = /* @__PURE__ */ import_react12.default.createElement("div", {
3500
+ const actionRow = /* @__PURE__ */ import_react13.default.createElement("div", {
3170
3501
  className: classNames?.actions,
3171
3502
  "data-actions-for": item.role
3172
- }, item.copy.available ? /* @__PURE__ */ import_react12.default.createElement("button", {
3503
+ }, item.copy.available ? /* @__PURE__ */ import_react13.default.createElement("button", {
3173
3504
  type: "button",
3174
3505
  onClick: item.copy.copy,
3175
3506
  className: classNames?.actionButton,
3176
3507
  title: item.copy.copied ? "Copied!" : "Copy message text"
3177
- }, item.copy.copied ? "Copied" : "Copy") : null, item.fork.available ? /* @__PURE__ */ import_react12.default.createElement("button", {
3508
+ }, item.copy.copied ? "Copied" : "Copy") : null, item.fork.available ? /* @__PURE__ */ import_react13.default.createElement("button", {
3178
3509
  type: "button",
3179
3510
  onClick: item.fork.run,
3180
3511
  className: classNames?.actionButton,
3181
3512
  title: "Fork thread from this message"
3182
- }, "Fork") : null, item.regenerate.available ? /* @__PURE__ */ import_react12.default.createElement("button", {
3513
+ }, "Fork") : null, item.regenerate.available ? /* @__PURE__ */ import_react13.default.createElement("button", {
3183
3514
  type: "button",
3184
3515
  onClick: item.regenerate.run,
3185
3516
  className: classNames?.actionButton,
3186
3517
  title: "Regenerate response"
3187
- }, "Regenerate") : null, slots.meta, !item.isUser && item.usage ? /* @__PURE__ */ import_react12.default.createElement("span", {
3518
+ }, "Regenerate") : null, slots.meta, !item.isUser && item.usage ? /* @__PURE__ */ import_react13.default.createElement("span", {
3188
3519
  className: classNames?.usage,
3189
3520
  title: `Input ${item.usage.inputTokens.toLocaleString()} \xB7 Output ${item.usage.outputTokens.toLocaleString()} tokens`
3190
- }, item.usage.costLabel, " \xB7 ", item.usage.tokensLabel) : null, item.timestamp ? /* @__PURE__ */ import_react12.default.createElement("span", {
3521
+ }, item.usage.costLabel, " \xB7 ", item.usage.tokensLabel) : null, item.timestamp ? /* @__PURE__ */ import_react13.default.createElement("span", {
3191
3522
  className: classNames?.timestamp,
3192
3523
  title: item.timestamp.absolute
3193
3524
  }, item.timestamp.relative) : null);
3194
3525
  if (item.edit.available) {
3195
- return /* @__PURE__ */ import_react12.default.createElement(EditableUserBubble, {
3526
+ return /* @__PURE__ */ import_react13.default.createElement(EditableUserBubble, {
3196
3527
  item,
3197
3528
  slots,
3198
3529
  actions: actionRow
3199
3530
  });
3200
3531
  }
3201
3532
  const className = joinClasses(classNames?.root, classNames?.byRole?.[item.role]);
3202
- return /* @__PURE__ */ import_react12.default.createElement("div", {
3533
+ return /* @__PURE__ */ import_react13.default.createElement("div", {
3203
3534
  className,
3204
3535
  "data-role": item.role,
3205
3536
  "data-streaming": item.isStreaming ? "true" : void 0
3206
- }, /* @__PURE__ */ import_react12.default.createElement("div", null, renderBlocks(item, slots)), actionRow);
3537
+ }, /* @__PURE__ */ import_react13.default.createElement("div", null, renderBlocks(item, slots)), actionRow);
3207
3538
  }
3208
3539
  __name(MessageItemView, "MessageItemView");
3209
3540
  function renderBlocks(item, slots) {
3210
3541
  const nodes = [];
3211
3542
  for (const block of item.blocks) {
3212
3543
  if (block.kind === "text") {
3213
- nodes.push(/* @__PURE__ */ import_react12.default.createElement("div", {
3544
+ nodes.push(/* @__PURE__ */ import_react13.default.createElement("div", {
3214
3545
  key: block.key,
3215
3546
  className: slots.classNames?.text
3216
3547
  }, slots.renderText ? slots.renderText(block.text, {
@@ -3231,7 +3562,7 @@ function renderBlocks(item, slots) {
3231
3562
  continue;
3232
3563
  }
3233
3564
  if (block.kind === "ui" && slots.renderUi) {
3234
- nodes.push(/* @__PURE__ */ import_react12.default.createElement("div", {
3565
+ nodes.push(/* @__PURE__ */ import_react13.default.createElement("div", {
3235
3566
  key: block.key,
3236
3567
  "data-slot": "ui",
3237
3568
  "data-component": block.component
@@ -3242,18 +3573,18 @@ function renderBlocks(item, slots) {
3242
3573
  }
3243
3574
  __name(renderBlocks, "renderBlocks");
3244
3575
  function renderFilesBlock(block, slots) {
3245
- return /* @__PURE__ */ import_react12.default.createElement("div", {
3576
+ return /* @__PURE__ */ import_react13.default.createElement("div", {
3246
3577
  key: block.key,
3247
3578
  className: slots.classNames?.files,
3248
3579
  "data-files": "true"
3249
- }, slots.renderFiles ? slots.renderFiles(block.files) : block.files.map((file) => /* @__PURE__ */ import_react12.default.createElement("a", {
3580
+ }, slots.renderFiles ? slots.renderFiles(block.files) : block.files.map((file) => /* @__PURE__ */ import_react13.default.createElement("a", {
3250
3581
  key: file.url,
3251
3582
  href: file.url,
3252
3583
  target: "_blank",
3253
3584
  rel: "noreferrer",
3254
3585
  className: slots.classNames?.file,
3255
3586
  title: file.filename ?? file.mediaType
3256
- }, file.isImage ? /* @__PURE__ */ import_react12.default.createElement("img", {
3587
+ }, file.isImage ? /* @__PURE__ */ import_react13.default.createElement("img", {
3257
3588
  src: file.url,
3258
3589
  alt: file.filename ?? "image",
3259
3590
  className: slots.classNames?.fileImage
@@ -3261,17 +3592,17 @@ function renderFilesBlock(block, slots) {
3261
3592
  }
3262
3593
  __name(renderFilesBlock, "renderFilesBlock");
3263
3594
  function renderReasoningBlock(block, slots) {
3264
- return /* @__PURE__ */ import_react12.default.createElement("div", {
3595
+ return /* @__PURE__ */ import_react13.default.createElement("div", {
3265
3596
  key: block.key,
3266
3597
  className: slots.classNames?.reasoning,
3267
3598
  "data-reasoning": "true",
3268
3599
  "data-streaming": block.isStreaming ? "true" : void 0
3269
- }, /* @__PURE__ */ import_react12.default.createElement("button", {
3600
+ }, /* @__PURE__ */ import_react13.default.createElement("button", {
3270
3601
  type: "button",
3271
3602
  className: slots.classNames?.reasoningToggle,
3272
3603
  onClick: /* @__PURE__ */ __name(() => block.toggle(), "onClick"),
3273
3604
  "aria-expanded": block.isOpen
3274
- }, slots.reasoningLabel ?? "Reasoning"), block.isOpen ? /* @__PURE__ */ import_react12.default.createElement("div", {
3605
+ }, slots.reasoningLabel ?? "Reasoning"), block.isOpen ? /* @__PURE__ */ import_react13.default.createElement("div", {
3275
3606
  className: slots.classNames?.reasoningText
3276
3607
  }, slots.renderReasoning ? slots.renderReasoning(block.text, {
3277
3608
  isStreaming: block.isStreaming,
@@ -3282,7 +3613,7 @@ __name(renderReasoningBlock, "renderReasoningBlock");
3282
3613
  function renderToolBlock(item, block, slots) {
3283
3614
  if (slots.renderToolGroup) {
3284
3615
  return [
3285
- /* @__PURE__ */ import_react12.default.createElement(import_react12.default.Fragment, {
3616
+ /* @__PURE__ */ import_react13.default.createElement(import_react13.default.Fragment, {
3286
3617
  key: block.key
3287
3618
  }, slots.renderToolGroup(block.parts, block.key))
3288
3619
  ];
@@ -3293,7 +3624,7 @@ function renderToolBlock(item, block, slots) {
3293
3624
  const renderToolPart = slots.renderToolPart;
3294
3625
  return block.parts.map((part) => {
3295
3626
  const partKey = `${item.id}-${part.toolCallId}`;
3296
- return /* @__PURE__ */ import_react12.default.createElement(import_react12.default.Fragment, {
3627
+ return /* @__PURE__ */ import_react13.default.createElement(import_react13.default.Fragment, {
3297
3628
  key: partKey
3298
3629
  }, renderToolPart(part, partKey));
3299
3630
  });
@@ -3303,14 +3634,14 @@ function EditableUserBubble({ item, slots, actions }) {
3303
3634
  const { classNames } = slots;
3304
3635
  const { edit } = item;
3305
3636
  if (!edit.isEditing) {
3306
- return /* @__PURE__ */ import_react12.default.createElement("div", {
3637
+ return /* @__PURE__ */ import_react13.default.createElement("div", {
3307
3638
  className: joinClasses(classNames?.root, classNames?.byRole?.user),
3308
3639
  "data-role": "user"
3309
- }, /* @__PURE__ */ import_react12.default.createElement("div", {
3640
+ }, /* @__PURE__ */ import_react13.default.createElement("div", {
3310
3641
  className: classNames?.text
3311
3642
  }, slots.renderText ? slots.renderText(item.text, {
3312
3643
  isStreaming: false
3313
- }) : item.text), /* @__PURE__ */ import_react12.default.createElement("button", {
3644
+ }) : item.text), /* @__PURE__ */ import_react13.default.createElement("button", {
3314
3645
  type: "button",
3315
3646
  onClick: edit.start,
3316
3647
  className: classNames?.actionButton,
@@ -3318,18 +3649,18 @@ function EditableUserBubble({ item, slots, actions }) {
3318
3649
  }, "Edit"), actions);
3319
3650
  }
3320
3651
  const textareaProps = edit.getTextareaProps();
3321
- return /* @__PURE__ */ import_react12.default.createElement("div", {
3652
+ return /* @__PURE__ */ import_react13.default.createElement("div", {
3322
3653
  "data-editing": "true"
3323
- }, /* @__PURE__ */ import_react12.default.createElement("textarea", {
3654
+ }, /* @__PURE__ */ import_react13.default.createElement("textarea", {
3324
3655
  ...textareaProps,
3325
3656
  rows: Math.min(8, edit.draft.split("\n").length + 1),
3326
3657
  className: classNames?.editTextarea
3327
- }), /* @__PURE__ */ import_react12.default.createElement("div", {
3658
+ }), /* @__PURE__ */ import_react13.default.createElement("div", {
3328
3659
  className: classNames?.editActions
3329
- }, /* @__PURE__ */ import_react12.default.createElement("button", {
3660
+ }, /* @__PURE__ */ import_react13.default.createElement("button", {
3330
3661
  type: "button",
3331
3662
  onClick: edit.cancel
3332
- }, "Cancel"), /* @__PURE__ */ import_react12.default.createElement("button", {
3663
+ }, "Cancel"), /* @__PURE__ */ import_react13.default.createElement("button", {
3333
3664
  type: "button",
3334
3665
  onClick: edit.save,
3335
3666
  disabled: !edit.canSave
@@ -3573,35 +3904,35 @@ function toItem(entry) {
3573
3904
  __name(toItem, "toItem");
3574
3905
 
3575
3906
  // src/composer/use-composer-autocomplete.ts
3576
- var import_react13 = require("react");
3907
+ var import_react14 = require("react");
3577
3908
  function useComposerAutocomplete(options) {
3578
3909
  const { value, sources, acceptOnTab = true } = options;
3579
- const latest = (0, import_react13.useRef)(options);
3910
+ const latest = (0, import_react14.useRef)(options);
3580
3911
  latest.current = options;
3581
- const inputRef = (0, import_react13.useRef)(null);
3582
- const [caret, setCaret] = (0, import_react13.useState)(value.length);
3583
- const [items, setItems] = (0, import_react13.useState)([]);
3584
- const [isLoading, setLoading] = (0, import_react13.useState)(false);
3585
- const [error, setError] = (0, import_react13.useState)(null);
3586
- const [activeIndex, setActiveIndex] = (0, import_react13.useState)(0);
3587
- const [dismissedIndex, setDismissedIndex] = (0, import_react13.useState)(null);
3912
+ const inputRef = (0, import_react14.useRef)(null);
3913
+ const [caret, setCaret] = (0, import_react14.useState)(value.length);
3914
+ const [items, setItems] = (0, import_react14.useState)([]);
3915
+ const [isLoading, setLoading] = (0, import_react14.useState)(false);
3916
+ const [error, setError] = (0, import_react14.useState)(null);
3917
+ const [activeIndex, setActiveIndex] = (0, import_react14.useState)(0);
3918
+ const [dismissedIndex, setDismissedIndex] = (0, import_react14.useState)(null);
3588
3919
  const safeCaret = Math.min(caret, value.length);
3589
- const match = (0, import_react13.useMemo)(() => findActiveTrigger(value, safeCaret, sources), [
3920
+ const match = (0, import_react14.useMemo)(() => findActiveTrigger(value, safeCaret, sources), [
3590
3921
  value,
3591
3922
  safeCaret,
3592
3923
  sources
3593
3924
  ]);
3594
3925
  const isActive = match !== null && dismissedIndex !== match.index;
3595
- const matchRef = (0, import_react13.useRef)(match);
3926
+ const matchRef = (0, import_react14.useRef)(match);
3596
3927
  matchRef.current = match;
3597
- const caretRef = (0, import_react13.useRef)(safeCaret);
3928
+ const caretRef = (0, import_react14.useRef)(safeCaret);
3598
3929
  caretRef.current = safeCaret;
3599
- const itemsRef = (0, import_react13.useRef)(items);
3930
+ const itemsRef = (0, import_react14.useRef)(items);
3600
3931
  itemsRef.current = items;
3601
- const activeIndexRef = (0, import_react13.useRef)(activeIndex);
3932
+ const activeIndexRef = (0, import_react14.useRef)(activeIndex);
3602
3933
  activeIndexRef.current = activeIndex;
3603
3934
  const requestKey = isActive && match ? `${match.source.id}\0${match.query}` : null;
3604
- (0, import_react13.useEffect)(() => {
3935
+ (0, import_react14.useEffect)(() => {
3605
3936
  const current = matchRef.current;
3606
3937
  if (requestKey === null || current === null) {
3607
3938
  setItems([]);
@@ -3649,25 +3980,25 @@ function useComposerAutocomplete(options) {
3649
3980
  ]);
3650
3981
  const isOpen = isActive;
3651
3982
  const activeItem = isOpen ? items[activeIndex] ?? null : null;
3652
- const reactId = (0, import_react13.useId)();
3983
+ const reactId = (0, import_react14.useId)();
3653
3984
  const listboxId = options.id ?? `agent-autocomplete-${reactId}`;
3654
- const optionId = (0, import_react13.useCallback)((index) => `${listboxId}-option-${index}`, [
3985
+ const optionId = (0, import_react14.useCallback)((index) => `${listboxId}-option-${index}`, [
3655
3986
  listboxId
3656
3987
  ]);
3657
- const highlight = (0, import_react13.useCallback)((index) => {
3988
+ const highlight = (0, import_react14.useCallback)((index) => {
3658
3989
  const total = itemsRef.current.length;
3659
3990
  if (total === 0) return;
3660
3991
  setActiveIndex((index % total + total) % total);
3661
3992
  }, []);
3662
- const moveHighlight = (0, import_react13.useCallback)((delta) => highlight(activeIndexRef.current + delta), [
3993
+ const moveHighlight = (0, import_react14.useCallback)((delta) => highlight(activeIndexRef.current + delta), [
3663
3994
  highlight
3664
3995
  ]);
3665
- const dismiss = (0, import_react13.useCallback)(() => {
3996
+ const dismiss = (0, import_react14.useCallback)(() => {
3666
3997
  const current = matchRef.current;
3667
3998
  if (current) setDismissedIndex(current.index);
3668
3999
  }, []);
3669
- const pendingCaret = (0, import_react13.useRef)(null);
3670
- const accept = (0, import_react13.useCallback)((item) => {
4000
+ const pendingCaret = (0, import_react14.useRef)(null);
4001
+ const accept = (0, import_react14.useCallback)((item) => {
3671
4002
  const current = matchRef.current;
3672
4003
  if (!current) return;
3673
4004
  const chosen = item ?? itemsRef.current[activeIndexRef.current];
@@ -3678,16 +4009,16 @@ function useComposerAutocomplete(options) {
3678
4009
  setDismissedIndex(null);
3679
4010
  pendingCaret.current = edit.caret;
3680
4011
  }, []);
3681
- (0, import_react13.useLayoutEffect)(() => {
4012
+ (0, import_react14.useLayoutEffect)(() => {
3682
4013
  const position = pendingCaret.current;
3683
4014
  if (position === null) return;
3684
4015
  pendingCaret.current = null;
3685
4016
  inputRef.current?.setSelectionRange(position, position);
3686
4017
  });
3687
- const readCaret = (0, import_react13.useCallback)((element) => {
4018
+ const readCaret = (0, import_react14.useCallback)((element) => {
3688
4019
  setCaret(element.selectionStart ?? element.value.length);
3689
4020
  }, []);
3690
- const onKeyDown = (0, import_react13.useCallback)((event) => {
4021
+ const onKeyDown = (0, import_react14.useCallback)((event) => {
3691
4022
  if (!isOpen) return;
3692
4023
  const hasItems = itemsRef.current.length > 0;
3693
4024
  switch (event.key) {
@@ -3725,23 +4056,23 @@ function useComposerAutocomplete(options) {
3725
4056
  dismiss,
3726
4057
  moveHighlight
3727
4058
  ]);
3728
- const onChange = (0, import_react13.useCallback)((event) => {
4059
+ const onChange = (0, import_react14.useCallback)((event) => {
3729
4060
  readCaret(event.target);
3730
4061
  const next = findActiveTrigger(event.target.value, event.target.selectionStart ?? event.target.value.length, latest.current.sources);
3731
4062
  setDismissedIndex((current) => current !== null && next?.index === current ? current : null);
3732
4063
  }, [
3733
4064
  readCaret
3734
4065
  ]);
3735
- const onSelect = (0, import_react13.useCallback)((event) => readCaret(event.currentTarget), [
4066
+ const onSelect = (0, import_react14.useCallback)((event) => readCaret(event.currentTarget), [
3736
4067
  readCaret
3737
4068
  ]);
3738
- const onClick = (0, import_react13.useCallback)((event) => readCaret(event.currentTarget), [
4069
+ const onClick = (0, import_react14.useCallback)((event) => readCaret(event.currentTarget), [
3739
4070
  readCaret
3740
4071
  ]);
3741
- const setInput = (0, import_react13.useCallback)((element) => {
4072
+ const setInput = (0, import_react14.useCallback)((element) => {
3742
4073
  inputRef.current = element;
3743
4074
  }, []);
3744
- const getInputProps = (0, import_react13.useCallback)(() => ({
4075
+ const getInputProps = (0, import_react14.useCallback)(() => ({
3745
4076
  ref: setInput,
3746
4077
  onChange,
3747
4078
  onKeyDown,
@@ -3769,7 +4100,7 @@ function useComposerAutocomplete(options) {
3769
4100
  activeIndex,
3770
4101
  optionId
3771
4102
  ]);
3772
- const getListboxProps = (0, import_react13.useCallback)(() => ({
4103
+ const getListboxProps = (0, import_react14.useCallback)(() => ({
3773
4104
  id: listboxId,
3774
4105
  role: "listbox",
3775
4106
  "aria-label": match?.source.label ?? match?.source.id ?? "Suggestions"
@@ -3777,7 +4108,7 @@ function useComposerAutocomplete(options) {
3777
4108
  listboxId,
3778
4109
  match
3779
4110
  ]);
3780
- const getOptionProps = (0, import_react13.useCallback)((index) => ({
4111
+ const getOptionProps = (0, import_react14.useCallback)((index) => ({
3781
4112
  id: optionId(index),
3782
4113
  role: "option",
3783
4114
  "aria-selected": index === activeIndex
@@ -3811,7 +4142,7 @@ function isPromise(value) {
3811
4142
  __name(isPromise, "isPromise");
3812
4143
 
3813
4144
  // src/presentation/use-tool-catalog.ts
3814
- var import_react14 = require("react");
4145
+ var import_react15 = require("react");
3815
4146
  var shared = /* @__PURE__ */ new WeakMap();
3816
4147
  function load(client, agent) {
3817
4148
  let byAgent = shared.get(client);
@@ -3835,11 +4166,11 @@ function load(client, agent) {
3835
4166
  __name(load, "load");
3836
4167
  function useToolCatalog(options) {
3837
4168
  const { client, agent, enabled = true } = options;
3838
- const [entries, setEntries] = (0, import_react14.useState)([]);
3839
- const [isLoading, setLoading] = (0, import_react14.useState)(enabled);
3840
- const [error, setError] = (0, import_react14.useState)(null);
3841
- const [generation, setGeneration] = (0, import_react14.useState)(0);
3842
- (0, import_react14.useEffect)(() => {
4169
+ const [entries, setEntries] = (0, import_react15.useState)([]);
4170
+ const [isLoading, setLoading] = (0, import_react15.useState)(enabled);
4171
+ const [error, setError] = (0, import_react15.useState)(null);
4172
+ const [generation, setGeneration] = (0, import_react15.useState)(0);
4173
+ (0, import_react15.useEffect)(() => {
3843
4174
  if (!enabled) {
3844
4175
  setLoading(false);
3845
4176
  return;
@@ -3865,14 +4196,14 @@ function useToolCatalog(options) {
3865
4196
  enabled,
3866
4197
  generation
3867
4198
  ]);
3868
- const refresh = (0, import_react14.useCallback)(() => {
4199
+ const refresh = (0, import_react15.useCallback)(() => {
3869
4200
  shared.get(client)?.delete(agent ?? "");
3870
4201
  setGeneration((current) => current + 1);
3871
4202
  }, [
3872
4203
  client,
3873
4204
  agent
3874
4205
  ]);
3875
- const catalog = (0, import_react14.useMemo)(() => toolCatalogFrom(entries), [
4206
+ const catalog = (0, import_react15.useMemo)(() => toolCatalogFrom(entries), [
3876
4207
  entries
3877
4208
  ]);
3878
4209
  return {
@@ -3886,7 +4217,7 @@ function useToolCatalog(options) {
3886
4217
  __name(useToolCatalog, "useToolCatalog");
3887
4218
 
3888
4219
  // src/approvals/countdown.ts
3889
- var import_react15 = require("react");
4220
+ var import_react16 = require("react");
3890
4221
  function approvalCountdown(expiresAt, now = Date.now()) {
3891
4222
  const deadline = expiresAt == null ? Number.NaN : Date.parse(expiresAt);
3892
4223
  if (!Number.isFinite(deadline)) {
@@ -3904,10 +4235,10 @@ function approvalCountdown(expiresAt, now = Date.now()) {
3904
4235
  __name(approvalCountdown, "approvalCountdown");
3905
4236
  function useApprovalCountdown(expiresAt, options = {}) {
3906
4237
  const { intervalMs = 1e3 } = options;
3907
- const nowRef = (0, import_react15.useRef)(options.now ?? Date.now);
4238
+ const nowRef = (0, import_react16.useRef)(options.now ?? Date.now);
3908
4239
  nowRef.current = options.now ?? Date.now;
3909
- const [state, setState] = (0, import_react15.useState)(() => approvalCountdown(expiresAt, nowRef.current()));
3910
- (0, import_react15.useEffect)(() => {
4240
+ const [state, setState] = (0, import_react16.useState)(() => approvalCountdown(expiresAt, nowRef.current()));
4241
+ (0, import_react16.useEffect)(() => {
3911
4242
  const read = /* @__PURE__ */ __name(() => approvalCountdown(expiresAt, nowRef.current()), "read");
3912
4243
  const first = read();
3913
4244
  setState(first);
@@ -3970,7 +4301,13 @@ function storedMessageToUiMessage(message) {
3970
4301
  type: "file",
3971
4302
  mediaType: attachment.contentType,
3972
4303
  filename: attachment.name,
3973
- url: attachment.url
4304
+ url: attachment.url,
4305
+ // The stored id rides along, so a replayed file can be referenced again (`messageFiles`).
4306
+ providerMetadata: {
4307
+ agent: {
4308
+ mediaId: attachment.mediaId
4309
+ }
4310
+ }
3974
4311
  });
3975
4312
  }
3976
4313
  const toolCallIds = new Set((message.toolCalls ?? []).map((call) => call.id));
@@ -4148,21 +4485,21 @@ function sumUsage(rows) {
4148
4485
  __name(sumUsage, "sumUsage");
4149
4486
 
4150
4487
  // src/use-agent-chat.ts
4151
- var import_react16 = require("@ai-sdk/react");
4152
- var import_react17 = require("react");
4488
+ var import_react17 = require("@ai-sdk/react");
4489
+ var import_react18 = require("react");
4153
4490
  function useAgentChat(options) {
4154
- const latest = (0, import_react17.useRef)(options);
4491
+ const latest = (0, import_react18.useRef)(options);
4155
4492
  latest.current = options;
4156
- const [runId, setRunId] = (0, import_react17.useState)(options.resumeRunId);
4157
- const runIdRef = (0, import_react17.useRef)(runId);
4493
+ const [runId, setRunId] = (0, import_react18.useState)(options.resumeRunId);
4494
+ const runIdRef = (0, import_react18.useRef)(runId);
4158
4495
  runIdRef.current = runId;
4159
- const settlingRunIdRef = (0, import_react17.useRef)(void 0);
4160
- const autoResumeRunIdRef = (0, import_react17.useRef)(void 0);
4161
- const [autoResume, setAutoResume] = (0, import_react17.useState)(false);
4162
- const [activeRunId, setActiveRunId] = (0, import_react17.useState)(null);
4163
- const regenerateNext = (0, import_react17.useRef)(false);
4164
- const createdThreadId = (0, import_react17.useRef)(void 0);
4165
- const client = (0, import_react17.useMemo)(() => {
4496
+ const settlingRunIdRef = (0, import_react18.useRef)(void 0);
4497
+ const autoResumeRunIdRef = (0, import_react18.useRef)(void 0);
4498
+ const [autoResume, setAutoResume] = (0, import_react18.useState)(false);
4499
+ const [activeRunId, setActiveRunId] = (0, import_react18.useState)(null);
4500
+ const regenerateNext = (0, import_react18.useRef)(false);
4501
+ const createdThreadId = (0, import_react18.useRef)(void 0);
4502
+ const client = (0, import_react18.useMemo)(() => {
4166
4503
  if (options.backend) return options.backend;
4167
4504
  if (options.client) return options.client;
4168
4505
  return new AgentClient({
@@ -4178,11 +4515,11 @@ function useAgentChat(options) {
4178
4515
  getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders")
4179
4516
  });
4180
4517
  }, []);
4181
- const [connection, setConnection] = (0, import_react17.useState)({
4518
+ const [connection, setConnection] = (0, import_react18.useState)({
4182
4519
  status: "live"
4183
4520
  });
4184
- const resyncAfterSettle = (0, import_react17.useRef)(false);
4185
- (0, import_react17.useEffect)(() => {
4521
+ const resyncAfterSettle = (0, import_react18.useRef)(false);
4522
+ (0, import_react18.useEffect)(() => {
4186
4523
  if (!options.resume || options.threadId === void 0) return;
4187
4524
  let cancelled = false;
4188
4525
  const threadId = options.threadId;
@@ -4202,7 +4539,7 @@ function useAgentChat(options) {
4202
4539
  options.resume,
4203
4540
  options.threadId
4204
4541
  ]);
4205
- const transport = (0, import_react17.useMemo)(() => {
4542
+ const transport = (0, import_react18.useMemo)(() => {
4206
4543
  function onMeta(meta) {
4207
4544
  settlingRunIdRef.current = meta.runId;
4208
4545
  setRunId(meta.runId);
@@ -4257,8 +4594,8 @@ function useAgentChat(options) {
4257
4594
  }, "onAttemptStart")
4258
4595
  });
4259
4596
  }, []);
4260
- const backgroundRef = (0, import_react17.useRef)(() => Promise.resolve());
4261
- const chat = (0, import_react16.useChat)({
4597
+ const backgroundRef = (0, import_react18.useRef)(() => Promise.resolve());
4598
+ const chat = (0, import_react17.useChat)({
4262
4599
  transport,
4263
4600
  resume: options.resumeRunId !== void 0 || autoResume,
4264
4601
  ...options.threadId !== void 0 ? {
@@ -4306,7 +4643,7 @@ function useAgentChat(options) {
4306
4643
  }
4307
4644
  }, "onFinish")
4308
4645
  });
4309
- const chatRef = (0, import_react17.useRef)(chat);
4646
+ const chatRef = (0, import_react18.useRef)(chat);
4310
4647
  chatRef.current = chat;
4311
4648
  async function resyncFromThread() {
4312
4649
  const threadId = latest.current.threadId ?? createdThreadId.current;
@@ -4318,11 +4655,11 @@ function useAgentChat(options) {
4318
4655
  }
4319
4656
  }
4320
4657
  __name(resyncFromThread, "resyncFromThread");
4321
- const turnInFlight = (0, import_react17.useRef)(false);
4322
- const isTurnInFlight = (0, import_react17.useCallback)(() => turnInFlight.current || transport.isAttemptLive, [
4658
+ const turnInFlight = (0, import_react18.useRef)(false);
4659
+ const isTurnInFlight = (0, import_react18.useCallback)(() => turnInFlight.current || transport.isAttemptLive, [
4323
4660
  transport
4324
4661
  ]);
4325
- const sendMessage = (0, import_react17.useCallback)(async (...args) => {
4662
+ const sendMessage = (0, import_react18.useCallback)(async (...args) => {
4326
4663
  const blocked = latest.current.blocked;
4327
4664
  if (blocked != null) throw new QuotaBlockedError(blocked);
4328
4665
  if (isTurnInFlight()) return;
@@ -4335,15 +4672,15 @@ function useAgentChat(options) {
4335
4672
  }, [
4336
4673
  isTurnInFlight
4337
4674
  ]);
4338
- const addToolResult = (0, import_react17.useCallback)(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
4675
+ const addToolResult = (0, import_react18.useCallback)(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
4339
4676
  tool,
4340
4677
  toolCallId,
4341
4678
  output
4342
4679
  }), []);
4343
- const [threads, setThreads] = (0, import_react17.useState)([]);
4344
- const [quota, setQuota] = (0, import_react17.useState)(null);
4345
- const [backgroundRuns, setBackgroundRuns] = (0, import_react17.useState)([]);
4346
- const refreshBackground = (0, import_react17.useCallback)(async () => {
4680
+ const [threads, setThreads] = (0, import_react18.useState)([]);
4681
+ const [quota, setQuota] = (0, import_react18.useState)(null);
4682
+ const [backgroundRuns, setBackgroundRuns] = (0, import_react18.useState)([]);
4683
+ const refreshBackground = (0, import_react18.useCallback)(async () => {
4347
4684
  const threadId = latest.current.threadId ?? createdThreadId.current;
4348
4685
  if (threadId === void 0) {
4349
4686
  return;
@@ -4368,7 +4705,7 @@ function useAgentChat(options) {
4368
4705
  ]);
4369
4706
  backgroundRef.current = refreshBackground;
4370
4707
  const isWorking = backgroundRuns.some((run) => run.status === "running");
4371
- (0, import_react17.useEffect)(() => {
4708
+ (0, import_react18.useEffect)(() => {
4372
4709
  if (options.background !== true || options.threadId === void 0) {
4373
4710
  return;
4374
4711
  }
@@ -4378,7 +4715,7 @@ function useAgentChat(options) {
4378
4715
  options.threadId,
4379
4716
  refreshBackground
4380
4717
  ]);
4381
- (0, import_react17.useEffect)(() => {
4718
+ (0, import_react18.useEffect)(() => {
4382
4719
  const every = latest.current.backgroundPollMs ?? 5e3;
4383
4720
  if (latest.current.background !== true || !isWorking || every <= 0) {
4384
4721
  return;
@@ -4389,26 +4726,26 @@ function useAgentChat(options) {
4389
4726
  isWorking,
4390
4727
  refreshBackground
4391
4728
  ]);
4392
- const loadThreads = (0, import_react17.useCallback)(async () => {
4729
+ const loadThreads = (0, import_react18.useCallback)(async () => {
4393
4730
  const list = await client.listThreads();
4394
4731
  setThreads(list);
4395
4732
  return list;
4396
4733
  }, [
4397
4734
  client
4398
4735
  ]);
4399
- const loadThread = (0, import_react17.useCallback)((id) => client.getThread(id), [
4736
+ const loadThread = (0, import_react18.useCallback)((id) => client.getThread(id), [
4400
4737
  client
4401
4738
  ]);
4402
- const deleteThread = (0, import_react17.useCallback)(async (id) => {
4739
+ const deleteThread = (0, import_react18.useCallback)(async (id) => {
4403
4740
  await client.deleteThread(id);
4404
4741
  setThreads((current) => current.filter((thread) => thread.id !== id));
4405
4742
  }, [
4406
4743
  client
4407
4744
  ]);
4408
- const forkThread = (0, import_react17.useCallback)((threadId, messageId) => requireBackendMethod(client, "forkFromMessage")(threadId, messageId), [
4745
+ const forkThread = (0, import_react18.useCallback)((threadId, messageId) => requireBackendMethod(client, "forkFromMessage")(threadId, messageId), [
4409
4746
  client
4410
4747
  ]);
4411
- const renameThread = (0, import_react17.useCallback)(async (id, title) => {
4748
+ const renameThread = (0, import_react18.useCallback)(async (id, title) => {
4412
4749
  await client.updateThread(id, {
4413
4750
  title
4414
4751
  });
@@ -4419,24 +4756,24 @@ function useAgentChat(options) {
4419
4756
  }, [
4420
4757
  client
4421
4758
  ]);
4422
- const promoteThread = (0, import_react17.useCallback)(async (id) => {
4759
+ const promoteThread = (0, import_react18.useCallback)(async (id) => {
4423
4760
  await requireBackendMethod(client, "promoteThread")(id);
4424
4761
  await loadThreads();
4425
4762
  }, [
4426
4763
  client,
4427
4764
  loadThreads
4428
4765
  ]);
4429
- const truncateFromMessage = (0, import_react17.useCallback)((threadId, messageId) => requireBackendMethod(client, "truncateFromMessage")(threadId, messageId).then(() => void 0), [
4766
+ const truncateFromMessage = (0, import_react18.useCallback)((threadId, messageId) => requireBackendMethod(client, "truncateFromMessage")(threadId, messageId).then(() => void 0), [
4430
4767
  client
4431
4768
  ]);
4432
- const loadQuota = (0, import_react17.useCallback)(async () => {
4769
+ const loadQuota = (0, import_react18.useCallback)(async () => {
4433
4770
  const today = await requireBackendMethod(client, "getQuotaToday")();
4434
4771
  setQuota(today);
4435
4772
  return today;
4436
4773
  }, [
4437
4774
  client
4438
4775
  ]);
4439
- const cancel = (0, import_react17.useCallback)(async () => {
4776
+ const cancel = (0, import_react18.useCallback)(async () => {
4440
4777
  chatRef.current.stop();
4441
4778
  const activeRunId2 = runIdRef.current;
4442
4779
  if (activeRunId2) {
@@ -4448,7 +4785,7 @@ function useAgentChat(options) {
4448
4785
  }, [
4449
4786
  client
4450
4787
  ]);
4451
- const approve = (0, import_react17.useCallback)(async ({ toolCallId, remember, via }) => {
4788
+ const approve = (0, import_react18.useCallback)(async ({ toolCallId, remember, via }) => {
4452
4789
  await requireBackendMethod(client, "approveToolCall")({
4453
4790
  toolCallId,
4454
4791
  ...remember === true ? {
@@ -4461,7 +4798,7 @@ function useAgentChat(options) {
4461
4798
  }, [
4462
4799
  client
4463
4800
  ]);
4464
- const reject = (0, import_react17.useCallback)(async ({ toolCallId, reason, via }) => {
4801
+ const reject = (0, import_react18.useCallback)(async ({ toolCallId, reason, via }) => {
4465
4802
  await requireBackendMethod(client, "rejectToolCall")({
4466
4803
  toolCallId,
4467
4804
  ...reason !== void 0 ? {
@@ -4474,7 +4811,7 @@ function useAgentChat(options) {
4474
4811
  }, [
4475
4812
  client
4476
4813
  ]);
4477
- const answer = (0, import_react17.useCallback)(async ({ toolCallId, answers }) => {
4814
+ const answer = (0, import_react18.useCallback)(async ({ toolCallId, answers }) => {
4478
4815
  await requireBackendMethod(client, "answerToolCall")({
4479
4816
  toolCallId,
4480
4817
  ...answers !== void 0 ? {
@@ -4484,14 +4821,14 @@ function useAgentChat(options) {
4484
4821
  }, [
4485
4822
  client
4486
4823
  ]);
4487
- const skip = (0, import_react17.useCallback)(async ({ toolCallId }) => {
4824
+ const skip = (0, import_react18.useCallback)(async ({ toolCallId }) => {
4488
4825
  await requireBackendMethod(client, "skipToolCall")({
4489
4826
  toolCallId
4490
4827
  });
4491
4828
  }, [
4492
4829
  client
4493
4830
  ]);
4494
- const regenerate = (0, import_react17.useCallback)(() => {
4831
+ const regenerate = (0, import_react18.useCallback)(() => {
4495
4832
  const blocked = latest.current.blocked;
4496
4833
  if (blocked != null) throw new QuotaBlockedError(blocked);
4497
4834
  if (isTurnInFlight()) return;
@@ -4508,7 +4845,7 @@ function useAgentChat(options) {
4508
4845
  isWorking,
4509
4846
  refresh: refreshBackground
4510
4847
  };
4511
- const setThreadModel = (0, import_react17.useCallback)(async (model) => {
4848
+ const setThreadModel = (0, import_react18.useCallback)(async (model) => {
4512
4849
  const threadId = latest.current.threadId ?? createdThreadId.current;
4513
4850
  if (threadId === void 0) {
4514
4851
  throw new Error("setThreadModel: this chat has no thread yet");
@@ -4522,7 +4859,7 @@ function useAgentChat(options) {
4522
4859
  }, [
4523
4860
  client
4524
4861
  ]);
4525
- const getThreadId = (0, import_react17.useCallback)(() => latest.current.threadId ?? createdThreadId.current, []);
4862
+ const getThreadId = (0, import_react18.useCallback)(() => latest.current.threadId ?? createdThreadId.current, []);
4526
4863
  const status = connection.status === "reconnecting" ? "reconnecting" : chat.status;
4527
4864
  return {
4528
4865
  ...chat,
@@ -4596,6 +4933,7 @@ __name(mergeHeaders, "mergeHeaders");
4596
4933
  MessageList,
4597
4934
  QuotaBlockedError,
4598
4935
  SPEECH_LANGUAGES,
4936
+ acceptsFile,
4599
4937
  applyCompletion,
4600
4938
  approvalCountdown,
4601
4939
  backgroundRunsFromThread,
@@ -4606,7 +4944,10 @@ __name(mergeHeaders, "mergeHeaders");
4606
4944
  describeTimestamp,
4607
4945
  describeToolCall,
4608
4946
  describeUsage,
4947
+ dragHasFiles,
4609
4948
  extractMessageText,
4949
+ fileKind,
4950
+ filesFromClipboard,
4610
4951
  fillTemplate,
4611
4952
  filterAutocompleteItems,
4612
4953
  findActiveTrigger,
@@ -4616,6 +4957,7 @@ __name(mergeHeaders, "mergeHeaders");
4616
4957
  inferResultView,
4617
4958
  isActionCall,
4618
4959
  loadStoredSpeechLang,
4960
+ messageFiles,
4619
4961
  notifyThreads,
4620
4962
  onThreadsEvent,
4621
4963
  persistSpeechLang,
@@ -4631,6 +4973,7 @@ __name(mergeHeaders, "mergeHeaders");
4631
4973
  useAgentChat,
4632
4974
  useAgents,
4633
4975
  useApprovalCountdown,
4976
+ useAttachments,
4634
4977
  useChatTranscript,
4635
4978
  useComposerAutocomplete,
4636
4979
  useElapsed,