@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.js CHANGED
@@ -125,6 +125,9 @@ var AgentClient = class {
125
125
  * what a caller then rides on `sendMessage({ text }, { body: { attachments: [...] } })`.
126
126
  */
127
127
  async uploadAttachment(file, options = {}) {
128
+ if (options.onProgress !== void 0 && this.options.fetch === void 0 && typeof XMLHttpRequest !== "undefined") {
129
+ return this.uploadWithProgress(file, options);
130
+ }
128
131
  const formData = new FormData();
129
132
  formData.append("file", file);
130
133
  const response = await this.fetchImpl()(`${this.baseUrl()}/agent/attachments`, {
@@ -139,7 +142,47 @@ var AgentClient = class {
139
142
  signal: options.signal
140
143
  } : {}
141
144
  });
142
- return this.handleResponse(response, "POST", "/agent/attachments");
145
+ const attachment = await this.handleResponse(response, "POST", "/agent/attachments");
146
+ options.onProgress?.(1);
147
+ return attachment;
148
+ }
149
+ async uploadWithProgress(file, { signal, onProgress }) {
150
+ const headers = {
151
+ accept: "application/json",
152
+ ...await this.resolveHeaders()
153
+ };
154
+ const url = `${this.baseUrl()}/agent/attachments`;
155
+ return new Promise((resolve, reject) => {
156
+ const xhr = new XMLHttpRequest();
157
+ xhr.open("POST", url);
158
+ for (const [name, value] of Object.entries(headers)) xhr.setRequestHeader(name, value);
159
+ xhr.withCredentials = this.options.credentials === "include";
160
+ xhr.upload.onprogress = (event) => {
161
+ if (event.lengthComputable && event.total > 0) onProgress?.(event.loaded / event.total);
162
+ };
163
+ xhr.onload = () => {
164
+ if (xhr.status < 200 || xhr.status >= 300) {
165
+ reject(new AgentHttpError(xhr.status, "POST", "/agent/attachments", xhr.statusText));
166
+ return;
167
+ }
168
+ onProgress?.(1);
169
+ resolve(JSON.parse(xhr.responseText));
170
+ };
171
+ xhr.onerror = () => reject(new TypeError("Network error while uploading the attachment"));
172
+ xhr.onabort = () => reject(new DOMException("The upload was aborted", "AbortError"));
173
+ if (signal !== void 0) {
174
+ if (signal.aborted) {
175
+ reject(new DOMException("The upload was aborted", "AbortError"));
176
+ return;
177
+ }
178
+ signal.addEventListener("abort", () => xhr.abort(), {
179
+ once: true
180
+ });
181
+ }
182
+ const formData = new FormData();
183
+ formData.append("file", file);
184
+ xhr.send(formData);
185
+ });
143
186
  }
144
187
  promoteThread(id) {
145
188
  return this.request("POST", `/agent/threads/${encodeURIComponent(id)}/promote`);
@@ -987,6 +1030,68 @@ function delay(ms, signal) {
987
1030
  }
988
1031
  __name(delay, "delay");
989
1032
 
1033
+ // src/attachments/files.ts
1034
+ function fileKind(mediaType) {
1035
+ const type = mediaType.toLowerCase();
1036
+ if (type.startsWith("image/")) return "image";
1037
+ if (type === "application/pdf") return "pdf";
1038
+ if (type.startsWith("text/") || type === "application/json") return "text";
1039
+ if (type.startsWith("audio/")) return "audio";
1040
+ if (type.startsWith("video/")) return "video";
1041
+ return "other";
1042
+ }
1043
+ __name(fileKind, "fileKind");
1044
+ function messageFiles(message) {
1045
+ const files = [];
1046
+ for (const part of message.parts) {
1047
+ if (part.type !== "file") continue;
1048
+ const file = part;
1049
+ const filename = file.filename ?? null;
1050
+ const dot = filename?.lastIndexOf(".") ?? -1;
1051
+ const mediaId = file.providerMetadata?.agent?.mediaId;
1052
+ files.push({
1053
+ url: file.url,
1054
+ mediaType: file.mediaType,
1055
+ filename,
1056
+ kind: fileKind(file.mediaType),
1057
+ extension: filename !== null && dot > 0 ? filename.slice(dot + 1).toLowerCase() : null,
1058
+ mediaId: typeof mediaId === "string" ? mediaId : null
1059
+ });
1060
+ }
1061
+ return files;
1062
+ }
1063
+ __name(messageFiles, "messageFiles");
1064
+ function acceptsFile(accept, file) {
1065
+ const rules = (typeof accept === "string" ? accept.split(",") : accept ?? []).map((rule) => rule.trim().toLowerCase()).filter((rule) => rule.length > 0);
1066
+ if (rules.length === 0) return true;
1067
+ const type = file.type.toLowerCase();
1068
+ const name = file.name.toLowerCase();
1069
+ return rules.some((rule) => {
1070
+ if (rule.startsWith(".")) return name.endsWith(rule);
1071
+ if (rule.endsWith("/*")) return type.startsWith(rule.slice(0, -1));
1072
+ return type === rule;
1073
+ });
1074
+ }
1075
+ __name(acceptsFile, "acceptsFile");
1076
+ function filesFromClipboard(data) {
1077
+ if (!data) return [];
1078
+ const files = [];
1079
+ for (const item of Array.from(data.items ?? [])) {
1080
+ if (item.kind !== "file") continue;
1081
+ const file = item.getAsFile();
1082
+ if (file !== null) files.push(file);
1083
+ }
1084
+ return files.length > 0 ? files : Array.from(data.files ?? []);
1085
+ }
1086
+ __name(filesFromClipboard, "filesFromClipboard");
1087
+ function dragHasFiles(data) {
1088
+ return Array.from(data?.types ?? []).includes("Files");
1089
+ }
1090
+ __name(dragHasFiles, "dragHasFiles");
1091
+
1092
+ // src/attachments/use-attachments.ts
1093
+ import { useCallback, useEffect as useEffect2, useMemo, useRef as useRef2, useState as useState2 } from "react";
1094
+
990
1095
  // src/backend.ts
991
1096
  var AgentBackendUnsupportedError = class extends Error {
992
1097
  static {
@@ -1007,19 +1112,239 @@ function requireBackendMethod(backend, name) {
1007
1112
  }
1008
1113
  __name(requireBackendMethod, "requireBackendMethod");
1009
1114
 
1115
+ // src/attachments/use-attachments.ts
1116
+ var DEFAULT_REJECTION = {
1117
+ type: /* @__PURE__ */ __name((file) => `${file.name} is not a supported file type`, "type"),
1118
+ size: /* @__PURE__ */ __name((file) => `${file.name} is too large`, "size"),
1119
+ count: /* @__PURE__ */ __name(() => "Too many files", "count")
1120
+ };
1121
+ var sequence = 0;
1122
+ function useAttachments(options) {
1123
+ const latest = useRef2(options);
1124
+ latest.current = options;
1125
+ const [items, setItems] = useState2([]);
1126
+ const itemsRef = useRef2(items);
1127
+ itemsRef.current = items;
1128
+ const controllers = useRef2(/* @__PURE__ */ new Map());
1129
+ const [dragDepth, setDragDepth] = useState2(0);
1130
+ const patch = useCallback((id, change) => {
1131
+ setItems((current) => current.map((item) => item.id === id ? {
1132
+ ...item,
1133
+ ...change
1134
+ } : item));
1135
+ }, []);
1136
+ const start = useCallback((item) => {
1137
+ const { upload, backend } = latest.current;
1138
+ const run = upload ?? (backend?.uploadAttachment !== void 0 ? backend.uploadAttachment.bind(backend) : void 0);
1139
+ if (run === void 0) {
1140
+ patch(item.id, {
1141
+ status: "error",
1142
+ error: new AgentBackendUnsupportedError("uploadAttachment").message
1143
+ });
1144
+ return;
1145
+ }
1146
+ const controller = new AbortController();
1147
+ controllers.current.set(item.id, controller);
1148
+ run(item.file, {
1149
+ signal: controller.signal,
1150
+ onProgress: /* @__PURE__ */ __name((fraction) => patch(item.id, {
1151
+ progress: Math.max(0, Math.min(1, fraction))
1152
+ }), "onProgress")
1153
+ }).then((attachment) => {
1154
+ if (controller.signal.aborted) return;
1155
+ controllers.current.delete(item.id);
1156
+ patch(item.id, {
1157
+ status: "ready",
1158
+ progress: 1,
1159
+ attachment
1160
+ });
1161
+ }, (error) => {
1162
+ if (controller.signal.aborted) return;
1163
+ controllers.current.delete(item.id);
1164
+ patch(item.id, {
1165
+ status: "error",
1166
+ error: error instanceof Error ? error.message : String(error)
1167
+ });
1168
+ });
1169
+ }, [
1170
+ patch
1171
+ ]);
1172
+ const add = useCallback((files) => {
1173
+ if (files == null) return;
1174
+ const { accept: accept2, maxBytes, maxFiles, describeRejection } = latest.current;
1175
+ const describe = /* @__PURE__ */ __name((reason, file) => describeRejection?.(reason, file) ?? DEFAULT_REJECTION[reason](file), "describe");
1176
+ let room = maxFiles === void 0 ? Number.POSITIVE_INFINITY : maxFiles - itemsRef.current.filter((item) => item.status !== "rejected").length;
1177
+ const staged = [];
1178
+ for (const file of Array.from(files)) {
1179
+ sequence += 1;
1180
+ const base = {
1181
+ id: `att-${sequence}`,
1182
+ file,
1183
+ name: file.name,
1184
+ size: file.size,
1185
+ type: file.type,
1186
+ progress: 0
1187
+ };
1188
+ const reason = !acceptsFile(accept2, file) ? "type" : maxBytes !== void 0 && file.size > maxBytes ? "size" : room <= 0 ? "count" : void 0;
1189
+ if (reason !== void 0) {
1190
+ staged.push({
1191
+ ...base,
1192
+ status: "rejected",
1193
+ error: describe(reason, file)
1194
+ });
1195
+ continue;
1196
+ }
1197
+ room -= 1;
1198
+ const previewUrl = file.type.startsWith("image/") && typeof URL.createObjectURL === "function" ? URL.createObjectURL(file) : void 0;
1199
+ staged.push({
1200
+ ...base,
1201
+ status: "uploading",
1202
+ ...previewUrl ? {
1203
+ previewUrl
1204
+ } : {}
1205
+ });
1206
+ }
1207
+ if (staged.length === 0) return;
1208
+ setItems((current) => [
1209
+ ...current,
1210
+ ...staged
1211
+ ]);
1212
+ for (const item of staged) {
1213
+ if (item.status === "uploading") start(item);
1214
+ }
1215
+ }, [
1216
+ start
1217
+ ]);
1218
+ const release = useCallback((item) => {
1219
+ if (item === void 0) return;
1220
+ controllers.current.get(item.id)?.abort();
1221
+ controllers.current.delete(item.id);
1222
+ if (item.previewUrl !== void 0 && typeof URL.revokeObjectURL === "function") {
1223
+ URL.revokeObjectURL(item.previewUrl);
1224
+ }
1225
+ }, []);
1226
+ const remove = useCallback((id) => {
1227
+ release(itemsRef.current.find((item) => item.id === id));
1228
+ setItems((current) => current.filter((item) => item.id !== id));
1229
+ }, [
1230
+ release
1231
+ ]);
1232
+ const clear = useCallback(() => {
1233
+ for (const item of itemsRef.current) release(item);
1234
+ setItems([]);
1235
+ }, [
1236
+ release
1237
+ ]);
1238
+ const retry = useCallback((id) => {
1239
+ const item = itemsRef.current.find((candidate) => candidate.id === id);
1240
+ if (item === void 0 || item.status !== "error") return;
1241
+ const { error: _previous, ...rest } = item;
1242
+ const again = {
1243
+ ...rest,
1244
+ status: "uploading",
1245
+ progress: 0
1246
+ };
1247
+ setItems((current) => current.map((candidate) => candidate.id === id ? again : candidate));
1248
+ start(again);
1249
+ }, [
1250
+ start
1251
+ ]);
1252
+ useEffect2(() => () => {
1253
+ for (const item of itemsRef.current) release(item);
1254
+ }, [
1255
+ release
1256
+ ]);
1257
+ const onPaste = useCallback((event) => {
1258
+ const files = filesFromClipboard(event.clipboardData);
1259
+ if (files.length === 0) return;
1260
+ event.preventDefault();
1261
+ add(files);
1262
+ }, [
1263
+ add
1264
+ ]);
1265
+ const dropZoneProps = useMemo(() => ({
1266
+ onDragEnter: /* @__PURE__ */ __name((event) => {
1267
+ if (!dragHasFiles(event.dataTransfer)) return;
1268
+ event.preventDefault();
1269
+ setDragDepth((depth) => depth + 1);
1270
+ }, "onDragEnter"),
1271
+ onDragOver: /* @__PURE__ */ __name((event) => {
1272
+ if (dragHasFiles(event.dataTransfer)) event.preventDefault();
1273
+ }, "onDragOver"),
1274
+ onDragLeave: /* @__PURE__ */ __name((event) => {
1275
+ if (!dragHasFiles(event.dataTransfer)) return;
1276
+ setDragDepth((depth) => Math.max(0, depth - 1));
1277
+ }, "onDragLeave"),
1278
+ onDrop: /* @__PURE__ */ __name((event) => {
1279
+ if (!dragHasFiles(event.dataTransfer)) return;
1280
+ event.preventDefault();
1281
+ setDragDepth(0);
1282
+ add(event.dataTransfer?.files);
1283
+ }, "onDrop")
1284
+ }), [
1285
+ add
1286
+ ]);
1287
+ const accept = options.accept;
1288
+ const inputProps = useMemo(() => ({
1289
+ type: "file",
1290
+ accept: typeof accept === "string" ? accept : accept?.join(","),
1291
+ multiple: options.maxFiles === void 0 || options.maxFiles > 1,
1292
+ onChange: /* @__PURE__ */ __name((event) => {
1293
+ const input = event.currentTarget;
1294
+ add(input.files);
1295
+ input.value = "";
1296
+ }, "onChange")
1297
+ }), [
1298
+ accept,
1299
+ options.maxFiles,
1300
+ add
1301
+ ]);
1302
+ return useMemo(() => {
1303
+ const ready = items.filter((item) => item.status === "ready" && item.attachment !== void 0);
1304
+ const attachments = ready.map((item) => item.attachment);
1305
+ return {
1306
+ items,
1307
+ add,
1308
+ remove,
1309
+ retry,
1310
+ clear,
1311
+ isUploading: items.some((item) => item.status === "uploading"),
1312
+ attachments,
1313
+ refs: attachments.map((attachment) => ({
1314
+ mediaId: attachment.mediaId
1315
+ })),
1316
+ inputProps,
1317
+ dropZoneProps,
1318
+ isDragging: dragDepth > 0,
1319
+ onPaste
1320
+ };
1321
+ }, [
1322
+ items,
1323
+ add,
1324
+ remove,
1325
+ retry,
1326
+ clear,
1327
+ inputProps,
1328
+ dropZoneProps,
1329
+ dragDepth,
1330
+ onPaste
1331
+ ]);
1332
+ }
1333
+ __name(useAttachments, "useAttachments");
1334
+
1010
1335
  // src/catalog/use-agents.ts
1011
- import { useMemo } from "react";
1336
+ import { useMemo as useMemo2 } from "react";
1012
1337
 
1013
1338
  // src/catalog/use-resource.ts
1014
- import { useCallback, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
1339
+ import { useCallback as useCallback2, useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
1015
1340
  function useResource(load2, key, enabled) {
1016
- const [data, setData] = useState2(void 0);
1017
- const [isLoading, setIsLoading] = useState2(enabled);
1018
- const [error, setError] = useState2(null);
1019
- const loadRef = useRef2(load2);
1341
+ const [data, setData] = useState3(void 0);
1342
+ const [isLoading, setIsLoading] = useState3(enabled);
1343
+ const [error, setError] = useState3(null);
1344
+ const loadRef = useRef3(load2);
1020
1345
  loadRef.current = load2;
1021
- const generation = useRef2(0);
1022
- const refresh = useCallback(async () => {
1346
+ const generation = useRef3(0);
1347
+ const refresh = useCallback2(async () => {
1023
1348
  const mine = ++generation.current;
1024
1349
  setIsLoading(true);
1025
1350
  try {
@@ -1034,7 +1359,7 @@ function useResource(load2, key, enabled) {
1034
1359
  if (mine === generation.current) setIsLoading(false);
1035
1360
  }
1036
1361
  }, []);
1037
- useEffect2(() => {
1362
+ useEffect3(() => {
1038
1363
  if (!enabled) return;
1039
1364
  void refresh();
1040
1365
  }, [
@@ -1055,7 +1380,7 @@ __name(useResource, "useResource");
1055
1380
  function useAgents(options) {
1056
1381
  const { backend, enabled = true } = options;
1057
1382
  const { data, isLoading, error, refresh } = useResource(() => requireBackendMethod(backend, "listAgents")(), "", enabled);
1058
- return useMemo(() => {
1383
+ return useMemo2(() => {
1059
1384
  const agents = data ?? [];
1060
1385
  return {
1061
1386
  agents,
@@ -1074,11 +1399,11 @@ function useAgents(options) {
1074
1399
  __name(useAgents, "useAgents");
1075
1400
 
1076
1401
  // src/catalog/use-models.ts
1077
- import { useMemo as useMemo2 } from "react";
1402
+ import { useMemo as useMemo3 } from "react";
1078
1403
  function useModels(options) {
1079
1404
  const { backend, agent, enabled = true } = options;
1080
1405
  const { data, isLoading, error, refresh } = useResource(() => requireBackendMethod(backend, "listModels")(agent), `${agent ?? ""}`, enabled);
1081
- return useMemo2(() => {
1406
+ return useMemo3(() => {
1082
1407
  const providers = data?.providers ?? [];
1083
1408
  const models = providers.flatMap((provider) => provider.models.map((model) => ({
1084
1409
  ...model,
@@ -1108,7 +1433,7 @@ function useModels(options) {
1108
1433
  __name(useModels, "useModels");
1109
1434
 
1110
1435
  // src/quota/use-quota.ts
1111
- import { useEffect as useEffect3, useMemo as useMemo3 } from "react";
1436
+ import { useEffect as useEffect4, useMemo as useMemo4 } from "react";
1112
1437
 
1113
1438
  // src/threads/threads-events.ts
1114
1439
  var listeners = /* @__PURE__ */ new WeakMap();
@@ -1135,7 +1460,7 @@ __name(onThreadsEvent, "onThreadsEvent");
1135
1460
  function useQuota(options) {
1136
1461
  const { backend, enabled = true, pollMs } = options;
1137
1462
  const { data, isLoading, error, refresh } = useResource(() => requireBackendMethod(backend, "getQuota")(), "", enabled);
1138
- useEffect3(() => {
1463
+ useEffect4(() => {
1139
1464
  if (!enabled) return;
1140
1465
  return onThreadsEvent(backend, (event) => {
1141
1466
  if (event.type === "changed") void refresh();
@@ -1145,7 +1470,7 @@ function useQuota(options) {
1145
1470
  enabled,
1146
1471
  refresh
1147
1472
  ]);
1148
- useEffect3(() => {
1473
+ useEffect4(() => {
1149
1474
  if (!enabled || pollMs === void 0 || pollMs <= 0) return;
1150
1475
  const timer = setInterval(() => void refresh(), pollMs);
1151
1476
  return () => clearInterval(timer);
@@ -1154,7 +1479,7 @@ function useQuota(options) {
1154
1479
  pollMs,
1155
1480
  refresh
1156
1481
  ]);
1157
- return useMemo3(() => {
1482
+ return useMemo4(() => {
1158
1483
  const windows = data?.windows ?? [];
1159
1484
  return {
1160
1485
  report: data,
@@ -1176,27 +1501,27 @@ function useQuota(options) {
1176
1501
  __name(useQuota, "useQuota");
1177
1502
 
1178
1503
  // src/feedback/use-message-feedback.ts
1179
- import { useCallback as useCallback2, useRef as useRef3, useState as useState3 } from "react";
1504
+ import { useCallback as useCallback3, useRef as useRef4, useState as useState4 } from "react";
1180
1505
  function useMessageFeedback(options) {
1181
- const latest = useRef3(options);
1506
+ const latest = useRef4(options);
1182
1507
  latest.current = options;
1183
- const [local, setLocal] = useState3(/* @__PURE__ */ new Map());
1184
- const [pending, setPending] = useState3(/* @__PURE__ */ new Set());
1185
- const [error, setError] = useState3(null);
1186
- const localRef = useRef3(local);
1508
+ const [local, setLocal] = useState4(/* @__PURE__ */ new Map());
1509
+ const [pending, setPending] = useState4(/* @__PURE__ */ new Set());
1510
+ const [error, setError] = useState4(null);
1511
+ const localRef = useRef4(local);
1187
1512
  localRef.current = local;
1188
- const resolved = useRef3(/* @__PURE__ */ new Map());
1189
- const feedbackOf = useCallback2((message) => {
1513
+ const resolved = useRef4(/* @__PURE__ */ new Map());
1514
+ const feedbackOf = useCallback3((message) => {
1190
1515
  const mine = local.get(message.id);
1191
1516
  if (mine !== void 0) return mine.feedback;
1192
1517
  return metadataOf(message).feedback ?? null;
1193
1518
  }, [
1194
1519
  local
1195
1520
  ]);
1196
- const isPending = useCallback2((message) => pending.has(message.id), [
1521
+ const isPending = useCallback3((message) => pending.has(message.id), [
1197
1522
  pending
1198
1523
  ]);
1199
- const storedIdFor = useCallback2(async (message) => {
1524
+ const storedIdFor = useCallback3(async (message) => {
1200
1525
  const runId = metadataOf(message).runId;
1201
1526
  if (runId === void 0) return message.id;
1202
1527
  const known = resolved.current.get(message.id);
@@ -1216,7 +1541,7 @@ function useMessageFeedback(options) {
1216
1541
  resolved.current.set(message.id, row.id);
1217
1542
  return row.id;
1218
1543
  }, []);
1219
- const rate = useCallback2(async (message, value, comment) => {
1544
+ const rate = useCallback3(async (message, value, comment) => {
1220
1545
  const setFeedback = requireBackendMethod(latest.current.backend, "setMessageFeedback");
1221
1546
  const had = localRef.current.get(message.id);
1222
1547
  const optimistic = value === null ? null : {
@@ -1262,7 +1587,7 @@ function useMessageFeedback(options) {
1262
1587
  }, [
1263
1588
  storedIdFor
1264
1589
  ]);
1265
- const toggle = useCallback2((message, value) => {
1590
+ const toggle = useCallback3((message, value) => {
1266
1591
  const mine = localRef.current.get(message.id);
1267
1592
  const current = mine !== void 0 ? mine.feedback : metadataOf(message).feedback ?? null;
1268
1593
  return rate(message, current?.value === value ? null : value);
@@ -1285,16 +1610,16 @@ function metadataOf(message) {
1285
1610
  __name(metadataOf, "metadataOf");
1286
1611
 
1287
1612
  // src/threads/use-threads.ts
1288
- import { useCallback as useCallback3, useEffect as useEffect4, useRef as useRef4, useState as useState4 } from "react";
1613
+ import { useCallback as useCallback4, useEffect as useEffect5, useRef as useRef5, useState as useState5 } from "react";
1289
1614
  function useThreads(options) {
1290
1615
  const { backend, enabled = true } = options;
1291
- const [threads, setThreads] = useState4([]);
1292
- const [isLoading, setIsLoading] = useState4(enabled);
1293
- const [error, setError] = useState4(null);
1294
- const threadsRef = useRef4(threads);
1616
+ const [threads, setThreads] = useState5([]);
1617
+ const [isLoading, setIsLoading] = useState5(enabled);
1618
+ const [error, setError] = useState5(null);
1619
+ const threadsRef = useRef5(threads);
1295
1620
  threadsRef.current = threads;
1296
- const generation = useRef4(0);
1297
- const refresh = useCallback3(async () => {
1621
+ const generation = useRef5(0);
1622
+ const refresh = useCallback4(async () => {
1298
1623
  const mine = ++generation.current;
1299
1624
  setIsLoading(true);
1300
1625
  try {
@@ -1311,7 +1636,7 @@ function useThreads(options) {
1311
1636
  }, [
1312
1637
  backend
1313
1638
  ]);
1314
- useEffect4(() => {
1639
+ useEffect5(() => {
1315
1640
  if (!enabled) return;
1316
1641
  void refresh();
1317
1642
  return onThreadsEvent(backend, (event) => {
@@ -1331,7 +1656,7 @@ function useThreads(options) {
1331
1656
  enabled,
1332
1657
  refresh
1333
1658
  ]);
1334
- const rename = useCallback3(async (id, title) => {
1659
+ const rename = useCallback4(async (id, title) => {
1335
1660
  const before = threadsRef.current;
1336
1661
  setThreads((current) => current.map((thread) => thread.id === id ? {
1337
1662
  ...thread,
@@ -1353,7 +1678,7 @@ function useThreads(options) {
1353
1678
  }, [
1354
1679
  backend
1355
1680
  ]);
1356
- const remove = useCallback3(async (id) => {
1681
+ const remove = useCallback4(async (id) => {
1357
1682
  const before = threadsRef.current;
1358
1683
  setThreads((current) => current.filter((thread) => thread.id !== id));
1359
1684
  try {
@@ -1428,10 +1753,10 @@ function backgroundRunsFromThread(messages) {
1428
1753
  __name(backgroundRunsFromThread, "backgroundRunsFromThread");
1429
1754
 
1430
1755
  // src/components/chat-input.tsx
1431
- import React2, { useEffect as useEffect6, useState as useState6 } from "react";
1756
+ import React2, { useEffect as useEffect7, useState as useState7 } from "react";
1432
1757
 
1433
1758
  // src/components/use-speech-recognition.ts
1434
- import { useCallback as useCallback4, useEffect as useEffect5, useMemo as useMemo4, useRef as useRef5, useState as useState5 } from "react";
1759
+ import { useCallback as useCallback5, useEffect as useEffect6, useMemo as useMemo5, useRef as useRef6, useState as useState6 } from "react";
1435
1760
  function getRecognitionCtor() {
1436
1761
  if (typeof window === "undefined") return null;
1437
1762
  const scope = window;
@@ -1439,15 +1764,15 @@ function getRecognitionCtor() {
1439
1764
  }
1440
1765
  __name(getRecognitionCtor, "getRecognitionCtor");
1441
1766
  function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTranscript }) {
1442
- const recognitionRef = useRef5(null);
1443
- const [isListening, setIsListening] = useState5(false);
1444
- const ctor = useMemo4(() => getRecognitionCtor(), []);
1767
+ const recognitionRef = useRef6(null);
1768
+ const [isListening, setIsListening] = useState6(false);
1769
+ const ctor = useMemo5(() => getRecognitionCtor(), []);
1445
1770
  const isSupported = ctor !== null;
1446
- const finalRef = useRef5(onFinalTranscript);
1447
- const interimRef = useRef5(onInterimTranscript);
1771
+ const finalRef = useRef6(onFinalTranscript);
1772
+ const interimRef = useRef6(onInterimTranscript);
1448
1773
  finalRef.current = onFinalTranscript;
1449
1774
  interimRef.current = onInterimTranscript;
1450
- useEffect5(() => {
1775
+ useEffect6(() => {
1451
1776
  if (!ctor) return;
1452
1777
  const recognition = new ctor();
1453
1778
  recognition.lang = lang;
@@ -1486,7 +1811,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
1486
1811
  ctor,
1487
1812
  lang
1488
1813
  ]);
1489
- const start = useCallback4(() => {
1814
+ const start = useCallback5(() => {
1490
1815
  const recognition = recognitionRef.current;
1491
1816
  if (!recognition) return;
1492
1817
  try {
@@ -1496,7 +1821,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
1496
1821
  console.warn("SpeechRecognition start failed", error);
1497
1822
  }
1498
1823
  }, []);
1499
- const stop = useCallback4(() => {
1824
+ const stop = useCallback5(() => {
1500
1825
  const recognition = recognitionRef.current;
1501
1826
  if (!recognition) return;
1502
1827
  try {
@@ -1515,8 +1840,8 @@ __name(useSpeechRecognition, "useSpeechRecognition");
1515
1840
 
1516
1841
  // src/components/chat-input.tsx
1517
1842
  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 }) {
1518
- const [value, setValue] = useState6("");
1519
- const [interim, setInterim] = useState6("");
1843
+ const [value, setValue] = useState7("");
1844
+ const [interim, setInterim] = useState7("");
1520
1845
  const speech = useSpeechRecognition({
1521
1846
  lang: speechLang,
1522
1847
  onFinalTranscript: /* @__PURE__ */ __name((text) => {
@@ -1527,7 +1852,7 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
1527
1852
  }, "onFinalTranscript"),
1528
1853
  onInterimTranscript: /* @__PURE__ */ __name((text) => setInterim(text), "onInterimTranscript")
1529
1854
  });
1530
- useEffect6(() => {
1855
+ useEffect7(() => {
1531
1856
  if (!speech.isListening) setInterim("");
1532
1857
  }, [
1533
1858
  speech.isListening
@@ -1594,7 +1919,7 @@ __name(ChatInput, "ChatInput");
1594
1919
  import React3 from "react";
1595
1920
 
1596
1921
  // src/transcript/use-chat-transcript.ts
1597
- import { useCallback as useCallback6, useEffect as useEffect8, useRef as useRef7, useState as useState8 } from "react";
1922
+ import { useCallback as useCallback7, useEffect as useEffect9, useRef as useRef8, useState as useState9 } from "react";
1598
1923
 
1599
1924
  // src/transcript/model.ts
1600
1925
  import { readElicitationInput, validateElicitationAnswer as validateElicitationAnswer2 } from "@dudousxd/nestjs-agent-core";
@@ -2571,11 +2896,11 @@ function formatCostUsd(cost) {
2571
2896
  __name(formatCostUsd, "formatCostUsd");
2572
2897
 
2573
2898
  // src/transcript/use-stick-to-bottom.ts
2574
- import { useCallback as useCallback5, useEffect as useEffect7, useRef as useRef6, useState as useState7 } from "react";
2899
+ import { useCallback as useCallback6, useEffect as useEffect8, useRef as useRef7, useState as useState8 } from "react";
2575
2900
  function useStickToBottom({ contentKey, threshold = 32 }) {
2576
- const containerRef = useRef6(null);
2577
- const [isAtBottom, setIsAtBottom] = useState7(true);
2578
- const measure = useCallback5(() => {
2901
+ const containerRef = useRef7(null);
2902
+ const [isAtBottom, setIsAtBottom] = useState8(true);
2903
+ const measure = useCallback6(() => {
2579
2904
  const element = containerRef.current;
2580
2905
  if (!element) {
2581
2906
  return;
@@ -2585,7 +2910,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
2585
2910
  }, [
2586
2911
  threshold
2587
2912
  ]);
2588
- const scrollToBottom = useCallback5(() => {
2913
+ const scrollToBottom = useCallback6(() => {
2589
2914
  const element = containerRef.current;
2590
2915
  if (!element) {
2591
2916
  return;
@@ -2593,7 +2918,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
2593
2918
  element.scrollTop = element.scrollHeight;
2594
2919
  setIsAtBottom(true);
2595
2920
  }, []);
2596
- const setContainer = useCallback5((element) => {
2921
+ const setContainer = useCallback6((element) => {
2597
2922
  containerRef.current = element;
2598
2923
  if (element) {
2599
2924
  measure();
@@ -2601,7 +2926,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
2601
2926
  }, [
2602
2927
  measure
2603
2928
  ]);
2604
- useEffect7(() => {
2929
+ useEffect8(() => {
2605
2930
  if (isAtBottom) {
2606
2931
  scrollToBottom();
2607
2932
  }
@@ -2610,7 +2935,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
2610
2935
  isAtBottom,
2611
2936
  scrollToBottom
2612
2937
  ]);
2613
- const getContainerProps = useCallback5(() => ({
2938
+ const getContainerProps = useCallback6(() => ({
2614
2939
  ref: setContainer,
2615
2940
  onScroll: measure
2616
2941
  }), [
@@ -2632,14 +2957,14 @@ var DEFAULT_LOAD_EARLIER_STEP = 50;
2632
2957
  var DEFAULT_COPY_RESET_MS = 1500;
2633
2958
  function useChatTranscript(options) {
2634
2959
  const { messages, status, initialVisibleCount = DEFAULT_VISIBLE_COUNT, loadEarlierStep = DEFAULT_LOAD_EARLIER_STEP, followUps, stickToBottomThreshold } = options;
2635
- const latest = useRef7(options);
2960
+ const latest = useRef8(options);
2636
2961
  latest.current = options;
2637
- const [visibleCount, setVisibleCount] = useState8(initialVisibleCount);
2638
- const [stopRequested, setStopRequested] = useState8(false);
2962
+ const [visibleCount, setVisibleCount] = useState9(initialVisibleCount);
2963
+ const [stopRequested, setStopRequested] = useState9(false);
2639
2964
  const isStreaming = status === "streaming" || status === "reconnecting";
2640
2965
  const isBusy = status === "submitted" || isStreaming;
2641
2966
  const isEmpty = messages.length === 0;
2642
- useEffect8(() => {
2967
+ useEffect9(() => {
2643
2968
  if (!isBusy) {
2644
2969
  setStopRequested(false);
2645
2970
  }
@@ -2656,7 +2981,7 @@ function useChatTranscript(options) {
2656
2981
  streamingMessageId,
2657
2982
  options
2658
2983
  });
2659
- const stop = useCallback6(() => {
2984
+ const stop = useCallback7(() => {
2660
2985
  const current = latest.current;
2661
2986
  if (current.status !== "submitted" && current.status !== "streaming" && current.status !== "reconnecting") {
2662
2987
  return;
@@ -2664,8 +2989,8 @@ function useChatTranscript(options) {
2664
2989
  setStopRequested(true);
2665
2990
  void current.onStop?.();
2666
2991
  }, []);
2667
- const loadEarlier = useCallback6(() => setVisibleCount((count) => count + (latest.current.loadEarlierStep ?? DEFAULT_LOAD_EARLIER_STEP)), []);
2668
- const showAll = useCallback6(() => setVisibleCount(latest.current.messages.length), []);
2992
+ const loadEarlier = useCallback7(() => setVisibleCount((count) => count + (latest.current.loadEarlierStep ?? DEFAULT_LOAD_EARLIER_STEP)), []);
2993
+ const showAll = useCallback7(() => setVisibleCount(latest.current.messages.length), []);
2669
2994
  const scroll = useStickToBottom({
2670
2995
  contentKey: contentSignature(messages),
2671
2996
  ...stickToBottomThreshold !== void 0 ? {
@@ -2701,7 +3026,7 @@ function useChatTranscript(options) {
2701
3026
  __name(useChatTranscript, "useChatTranscript");
2702
3027
  function useTranscriptItem(options) {
2703
3028
  const { message, isStreaming = false } = options;
2704
- const messagesRef = useRef7([
3029
+ const messagesRef = useRef8([
2705
3030
  message
2706
3031
  ]);
2707
3032
  if (messagesRef.current[0] !== message) {
@@ -2719,13 +3044,13 @@ function useTranscriptItem(options) {
2719
3044
  }
2720
3045
  __name(useTranscriptItem, "useTranscriptItem");
2721
3046
  function useTranscriptItems({ messages, visibleStart, streamingMessageId, options }) {
2722
- const [drafts, setDrafts] = useState8(() => /* @__PURE__ */ new Map());
2723
- const [copied, setCopied] = useState8(() => /* @__PURE__ */ new Set());
2724
- const [openReasoning, setOpenReasoning] = useState8(() => /* @__PURE__ */ new Map());
2725
- const [picks, setPicks] = useState8(() => /* @__PURE__ */ new Map());
2726
- const [settling, setSettling] = useState8(() => /* @__PURE__ */ new Map());
2727
- const [settleErrors, setSettleErrors] = useState8(() => /* @__PURE__ */ new Map());
2728
- const latest = useRef7({
3047
+ const [drafts, setDrafts] = useState9(() => /* @__PURE__ */ new Map());
3048
+ const [copied, setCopied] = useState9(() => /* @__PURE__ */ new Set());
3049
+ const [openReasoning, setOpenReasoning] = useState9(() => /* @__PURE__ */ new Map());
3050
+ const [picks, setPicks] = useState9(() => /* @__PURE__ */ new Map());
3051
+ const [settling, setSettling] = useState9(() => /* @__PURE__ */ new Map());
3052
+ const [settleErrors, setSettleErrors] = useState9(() => /* @__PURE__ */ new Map());
3053
+ const latest = useRef8({
2729
3054
  options,
2730
3055
  drafts,
2731
3056
  picks,
@@ -2737,23 +3062,23 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2737
3062
  picks,
2738
3063
  messages
2739
3064
  };
2740
- const copyTimers = useRef7(/* @__PURE__ */ new Map());
2741
- useEffect8(() => () => {
3065
+ const copyTimers = useRef8(/* @__PURE__ */ new Map());
3066
+ useEffect9(() => () => {
2742
3067
  for (const timer of copyTimers.current.values()) {
2743
3068
  clearTimeout(timer);
2744
3069
  }
2745
3070
  copyTimers.current.clear();
2746
3071
  }, []);
2747
- const callbackCache = useRef7(/* @__PURE__ */ new Map());
2748
- const reasoningToggleCache = useRef7(/* @__PURE__ */ new Map());
2749
- const toggleReasoning = useCallback6((key, open) => {
3072
+ const callbackCache = useRef8(/* @__PURE__ */ new Map());
3073
+ const reasoningToggleCache = useRef8(/* @__PURE__ */ new Map());
3074
+ const toggleReasoning = useCallback7((key, open) => {
2750
3075
  setOpenReasoning((current) => {
2751
3076
  const next = new Map(current);
2752
3077
  next.set(key, open ?? !(current.get(key) ?? false));
2753
3078
  return next;
2754
3079
  });
2755
3080
  }, []);
2756
- const stableToggle = useCallback6((key) => {
3081
+ const stableToggle = useCallback7((key) => {
2757
3082
  let cached = reasoningToggleCache.current.get(key);
2758
3083
  if (!cached) {
2759
3084
  cached = /* @__PURE__ */ __name((open) => toggleReasoning(key, open), "cached");
@@ -2763,10 +3088,10 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2763
3088
  }, [
2764
3089
  toggleReasoning
2765
3090
  ]);
2766
- const pick = useCallback6((toolCallId, questionId, values) => {
3091
+ const pick = useCallback7((toolCallId, questionId, values) => {
2767
3092
  setPicks((current) => new Map(current).set(pickKey(toolCallId, questionId), values));
2768
3093
  }, []);
2769
- const settle = useCallback6((toolCallId, action, send) => {
3094
+ const settle = useCallback7((toolCallId, action, send) => {
2770
3095
  setSettleErrors((current) => {
2771
3096
  if (!current.has(toolCallId)) {
2772
3097
  return current;
@@ -2790,7 +3115,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2790
3115
  failed(error);
2791
3116
  }
2792
3117
  }, []);
2793
- const answer = useCallback6((toolCallId) => {
3118
+ const answer = useCallback7((toolCallId) => {
2794
3119
  const current = latest.current;
2795
3120
  const onAnswer = current.options.onAnswer;
2796
3121
  if (!onAnswer) {
@@ -2800,7 +3125,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2800
3125
  }, [
2801
3126
  settle
2802
3127
  ]);
2803
- const skip = useCallback6((toolCallId) => {
3128
+ const skip = useCallback7((toolCallId) => {
2804
3129
  const onSkip = latest.current.options.onSkip;
2805
3130
  if (onSkip) {
2806
3131
  settle(toolCallId, "skip", () => onSkip(toolCallId));
@@ -2808,7 +3133,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2808
3133
  }, [
2809
3134
  settle
2810
3135
  ]);
2811
- const approve = useCallback6((toolCallId, options2) => {
3136
+ const approve = useCallback7((toolCallId, options2) => {
2812
3137
  const onApprove = latest.current.options.onApprove;
2813
3138
  if (onApprove) {
2814
3139
  settle(toolCallId, "approve", () => options2 === void 0 ? onApprove(toolCallId) : onApprove(toolCallId, options2));
@@ -2816,7 +3141,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2816
3141
  }, [
2817
3142
  settle
2818
3143
  ]);
2819
- const reject = useCallback6((toolCallId) => {
3144
+ const reject = useCallback7((toolCallId) => {
2820
3145
  const onReject = latest.current.options.onReject;
2821
3146
  if (onReject) {
2822
3147
  settle(toolCallId, "reject", () => onReject(toolCallId));
@@ -2824,7 +3149,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2824
3149
  }, [
2825
3150
  settle
2826
3151
  ]);
2827
- const callbacksFor = useCallback6((id) => {
3152
+ const callbacksFor = useCallback7((id) => {
2828
3153
  const cached = callbackCache.current.get(id);
2829
3154
  if (cached) {
2830
3155
  return cached;
@@ -3481,35 +3806,35 @@ function toItem(entry) {
3481
3806
  __name(toItem, "toItem");
3482
3807
 
3483
3808
  // src/composer/use-composer-autocomplete.ts
3484
- import { useCallback as useCallback7, useEffect as useEffect9, useId, useLayoutEffect, useMemo as useMemo5, useRef as useRef8, useState as useState9 } from "react";
3809
+ import { useCallback as useCallback8, useEffect as useEffect10, useId, useLayoutEffect, useMemo as useMemo6, useRef as useRef9, useState as useState10 } from "react";
3485
3810
  function useComposerAutocomplete(options) {
3486
3811
  const { value, sources, acceptOnTab = true } = options;
3487
- const latest = useRef8(options);
3812
+ const latest = useRef9(options);
3488
3813
  latest.current = options;
3489
- const inputRef = useRef8(null);
3490
- const [caret, setCaret] = useState9(value.length);
3491
- const [items, setItems] = useState9([]);
3492
- const [isLoading, setLoading] = useState9(false);
3493
- const [error, setError] = useState9(null);
3494
- const [activeIndex, setActiveIndex] = useState9(0);
3495
- const [dismissedIndex, setDismissedIndex] = useState9(null);
3814
+ const inputRef = useRef9(null);
3815
+ const [caret, setCaret] = useState10(value.length);
3816
+ const [items, setItems] = useState10([]);
3817
+ const [isLoading, setLoading] = useState10(false);
3818
+ const [error, setError] = useState10(null);
3819
+ const [activeIndex, setActiveIndex] = useState10(0);
3820
+ const [dismissedIndex, setDismissedIndex] = useState10(null);
3496
3821
  const safeCaret = Math.min(caret, value.length);
3497
- const match = useMemo5(() => findActiveTrigger(value, safeCaret, sources), [
3822
+ const match = useMemo6(() => findActiveTrigger(value, safeCaret, sources), [
3498
3823
  value,
3499
3824
  safeCaret,
3500
3825
  sources
3501
3826
  ]);
3502
3827
  const isActive = match !== null && dismissedIndex !== match.index;
3503
- const matchRef = useRef8(match);
3828
+ const matchRef = useRef9(match);
3504
3829
  matchRef.current = match;
3505
- const caretRef = useRef8(safeCaret);
3830
+ const caretRef = useRef9(safeCaret);
3506
3831
  caretRef.current = safeCaret;
3507
- const itemsRef = useRef8(items);
3832
+ const itemsRef = useRef9(items);
3508
3833
  itemsRef.current = items;
3509
- const activeIndexRef = useRef8(activeIndex);
3834
+ const activeIndexRef = useRef9(activeIndex);
3510
3835
  activeIndexRef.current = activeIndex;
3511
3836
  const requestKey = isActive && match ? `${match.source.id}\0${match.query}` : null;
3512
- useEffect9(() => {
3837
+ useEffect10(() => {
3513
3838
  const current = matchRef.current;
3514
3839
  if (requestKey === null || current === null) {
3515
3840
  setItems([]);
@@ -3559,23 +3884,23 @@ function useComposerAutocomplete(options) {
3559
3884
  const activeItem = isOpen ? items[activeIndex] ?? null : null;
3560
3885
  const reactId = useId();
3561
3886
  const listboxId = options.id ?? `agent-autocomplete-${reactId}`;
3562
- const optionId = useCallback7((index) => `${listboxId}-option-${index}`, [
3887
+ const optionId = useCallback8((index) => `${listboxId}-option-${index}`, [
3563
3888
  listboxId
3564
3889
  ]);
3565
- const highlight = useCallback7((index) => {
3890
+ const highlight = useCallback8((index) => {
3566
3891
  const total = itemsRef.current.length;
3567
3892
  if (total === 0) return;
3568
3893
  setActiveIndex((index % total + total) % total);
3569
3894
  }, []);
3570
- const moveHighlight = useCallback7((delta) => highlight(activeIndexRef.current + delta), [
3895
+ const moveHighlight = useCallback8((delta) => highlight(activeIndexRef.current + delta), [
3571
3896
  highlight
3572
3897
  ]);
3573
- const dismiss = useCallback7(() => {
3898
+ const dismiss = useCallback8(() => {
3574
3899
  const current = matchRef.current;
3575
3900
  if (current) setDismissedIndex(current.index);
3576
3901
  }, []);
3577
- const pendingCaret = useRef8(null);
3578
- const accept = useCallback7((item) => {
3902
+ const pendingCaret = useRef9(null);
3903
+ const accept = useCallback8((item) => {
3579
3904
  const current = matchRef.current;
3580
3905
  if (!current) return;
3581
3906
  const chosen = item ?? itemsRef.current[activeIndexRef.current];
@@ -3592,10 +3917,10 @@ function useComposerAutocomplete(options) {
3592
3917
  pendingCaret.current = null;
3593
3918
  inputRef.current?.setSelectionRange(position, position);
3594
3919
  });
3595
- const readCaret = useCallback7((element) => {
3920
+ const readCaret = useCallback8((element) => {
3596
3921
  setCaret(element.selectionStart ?? element.value.length);
3597
3922
  }, []);
3598
- const onKeyDown = useCallback7((event) => {
3923
+ const onKeyDown = useCallback8((event) => {
3599
3924
  if (!isOpen) return;
3600
3925
  const hasItems = itemsRef.current.length > 0;
3601
3926
  switch (event.key) {
@@ -3633,23 +3958,23 @@ function useComposerAutocomplete(options) {
3633
3958
  dismiss,
3634
3959
  moveHighlight
3635
3960
  ]);
3636
- const onChange = useCallback7((event) => {
3961
+ const onChange = useCallback8((event) => {
3637
3962
  readCaret(event.target);
3638
3963
  const next = findActiveTrigger(event.target.value, event.target.selectionStart ?? event.target.value.length, latest.current.sources);
3639
3964
  setDismissedIndex((current) => current !== null && next?.index === current ? current : null);
3640
3965
  }, [
3641
3966
  readCaret
3642
3967
  ]);
3643
- const onSelect = useCallback7((event) => readCaret(event.currentTarget), [
3968
+ const onSelect = useCallback8((event) => readCaret(event.currentTarget), [
3644
3969
  readCaret
3645
3970
  ]);
3646
- const onClick = useCallback7((event) => readCaret(event.currentTarget), [
3971
+ const onClick = useCallback8((event) => readCaret(event.currentTarget), [
3647
3972
  readCaret
3648
3973
  ]);
3649
- const setInput = useCallback7((element) => {
3974
+ const setInput = useCallback8((element) => {
3650
3975
  inputRef.current = element;
3651
3976
  }, []);
3652
- const getInputProps = useCallback7(() => ({
3977
+ const getInputProps = useCallback8(() => ({
3653
3978
  ref: setInput,
3654
3979
  onChange,
3655
3980
  onKeyDown,
@@ -3677,7 +4002,7 @@ function useComposerAutocomplete(options) {
3677
4002
  activeIndex,
3678
4003
  optionId
3679
4004
  ]);
3680
- const getListboxProps = useCallback7(() => ({
4005
+ const getListboxProps = useCallback8(() => ({
3681
4006
  id: listboxId,
3682
4007
  role: "listbox",
3683
4008
  "aria-label": match?.source.label ?? match?.source.id ?? "Suggestions"
@@ -3685,7 +4010,7 @@ function useComposerAutocomplete(options) {
3685
4010
  listboxId,
3686
4011
  match
3687
4012
  ]);
3688
- const getOptionProps = useCallback7((index) => ({
4013
+ const getOptionProps = useCallback8((index) => ({
3689
4014
  id: optionId(index),
3690
4015
  role: "option",
3691
4016
  "aria-selected": index === activeIndex
@@ -3719,7 +4044,7 @@ function isPromise(value) {
3719
4044
  __name(isPromise, "isPromise");
3720
4045
 
3721
4046
  // src/presentation/use-tool-catalog.ts
3722
- import { useCallback as useCallback8, useEffect as useEffect10, useMemo as useMemo6, useState as useState10 } from "react";
4047
+ import { useCallback as useCallback9, useEffect as useEffect11, useMemo as useMemo7, useState as useState11 } from "react";
3723
4048
  var shared = /* @__PURE__ */ new WeakMap();
3724
4049
  function load(client, agent) {
3725
4050
  let byAgent = shared.get(client);
@@ -3743,11 +4068,11 @@ function load(client, agent) {
3743
4068
  __name(load, "load");
3744
4069
  function useToolCatalog(options) {
3745
4070
  const { client, agent, enabled = true } = options;
3746
- const [entries, setEntries] = useState10([]);
3747
- const [isLoading, setLoading] = useState10(enabled);
3748
- const [error, setError] = useState10(null);
3749
- const [generation, setGeneration] = useState10(0);
3750
- useEffect10(() => {
4071
+ const [entries, setEntries] = useState11([]);
4072
+ const [isLoading, setLoading] = useState11(enabled);
4073
+ const [error, setError] = useState11(null);
4074
+ const [generation, setGeneration] = useState11(0);
4075
+ useEffect11(() => {
3751
4076
  if (!enabled) {
3752
4077
  setLoading(false);
3753
4078
  return;
@@ -3773,14 +4098,14 @@ function useToolCatalog(options) {
3773
4098
  enabled,
3774
4099
  generation
3775
4100
  ]);
3776
- const refresh = useCallback8(() => {
4101
+ const refresh = useCallback9(() => {
3777
4102
  shared.get(client)?.delete(agent ?? "");
3778
4103
  setGeneration((current) => current + 1);
3779
4104
  }, [
3780
4105
  client,
3781
4106
  agent
3782
4107
  ]);
3783
- const catalog = useMemo6(() => toolCatalogFrom(entries), [
4108
+ const catalog = useMemo7(() => toolCatalogFrom(entries), [
3784
4109
  entries
3785
4110
  ]);
3786
4111
  return {
@@ -3794,7 +4119,7 @@ function useToolCatalog(options) {
3794
4119
  __name(useToolCatalog, "useToolCatalog");
3795
4120
 
3796
4121
  // src/approvals/countdown.ts
3797
- import { useEffect as useEffect11, useRef as useRef9, useState as useState11 } from "react";
4122
+ import { useEffect as useEffect12, useRef as useRef10, useState as useState12 } from "react";
3798
4123
  function approvalCountdown(expiresAt, now = Date.now()) {
3799
4124
  const deadline = expiresAt == null ? Number.NaN : Date.parse(expiresAt);
3800
4125
  if (!Number.isFinite(deadline)) {
@@ -3812,10 +4137,10 @@ function approvalCountdown(expiresAt, now = Date.now()) {
3812
4137
  __name(approvalCountdown, "approvalCountdown");
3813
4138
  function useApprovalCountdown(expiresAt, options = {}) {
3814
4139
  const { intervalMs = 1e3 } = options;
3815
- const nowRef = useRef9(options.now ?? Date.now);
4140
+ const nowRef = useRef10(options.now ?? Date.now);
3816
4141
  nowRef.current = options.now ?? Date.now;
3817
- const [state, setState] = useState11(() => approvalCountdown(expiresAt, nowRef.current()));
3818
- useEffect11(() => {
4142
+ const [state, setState] = useState12(() => approvalCountdown(expiresAt, nowRef.current()));
4143
+ useEffect12(() => {
3819
4144
  const read = /* @__PURE__ */ __name(() => approvalCountdown(expiresAt, nowRef.current()), "read");
3820
4145
  const first = read();
3821
4146
  setState(first);
@@ -3878,7 +4203,13 @@ function storedMessageToUiMessage(message) {
3878
4203
  type: "file",
3879
4204
  mediaType: attachment.contentType,
3880
4205
  filename: attachment.name,
3881
- url: attachment.url
4206
+ url: attachment.url,
4207
+ // The stored id rides along, so a replayed file can be referenced again (`messageFiles`).
4208
+ providerMetadata: {
4209
+ agent: {
4210
+ mediaId: attachment.mediaId
4211
+ }
4212
+ }
3882
4213
  });
3883
4214
  }
3884
4215
  const toolCallIds = new Set((message.toolCalls ?? []).map((call) => call.id));
@@ -4057,20 +4388,20 @@ __name(sumUsage, "sumUsage");
4057
4388
 
4058
4389
  // src/use-agent-chat.ts
4059
4390
  import { useChat } from "@ai-sdk/react";
4060
- import { useCallback as useCallback9, useEffect as useEffect12, useMemo as useMemo7, useRef as useRef10, useState as useState12 } from "react";
4391
+ import { useCallback as useCallback10, useEffect as useEffect13, useMemo as useMemo8, useRef as useRef11, useState as useState13 } from "react";
4061
4392
  function useAgentChat(options) {
4062
- const latest = useRef10(options);
4393
+ const latest = useRef11(options);
4063
4394
  latest.current = options;
4064
- const [runId, setRunId] = useState12(options.resumeRunId);
4065
- const runIdRef = useRef10(runId);
4395
+ const [runId, setRunId] = useState13(options.resumeRunId);
4396
+ const runIdRef = useRef11(runId);
4066
4397
  runIdRef.current = runId;
4067
- const settlingRunIdRef = useRef10(void 0);
4068
- const autoResumeRunIdRef = useRef10(void 0);
4069
- const [autoResume, setAutoResume] = useState12(false);
4070
- const [activeRunId, setActiveRunId] = useState12(null);
4071
- const regenerateNext = useRef10(false);
4072
- const createdThreadId = useRef10(void 0);
4073
- const client = useMemo7(() => {
4398
+ const settlingRunIdRef = useRef11(void 0);
4399
+ const autoResumeRunIdRef = useRef11(void 0);
4400
+ const [autoResume, setAutoResume] = useState13(false);
4401
+ const [activeRunId, setActiveRunId] = useState13(null);
4402
+ const regenerateNext = useRef11(false);
4403
+ const createdThreadId = useRef11(void 0);
4404
+ const client = useMemo8(() => {
4074
4405
  if (options.backend) return options.backend;
4075
4406
  if (options.client) return options.client;
4076
4407
  return new AgentClient({
@@ -4086,11 +4417,11 @@ function useAgentChat(options) {
4086
4417
  getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders")
4087
4418
  });
4088
4419
  }, []);
4089
- const [connection, setConnection] = useState12({
4420
+ const [connection, setConnection] = useState13({
4090
4421
  status: "live"
4091
4422
  });
4092
- const resyncAfterSettle = useRef10(false);
4093
- useEffect12(() => {
4423
+ const resyncAfterSettle = useRef11(false);
4424
+ useEffect13(() => {
4094
4425
  if (!options.resume || options.threadId === void 0) return;
4095
4426
  let cancelled = false;
4096
4427
  const threadId = options.threadId;
@@ -4110,7 +4441,7 @@ function useAgentChat(options) {
4110
4441
  options.resume,
4111
4442
  options.threadId
4112
4443
  ]);
4113
- const transport = useMemo7(() => {
4444
+ const transport = useMemo8(() => {
4114
4445
  function onMeta(meta) {
4115
4446
  settlingRunIdRef.current = meta.runId;
4116
4447
  setRunId(meta.runId);
@@ -4165,7 +4496,7 @@ function useAgentChat(options) {
4165
4496
  }, "onAttemptStart")
4166
4497
  });
4167
4498
  }, []);
4168
- const backgroundRef = useRef10(() => Promise.resolve());
4499
+ const backgroundRef = useRef11(() => Promise.resolve());
4169
4500
  const chat = useChat({
4170
4501
  transport,
4171
4502
  resume: options.resumeRunId !== void 0 || autoResume,
@@ -4214,7 +4545,7 @@ function useAgentChat(options) {
4214
4545
  }
4215
4546
  }, "onFinish")
4216
4547
  });
4217
- const chatRef = useRef10(chat);
4548
+ const chatRef = useRef11(chat);
4218
4549
  chatRef.current = chat;
4219
4550
  async function resyncFromThread() {
4220
4551
  const threadId = latest.current.threadId ?? createdThreadId.current;
@@ -4226,11 +4557,11 @@ function useAgentChat(options) {
4226
4557
  }
4227
4558
  }
4228
4559
  __name(resyncFromThread, "resyncFromThread");
4229
- const turnInFlight = useRef10(false);
4230
- const isTurnInFlight = useCallback9(() => turnInFlight.current || transport.isAttemptLive, [
4560
+ const turnInFlight = useRef11(false);
4561
+ const isTurnInFlight = useCallback10(() => turnInFlight.current || transport.isAttemptLive, [
4231
4562
  transport
4232
4563
  ]);
4233
- const sendMessage = useCallback9(async (...args) => {
4564
+ const sendMessage = useCallback10(async (...args) => {
4234
4565
  const blocked = latest.current.blocked;
4235
4566
  if (blocked != null) throw new QuotaBlockedError(blocked);
4236
4567
  if (isTurnInFlight()) return;
@@ -4243,15 +4574,15 @@ function useAgentChat(options) {
4243
4574
  }, [
4244
4575
  isTurnInFlight
4245
4576
  ]);
4246
- const addToolResult = useCallback9(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
4577
+ const addToolResult = useCallback10(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
4247
4578
  tool,
4248
4579
  toolCallId,
4249
4580
  output
4250
4581
  }), []);
4251
- const [threads, setThreads] = useState12([]);
4252
- const [quota, setQuota] = useState12(null);
4253
- const [backgroundRuns, setBackgroundRuns] = useState12([]);
4254
- const refreshBackground = useCallback9(async () => {
4582
+ const [threads, setThreads] = useState13([]);
4583
+ const [quota, setQuota] = useState13(null);
4584
+ const [backgroundRuns, setBackgroundRuns] = useState13([]);
4585
+ const refreshBackground = useCallback10(async () => {
4255
4586
  const threadId = latest.current.threadId ?? createdThreadId.current;
4256
4587
  if (threadId === void 0) {
4257
4588
  return;
@@ -4276,7 +4607,7 @@ function useAgentChat(options) {
4276
4607
  ]);
4277
4608
  backgroundRef.current = refreshBackground;
4278
4609
  const isWorking = backgroundRuns.some((run) => run.status === "running");
4279
- useEffect12(() => {
4610
+ useEffect13(() => {
4280
4611
  if (options.background !== true || options.threadId === void 0) {
4281
4612
  return;
4282
4613
  }
@@ -4286,7 +4617,7 @@ function useAgentChat(options) {
4286
4617
  options.threadId,
4287
4618
  refreshBackground
4288
4619
  ]);
4289
- useEffect12(() => {
4620
+ useEffect13(() => {
4290
4621
  const every = latest.current.backgroundPollMs ?? 5e3;
4291
4622
  if (latest.current.background !== true || !isWorking || every <= 0) {
4292
4623
  return;
@@ -4297,26 +4628,26 @@ function useAgentChat(options) {
4297
4628
  isWorking,
4298
4629
  refreshBackground
4299
4630
  ]);
4300
- const loadThreads = useCallback9(async () => {
4631
+ const loadThreads = useCallback10(async () => {
4301
4632
  const list = await client.listThreads();
4302
4633
  setThreads(list);
4303
4634
  return list;
4304
4635
  }, [
4305
4636
  client
4306
4637
  ]);
4307
- const loadThread = useCallback9((id) => client.getThread(id), [
4638
+ const loadThread = useCallback10((id) => client.getThread(id), [
4308
4639
  client
4309
4640
  ]);
4310
- const deleteThread = useCallback9(async (id) => {
4641
+ const deleteThread = useCallback10(async (id) => {
4311
4642
  await client.deleteThread(id);
4312
4643
  setThreads((current) => current.filter((thread) => thread.id !== id));
4313
4644
  }, [
4314
4645
  client
4315
4646
  ]);
4316
- const forkThread = useCallback9((threadId, messageId) => requireBackendMethod(client, "forkFromMessage")(threadId, messageId), [
4647
+ const forkThread = useCallback10((threadId, messageId) => requireBackendMethod(client, "forkFromMessage")(threadId, messageId), [
4317
4648
  client
4318
4649
  ]);
4319
- const renameThread = useCallback9(async (id, title) => {
4650
+ const renameThread = useCallback10(async (id, title) => {
4320
4651
  await client.updateThread(id, {
4321
4652
  title
4322
4653
  });
@@ -4327,24 +4658,24 @@ function useAgentChat(options) {
4327
4658
  }, [
4328
4659
  client
4329
4660
  ]);
4330
- const promoteThread = useCallback9(async (id) => {
4661
+ const promoteThread = useCallback10(async (id) => {
4331
4662
  await requireBackendMethod(client, "promoteThread")(id);
4332
4663
  await loadThreads();
4333
4664
  }, [
4334
4665
  client,
4335
4666
  loadThreads
4336
4667
  ]);
4337
- const truncateFromMessage = useCallback9((threadId, messageId) => requireBackendMethod(client, "truncateFromMessage")(threadId, messageId).then(() => void 0), [
4668
+ const truncateFromMessage = useCallback10((threadId, messageId) => requireBackendMethod(client, "truncateFromMessage")(threadId, messageId).then(() => void 0), [
4338
4669
  client
4339
4670
  ]);
4340
- const loadQuota = useCallback9(async () => {
4671
+ const loadQuota = useCallback10(async () => {
4341
4672
  const today = await requireBackendMethod(client, "getQuotaToday")();
4342
4673
  setQuota(today);
4343
4674
  return today;
4344
4675
  }, [
4345
4676
  client
4346
4677
  ]);
4347
- const cancel = useCallback9(async () => {
4678
+ const cancel = useCallback10(async () => {
4348
4679
  chatRef.current.stop();
4349
4680
  const activeRunId2 = runIdRef.current;
4350
4681
  if (activeRunId2) {
@@ -4356,7 +4687,7 @@ function useAgentChat(options) {
4356
4687
  }, [
4357
4688
  client
4358
4689
  ]);
4359
- const approve = useCallback9(async ({ toolCallId, remember, via }) => {
4690
+ const approve = useCallback10(async ({ toolCallId, remember, via }) => {
4360
4691
  await requireBackendMethod(client, "approveToolCall")({
4361
4692
  toolCallId,
4362
4693
  ...remember === true ? {
@@ -4369,7 +4700,7 @@ function useAgentChat(options) {
4369
4700
  }, [
4370
4701
  client
4371
4702
  ]);
4372
- const reject = useCallback9(async ({ toolCallId, reason, via }) => {
4703
+ const reject = useCallback10(async ({ toolCallId, reason, via }) => {
4373
4704
  await requireBackendMethod(client, "rejectToolCall")({
4374
4705
  toolCallId,
4375
4706
  ...reason !== void 0 ? {
@@ -4382,7 +4713,7 @@ function useAgentChat(options) {
4382
4713
  }, [
4383
4714
  client
4384
4715
  ]);
4385
- const answer = useCallback9(async ({ toolCallId, answers }) => {
4716
+ const answer = useCallback10(async ({ toolCallId, answers }) => {
4386
4717
  await requireBackendMethod(client, "answerToolCall")({
4387
4718
  toolCallId,
4388
4719
  ...answers !== void 0 ? {
@@ -4392,14 +4723,14 @@ function useAgentChat(options) {
4392
4723
  }, [
4393
4724
  client
4394
4725
  ]);
4395
- const skip = useCallback9(async ({ toolCallId }) => {
4726
+ const skip = useCallback10(async ({ toolCallId }) => {
4396
4727
  await requireBackendMethod(client, "skipToolCall")({
4397
4728
  toolCallId
4398
4729
  });
4399
4730
  }, [
4400
4731
  client
4401
4732
  ]);
4402
- const regenerate = useCallback9(() => {
4733
+ const regenerate = useCallback10(() => {
4403
4734
  const blocked = latest.current.blocked;
4404
4735
  if (blocked != null) throw new QuotaBlockedError(blocked);
4405
4736
  if (isTurnInFlight()) return;
@@ -4416,7 +4747,7 @@ function useAgentChat(options) {
4416
4747
  isWorking,
4417
4748
  refresh: refreshBackground
4418
4749
  };
4419
- const setThreadModel = useCallback9(async (model) => {
4750
+ const setThreadModel = useCallback10(async (model) => {
4420
4751
  const threadId = latest.current.threadId ?? createdThreadId.current;
4421
4752
  if (threadId === void 0) {
4422
4753
  throw new Error("setThreadModel: this chat has no thread yet");
@@ -4430,7 +4761,7 @@ function useAgentChat(options) {
4430
4761
  }, [
4431
4762
  client
4432
4763
  ]);
4433
- const getThreadId = useCallback9(() => latest.current.threadId ?? createdThreadId.current, []);
4764
+ const getThreadId = useCallback10(() => latest.current.threadId ?? createdThreadId.current, []);
4434
4765
  const status = connection.status === "reconnecting" ? "reconnecting" : chat.status;
4435
4766
  return {
4436
4767
  ...chat,
@@ -4503,6 +4834,7 @@ export {
4503
4834
  MessageList,
4504
4835
  QuotaBlockedError,
4505
4836
  SPEECH_LANGUAGES,
4837
+ acceptsFile,
4506
4838
  applyCompletion,
4507
4839
  approvalCountdown,
4508
4840
  backgroundRunsFromThread,
@@ -4513,7 +4845,10 @@ export {
4513
4845
  describeTimestamp,
4514
4846
  describeToolCall,
4515
4847
  describeUsage,
4848
+ dragHasFiles,
4516
4849
  extractMessageText,
4850
+ fileKind,
4851
+ filesFromClipboard,
4517
4852
  fillTemplate,
4518
4853
  filterAutocompleteItems,
4519
4854
  findActiveTrigger,
@@ -4523,6 +4858,7 @@ export {
4523
4858
  inferResultView,
4524
4859
  isActionCall,
4525
4860
  loadStoredSpeechLang,
4861
+ messageFiles,
4526
4862
  notifyThreads,
4527
4863
  onThreadsEvent,
4528
4864
  persistSpeechLang,
@@ -4538,6 +4874,7 @@ export {
4538
4874
  useAgentChat,
4539
4875
  useAgents,
4540
4876
  useApprovalCountdown,
4877
+ useAttachments,
4541
4878
  useChatTranscript,
4542
4879
  useComposerAutocomplete,
4543
4880
  useElapsed,