@dudousxd/nestjs-agent-react 0.16.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`);
@@ -156,6 +199,10 @@ var AgentClient = class {
156
199
  listAgents() {
157
200
  return this.request("GET", "/agent/agents");
158
201
  }
202
+ /** `GET /agent/quota` — the caller's budget windows and the one blocking sends, if any. */
203
+ getQuota() {
204
+ return this.request("GET", "/agent/quota");
205
+ }
159
206
  getQuotaToday() {
160
207
  return this.request("GET", "/agent/quota/today");
161
208
  }
@@ -983,6 +1030,68 @@ function delay(ms, signal) {
983
1030
  }
984
1031
  __name(delay, "delay");
985
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
+
986
1095
  // src/backend.ts
987
1096
  var AgentBackendUnsupportedError = class extends Error {
988
1097
  static {
@@ -1003,19 +1112,239 @@ function requireBackendMethod(backend, name) {
1003
1112
  }
1004
1113
  __name(requireBackendMethod, "requireBackendMethod");
1005
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
+
1006
1335
  // src/catalog/use-agents.ts
1007
- import { useMemo } from "react";
1336
+ import { useMemo as useMemo2 } from "react";
1008
1337
 
1009
1338
  // src/catalog/use-resource.ts
1010
- 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";
1011
1340
  function useResource(load2, key, enabled) {
1012
- const [data, setData] = useState2(void 0);
1013
- const [isLoading, setIsLoading] = useState2(enabled);
1014
- const [error, setError] = useState2(null);
1015
- 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);
1016
1345
  loadRef.current = load2;
1017
- const generation = useRef2(0);
1018
- const refresh = useCallback(async () => {
1346
+ const generation = useRef3(0);
1347
+ const refresh = useCallback2(async () => {
1019
1348
  const mine = ++generation.current;
1020
1349
  setIsLoading(true);
1021
1350
  try {
@@ -1030,7 +1359,7 @@ function useResource(load2, key, enabled) {
1030
1359
  if (mine === generation.current) setIsLoading(false);
1031
1360
  }
1032
1361
  }, []);
1033
- useEffect2(() => {
1362
+ useEffect3(() => {
1034
1363
  if (!enabled) return;
1035
1364
  void refresh();
1036
1365
  }, [
@@ -1051,7 +1380,7 @@ __name(useResource, "useResource");
1051
1380
  function useAgents(options) {
1052
1381
  const { backend, enabled = true } = options;
1053
1382
  const { data, isLoading, error, refresh } = useResource(() => requireBackendMethod(backend, "listAgents")(), "", enabled);
1054
- return useMemo(() => {
1383
+ return useMemo2(() => {
1055
1384
  const agents = data ?? [];
1056
1385
  return {
1057
1386
  agents,
@@ -1070,11 +1399,11 @@ function useAgents(options) {
1070
1399
  __name(useAgents, "useAgents");
1071
1400
 
1072
1401
  // src/catalog/use-models.ts
1073
- import { useMemo as useMemo2 } from "react";
1402
+ import { useMemo as useMemo3 } from "react";
1074
1403
  function useModels(options) {
1075
1404
  const { backend, agent, enabled = true } = options;
1076
1405
  const { data, isLoading, error, refresh } = useResource(() => requireBackendMethod(backend, "listModels")(agent), `${agent ?? ""}`, enabled);
1077
- return useMemo2(() => {
1406
+ return useMemo3(() => {
1078
1407
  const providers = data?.providers ?? [];
1079
1408
  const models = providers.flatMap((provider) => provider.models.map((model) => ({
1080
1409
  ...model,
@@ -1103,28 +1432,96 @@ function useModels(options) {
1103
1432
  }
1104
1433
  __name(useModels, "useModels");
1105
1434
 
1435
+ // src/quota/use-quota.ts
1436
+ import { useEffect as useEffect4, useMemo as useMemo4 } from "react";
1437
+
1438
+ // src/threads/threads-events.ts
1439
+ var listeners = /* @__PURE__ */ new WeakMap();
1440
+ function notifyThreads(backend, event) {
1441
+ for (const listener of listeners.get(backend) ?? []) {
1442
+ listener(event);
1443
+ }
1444
+ }
1445
+ __name(notifyThreads, "notifyThreads");
1446
+ function onThreadsEvent(backend, listener) {
1447
+ let set = listeners.get(backend);
1448
+ if (set === void 0) {
1449
+ set = /* @__PURE__ */ new Set();
1450
+ listeners.set(backend, set);
1451
+ }
1452
+ set.add(listener);
1453
+ return () => {
1454
+ set.delete(listener);
1455
+ };
1456
+ }
1457
+ __name(onThreadsEvent, "onThreadsEvent");
1458
+
1459
+ // src/quota/use-quota.ts
1460
+ function useQuota(options) {
1461
+ const { backend, enabled = true, pollMs } = options;
1462
+ const { data, isLoading, error, refresh } = useResource(() => requireBackendMethod(backend, "getQuota")(), "", enabled);
1463
+ useEffect4(() => {
1464
+ if (!enabled) return;
1465
+ return onThreadsEvent(backend, (event) => {
1466
+ if (event.type === "changed") void refresh();
1467
+ });
1468
+ }, [
1469
+ backend,
1470
+ enabled,
1471
+ refresh
1472
+ ]);
1473
+ useEffect4(() => {
1474
+ if (!enabled || pollMs === void 0 || pollMs <= 0) return;
1475
+ const timer = setInterval(() => void refresh(), pollMs);
1476
+ return () => clearInterval(timer);
1477
+ }, [
1478
+ enabled,
1479
+ pollMs,
1480
+ refresh
1481
+ ]);
1482
+ return useMemo4(() => {
1483
+ const windows = data?.windows ?? [];
1484
+ return {
1485
+ report: data,
1486
+ windows,
1487
+ day: windows.find((window2) => window2.period === "day"),
1488
+ month: windows.find((window2) => window2.period === "month"),
1489
+ blocked: data?.blocked ?? null,
1490
+ isLoading,
1491
+ error,
1492
+ refresh
1493
+ };
1494
+ }, [
1495
+ data,
1496
+ isLoading,
1497
+ error,
1498
+ refresh
1499
+ ]);
1500
+ }
1501
+ __name(useQuota, "useQuota");
1502
+
1106
1503
  // src/feedback/use-message-feedback.ts
1107
- 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";
1108
1505
  function useMessageFeedback(options) {
1109
- const latest = useRef3(options);
1506
+ const latest = useRef4(options);
1110
1507
  latest.current = options;
1111
- const [local, setLocal] = useState3(/* @__PURE__ */ new Map());
1112
- const [pending, setPending] = useState3(/* @__PURE__ */ new Set());
1113
- const [error, setError] = useState3(null);
1114
- 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);
1115
1512
  localRef.current = local;
1116
- const resolved = useRef3(/* @__PURE__ */ new Map());
1117
- const feedbackOf = useCallback2((message) => {
1513
+ const resolved = useRef4(/* @__PURE__ */ new Map());
1514
+ const feedbackOf = useCallback3((message) => {
1118
1515
  const mine = local.get(message.id);
1119
1516
  if (mine !== void 0) return mine.feedback;
1120
1517
  return metadataOf(message).feedback ?? null;
1121
1518
  }, [
1122
1519
  local
1123
1520
  ]);
1124
- const isPending = useCallback2((message) => pending.has(message.id), [
1521
+ const isPending = useCallback3((message) => pending.has(message.id), [
1125
1522
  pending
1126
1523
  ]);
1127
- const storedIdFor = useCallback2(async (message) => {
1524
+ const storedIdFor = useCallback3(async (message) => {
1128
1525
  const runId = metadataOf(message).runId;
1129
1526
  if (runId === void 0) return message.id;
1130
1527
  const known = resolved.current.get(message.id);
@@ -1144,7 +1541,7 @@ function useMessageFeedback(options) {
1144
1541
  resolved.current.set(message.id, row.id);
1145
1542
  return row.id;
1146
1543
  }, []);
1147
- const rate = useCallback2(async (message, value, comment) => {
1544
+ const rate = useCallback3(async (message, value, comment) => {
1148
1545
  const setFeedback = requireBackendMethod(latest.current.backend, "setMessageFeedback");
1149
1546
  const had = localRef.current.get(message.id);
1150
1547
  const optimistic = value === null ? null : {
@@ -1190,7 +1587,7 @@ function useMessageFeedback(options) {
1190
1587
  }, [
1191
1588
  storedIdFor
1192
1589
  ]);
1193
- const toggle = useCallback2((message, value) => {
1590
+ const toggle = useCallback3((message, value) => {
1194
1591
  const mine = localRef.current.get(message.id);
1195
1592
  const current = mine !== void 0 ? mine.feedback : metadataOf(message).feedback ?? null;
1196
1593
  return rate(message, current?.value === value ? null : value);
@@ -1212,38 +1609,17 @@ function metadataOf(message) {
1212
1609
  }
1213
1610
  __name(metadataOf, "metadataOf");
1214
1611
 
1215
- // src/threads/threads-events.ts
1216
- var listeners = /* @__PURE__ */ new WeakMap();
1217
- function notifyThreads(backend, event) {
1218
- for (const listener of listeners.get(backend) ?? []) {
1219
- listener(event);
1220
- }
1221
- }
1222
- __name(notifyThreads, "notifyThreads");
1223
- function onThreadsEvent(backend, listener) {
1224
- let set = listeners.get(backend);
1225
- if (set === void 0) {
1226
- set = /* @__PURE__ */ new Set();
1227
- listeners.set(backend, set);
1228
- }
1229
- set.add(listener);
1230
- return () => {
1231
- set.delete(listener);
1232
- };
1233
- }
1234
- __name(onThreadsEvent, "onThreadsEvent");
1235
-
1236
1612
  // src/threads/use-threads.ts
1237
- import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef4, useState as useState4 } from "react";
1613
+ import { useCallback as useCallback4, useEffect as useEffect5, useRef as useRef5, useState as useState5 } from "react";
1238
1614
  function useThreads(options) {
1239
1615
  const { backend, enabled = true } = options;
1240
- const [threads, setThreads] = useState4([]);
1241
- const [isLoading, setIsLoading] = useState4(enabled);
1242
- const [error, setError] = useState4(null);
1243
- 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);
1244
1620
  threadsRef.current = threads;
1245
- const generation = useRef4(0);
1246
- const refresh = useCallback3(async () => {
1621
+ const generation = useRef5(0);
1622
+ const refresh = useCallback4(async () => {
1247
1623
  const mine = ++generation.current;
1248
1624
  setIsLoading(true);
1249
1625
  try {
@@ -1260,7 +1636,7 @@ function useThreads(options) {
1260
1636
  }, [
1261
1637
  backend
1262
1638
  ]);
1263
- useEffect3(() => {
1639
+ useEffect5(() => {
1264
1640
  if (!enabled) return;
1265
1641
  void refresh();
1266
1642
  return onThreadsEvent(backend, (event) => {
@@ -1280,7 +1656,7 @@ function useThreads(options) {
1280
1656
  enabled,
1281
1657
  refresh
1282
1658
  ]);
1283
- const rename = useCallback3(async (id, title) => {
1659
+ const rename = useCallback4(async (id, title) => {
1284
1660
  const before = threadsRef.current;
1285
1661
  setThreads((current) => current.map((thread) => thread.id === id ? {
1286
1662
  ...thread,
@@ -1302,7 +1678,7 @@ function useThreads(options) {
1302
1678
  }, [
1303
1679
  backend
1304
1680
  ]);
1305
- const remove = useCallback3(async (id) => {
1681
+ const remove = useCallback4(async (id) => {
1306
1682
  const before = threadsRef.current;
1307
1683
  setThreads((current) => current.filter((thread) => thread.id !== id));
1308
1684
  try {
@@ -1377,10 +1753,10 @@ function backgroundRunsFromThread(messages) {
1377
1753
  __name(backgroundRunsFromThread, "backgroundRunsFromThread");
1378
1754
 
1379
1755
  // src/components/chat-input.tsx
1380
- import React2, { useEffect as useEffect5, useState as useState6 } from "react";
1756
+ import React2, { useEffect as useEffect7, useState as useState7 } from "react";
1381
1757
 
1382
1758
  // src/components/use-speech-recognition.ts
1383
- import { useCallback as useCallback4, useEffect as useEffect4, useMemo as useMemo3, 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";
1384
1760
  function getRecognitionCtor() {
1385
1761
  if (typeof window === "undefined") return null;
1386
1762
  const scope = window;
@@ -1388,15 +1764,15 @@ function getRecognitionCtor() {
1388
1764
  }
1389
1765
  __name(getRecognitionCtor, "getRecognitionCtor");
1390
1766
  function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTranscript }) {
1391
- const recognitionRef = useRef5(null);
1392
- const [isListening, setIsListening] = useState5(false);
1393
- const ctor = useMemo3(() => getRecognitionCtor(), []);
1767
+ const recognitionRef = useRef6(null);
1768
+ const [isListening, setIsListening] = useState6(false);
1769
+ const ctor = useMemo5(() => getRecognitionCtor(), []);
1394
1770
  const isSupported = ctor !== null;
1395
- const finalRef = useRef5(onFinalTranscript);
1396
- const interimRef = useRef5(onInterimTranscript);
1771
+ const finalRef = useRef6(onFinalTranscript);
1772
+ const interimRef = useRef6(onInterimTranscript);
1397
1773
  finalRef.current = onFinalTranscript;
1398
1774
  interimRef.current = onInterimTranscript;
1399
- useEffect4(() => {
1775
+ useEffect6(() => {
1400
1776
  if (!ctor) return;
1401
1777
  const recognition = new ctor();
1402
1778
  recognition.lang = lang;
@@ -1435,7 +1811,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
1435
1811
  ctor,
1436
1812
  lang
1437
1813
  ]);
1438
- const start = useCallback4(() => {
1814
+ const start = useCallback5(() => {
1439
1815
  const recognition = recognitionRef.current;
1440
1816
  if (!recognition) return;
1441
1817
  try {
@@ -1445,7 +1821,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
1445
1821
  console.warn("SpeechRecognition start failed", error);
1446
1822
  }
1447
1823
  }, []);
1448
- const stop = useCallback4(() => {
1824
+ const stop = useCallback5(() => {
1449
1825
  const recognition = recognitionRef.current;
1450
1826
  if (!recognition) return;
1451
1827
  try {
@@ -1464,8 +1840,8 @@ __name(useSpeechRecognition, "useSpeechRecognition");
1464
1840
 
1465
1841
  // src/components/chat-input.tsx
1466
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 }) {
1467
- const [value, setValue] = useState6("");
1468
- const [interim, setInterim] = useState6("");
1843
+ const [value, setValue] = useState7("");
1844
+ const [interim, setInterim] = useState7("");
1469
1845
  const speech = useSpeechRecognition({
1470
1846
  lang: speechLang,
1471
1847
  onFinalTranscript: /* @__PURE__ */ __name((text) => {
@@ -1476,7 +1852,7 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
1476
1852
  }, "onFinalTranscript"),
1477
1853
  onInterimTranscript: /* @__PURE__ */ __name((text) => setInterim(text), "onInterimTranscript")
1478
1854
  });
1479
- useEffect5(() => {
1855
+ useEffect7(() => {
1480
1856
  if (!speech.isListening) setInterim("");
1481
1857
  }, [
1482
1858
  speech.isListening
@@ -1543,7 +1919,7 @@ __name(ChatInput, "ChatInput");
1543
1919
  import React3 from "react";
1544
1920
 
1545
1921
  // src/transcript/use-chat-transcript.ts
1546
- import { useCallback as useCallback6, useEffect as useEffect7, useRef as useRef7, useState as useState8 } from "react";
1922
+ import { useCallback as useCallback7, useEffect as useEffect9, useRef as useRef8, useState as useState9 } from "react";
1547
1923
 
1548
1924
  // src/transcript/model.ts
1549
1925
  import { readElicitationInput, validateElicitationAnswer as validateElicitationAnswer2 } from "@dudousxd/nestjs-agent-core";
@@ -2520,11 +2896,11 @@ function formatCostUsd(cost) {
2520
2896
  __name(formatCostUsd, "formatCostUsd");
2521
2897
 
2522
2898
  // src/transcript/use-stick-to-bottom.ts
2523
- import { useCallback as useCallback5, useEffect as useEffect6, useRef as useRef6, useState as useState7 } from "react";
2899
+ import { useCallback as useCallback6, useEffect as useEffect8, useRef as useRef7, useState as useState8 } from "react";
2524
2900
  function useStickToBottom({ contentKey, threshold = 32 }) {
2525
- const containerRef = useRef6(null);
2526
- const [isAtBottom, setIsAtBottom] = useState7(true);
2527
- const measure = useCallback5(() => {
2901
+ const containerRef = useRef7(null);
2902
+ const [isAtBottom, setIsAtBottom] = useState8(true);
2903
+ const measure = useCallback6(() => {
2528
2904
  const element = containerRef.current;
2529
2905
  if (!element) {
2530
2906
  return;
@@ -2534,7 +2910,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
2534
2910
  }, [
2535
2911
  threshold
2536
2912
  ]);
2537
- const scrollToBottom = useCallback5(() => {
2913
+ const scrollToBottom = useCallback6(() => {
2538
2914
  const element = containerRef.current;
2539
2915
  if (!element) {
2540
2916
  return;
@@ -2542,7 +2918,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
2542
2918
  element.scrollTop = element.scrollHeight;
2543
2919
  setIsAtBottom(true);
2544
2920
  }, []);
2545
- const setContainer = useCallback5((element) => {
2921
+ const setContainer = useCallback6((element) => {
2546
2922
  containerRef.current = element;
2547
2923
  if (element) {
2548
2924
  measure();
@@ -2550,7 +2926,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
2550
2926
  }, [
2551
2927
  measure
2552
2928
  ]);
2553
- useEffect6(() => {
2929
+ useEffect8(() => {
2554
2930
  if (isAtBottom) {
2555
2931
  scrollToBottom();
2556
2932
  }
@@ -2559,7 +2935,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
2559
2935
  isAtBottom,
2560
2936
  scrollToBottom
2561
2937
  ]);
2562
- const getContainerProps = useCallback5(() => ({
2938
+ const getContainerProps = useCallback6(() => ({
2563
2939
  ref: setContainer,
2564
2940
  onScroll: measure
2565
2941
  }), [
@@ -2581,14 +2957,14 @@ var DEFAULT_LOAD_EARLIER_STEP = 50;
2581
2957
  var DEFAULT_COPY_RESET_MS = 1500;
2582
2958
  function useChatTranscript(options) {
2583
2959
  const { messages, status, initialVisibleCount = DEFAULT_VISIBLE_COUNT, loadEarlierStep = DEFAULT_LOAD_EARLIER_STEP, followUps, stickToBottomThreshold } = options;
2584
- const latest = useRef7(options);
2960
+ const latest = useRef8(options);
2585
2961
  latest.current = options;
2586
- const [visibleCount, setVisibleCount] = useState8(initialVisibleCount);
2587
- const [stopRequested, setStopRequested] = useState8(false);
2962
+ const [visibleCount, setVisibleCount] = useState9(initialVisibleCount);
2963
+ const [stopRequested, setStopRequested] = useState9(false);
2588
2964
  const isStreaming = status === "streaming" || status === "reconnecting";
2589
2965
  const isBusy = status === "submitted" || isStreaming;
2590
2966
  const isEmpty = messages.length === 0;
2591
- useEffect7(() => {
2967
+ useEffect9(() => {
2592
2968
  if (!isBusy) {
2593
2969
  setStopRequested(false);
2594
2970
  }
@@ -2605,7 +2981,7 @@ function useChatTranscript(options) {
2605
2981
  streamingMessageId,
2606
2982
  options
2607
2983
  });
2608
- const stop = useCallback6(() => {
2984
+ const stop = useCallback7(() => {
2609
2985
  const current = latest.current;
2610
2986
  if (current.status !== "submitted" && current.status !== "streaming" && current.status !== "reconnecting") {
2611
2987
  return;
@@ -2613,8 +2989,8 @@ function useChatTranscript(options) {
2613
2989
  setStopRequested(true);
2614
2990
  void current.onStop?.();
2615
2991
  }, []);
2616
- const loadEarlier = useCallback6(() => setVisibleCount((count) => count + (latest.current.loadEarlierStep ?? DEFAULT_LOAD_EARLIER_STEP)), []);
2617
- 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), []);
2618
2994
  const scroll = useStickToBottom({
2619
2995
  contentKey: contentSignature(messages),
2620
2996
  ...stickToBottomThreshold !== void 0 ? {
@@ -2650,7 +3026,7 @@ function useChatTranscript(options) {
2650
3026
  __name(useChatTranscript, "useChatTranscript");
2651
3027
  function useTranscriptItem(options) {
2652
3028
  const { message, isStreaming = false } = options;
2653
- const messagesRef = useRef7([
3029
+ const messagesRef = useRef8([
2654
3030
  message
2655
3031
  ]);
2656
3032
  if (messagesRef.current[0] !== message) {
@@ -2668,13 +3044,13 @@ function useTranscriptItem(options) {
2668
3044
  }
2669
3045
  __name(useTranscriptItem, "useTranscriptItem");
2670
3046
  function useTranscriptItems({ messages, visibleStart, streamingMessageId, options }) {
2671
- const [drafts, setDrafts] = useState8(() => /* @__PURE__ */ new Map());
2672
- const [copied, setCopied] = useState8(() => /* @__PURE__ */ new Set());
2673
- const [openReasoning, setOpenReasoning] = useState8(() => /* @__PURE__ */ new Map());
2674
- const [picks, setPicks] = useState8(() => /* @__PURE__ */ new Map());
2675
- const [settling, setSettling] = useState8(() => /* @__PURE__ */ new Map());
2676
- const [settleErrors, setSettleErrors] = useState8(() => /* @__PURE__ */ new Map());
2677
- 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({
2678
3054
  options,
2679
3055
  drafts,
2680
3056
  picks,
@@ -2686,23 +3062,23 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2686
3062
  picks,
2687
3063
  messages
2688
3064
  };
2689
- const copyTimers = useRef7(/* @__PURE__ */ new Map());
2690
- useEffect7(() => () => {
3065
+ const copyTimers = useRef8(/* @__PURE__ */ new Map());
3066
+ useEffect9(() => () => {
2691
3067
  for (const timer of copyTimers.current.values()) {
2692
3068
  clearTimeout(timer);
2693
3069
  }
2694
3070
  copyTimers.current.clear();
2695
3071
  }, []);
2696
- const callbackCache = useRef7(/* @__PURE__ */ new Map());
2697
- const reasoningToggleCache = useRef7(/* @__PURE__ */ new Map());
2698
- 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) => {
2699
3075
  setOpenReasoning((current) => {
2700
3076
  const next = new Map(current);
2701
3077
  next.set(key, open ?? !(current.get(key) ?? false));
2702
3078
  return next;
2703
3079
  });
2704
3080
  }, []);
2705
- const stableToggle = useCallback6((key) => {
3081
+ const stableToggle = useCallback7((key) => {
2706
3082
  let cached = reasoningToggleCache.current.get(key);
2707
3083
  if (!cached) {
2708
3084
  cached = /* @__PURE__ */ __name((open) => toggleReasoning(key, open), "cached");
@@ -2712,10 +3088,10 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2712
3088
  }, [
2713
3089
  toggleReasoning
2714
3090
  ]);
2715
- const pick = useCallback6((toolCallId, questionId, values) => {
3091
+ const pick = useCallback7((toolCallId, questionId, values) => {
2716
3092
  setPicks((current) => new Map(current).set(pickKey(toolCallId, questionId), values));
2717
3093
  }, []);
2718
- const settle = useCallback6((toolCallId, action, send) => {
3094
+ const settle = useCallback7((toolCallId, action, send) => {
2719
3095
  setSettleErrors((current) => {
2720
3096
  if (!current.has(toolCallId)) {
2721
3097
  return current;
@@ -2739,7 +3115,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2739
3115
  failed(error);
2740
3116
  }
2741
3117
  }, []);
2742
- const answer = useCallback6((toolCallId) => {
3118
+ const answer = useCallback7((toolCallId) => {
2743
3119
  const current = latest.current;
2744
3120
  const onAnswer = current.options.onAnswer;
2745
3121
  if (!onAnswer) {
@@ -2749,7 +3125,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2749
3125
  }, [
2750
3126
  settle
2751
3127
  ]);
2752
- const skip = useCallback6((toolCallId) => {
3128
+ const skip = useCallback7((toolCallId) => {
2753
3129
  const onSkip = latest.current.options.onSkip;
2754
3130
  if (onSkip) {
2755
3131
  settle(toolCallId, "skip", () => onSkip(toolCallId));
@@ -2757,7 +3133,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2757
3133
  }, [
2758
3134
  settle
2759
3135
  ]);
2760
- const approve = useCallback6((toolCallId, options2) => {
3136
+ const approve = useCallback7((toolCallId, options2) => {
2761
3137
  const onApprove = latest.current.options.onApprove;
2762
3138
  if (onApprove) {
2763
3139
  settle(toolCallId, "approve", () => options2 === void 0 ? onApprove(toolCallId) : onApprove(toolCallId, options2));
@@ -2765,7 +3141,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2765
3141
  }, [
2766
3142
  settle
2767
3143
  ]);
2768
- const reject = useCallback6((toolCallId) => {
3144
+ const reject = useCallback7((toolCallId) => {
2769
3145
  const onReject = latest.current.options.onReject;
2770
3146
  if (onReject) {
2771
3147
  settle(toolCallId, "reject", () => onReject(toolCallId));
@@ -2773,7 +3149,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2773
3149
  }, [
2774
3150
  settle
2775
3151
  ]);
2776
- const callbacksFor = useCallback6((id) => {
3152
+ const callbacksFor = useCallback7((id) => {
2777
3153
  const cached = callbackCache.current.get(id);
2778
3154
  if (cached) {
2779
3155
  return cached;
@@ -3430,35 +3806,35 @@ function toItem(entry) {
3430
3806
  __name(toItem, "toItem");
3431
3807
 
3432
3808
  // src/composer/use-composer-autocomplete.ts
3433
- import { useCallback as useCallback7, useEffect as useEffect8, useId, useLayoutEffect, useMemo as useMemo4, 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";
3434
3810
  function useComposerAutocomplete(options) {
3435
3811
  const { value, sources, acceptOnTab = true } = options;
3436
- const latest = useRef8(options);
3812
+ const latest = useRef9(options);
3437
3813
  latest.current = options;
3438
- const inputRef = useRef8(null);
3439
- const [caret, setCaret] = useState9(value.length);
3440
- const [items, setItems] = useState9([]);
3441
- const [isLoading, setLoading] = useState9(false);
3442
- const [error, setError] = useState9(null);
3443
- const [activeIndex, setActiveIndex] = useState9(0);
3444
- 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);
3445
3821
  const safeCaret = Math.min(caret, value.length);
3446
- const match = useMemo4(() => findActiveTrigger(value, safeCaret, sources), [
3822
+ const match = useMemo6(() => findActiveTrigger(value, safeCaret, sources), [
3447
3823
  value,
3448
3824
  safeCaret,
3449
3825
  sources
3450
3826
  ]);
3451
3827
  const isActive = match !== null && dismissedIndex !== match.index;
3452
- const matchRef = useRef8(match);
3828
+ const matchRef = useRef9(match);
3453
3829
  matchRef.current = match;
3454
- const caretRef = useRef8(safeCaret);
3830
+ const caretRef = useRef9(safeCaret);
3455
3831
  caretRef.current = safeCaret;
3456
- const itemsRef = useRef8(items);
3832
+ const itemsRef = useRef9(items);
3457
3833
  itemsRef.current = items;
3458
- const activeIndexRef = useRef8(activeIndex);
3834
+ const activeIndexRef = useRef9(activeIndex);
3459
3835
  activeIndexRef.current = activeIndex;
3460
3836
  const requestKey = isActive && match ? `${match.source.id}\0${match.query}` : null;
3461
- useEffect8(() => {
3837
+ useEffect10(() => {
3462
3838
  const current = matchRef.current;
3463
3839
  if (requestKey === null || current === null) {
3464
3840
  setItems([]);
@@ -3508,23 +3884,23 @@ function useComposerAutocomplete(options) {
3508
3884
  const activeItem = isOpen ? items[activeIndex] ?? null : null;
3509
3885
  const reactId = useId();
3510
3886
  const listboxId = options.id ?? `agent-autocomplete-${reactId}`;
3511
- const optionId = useCallback7((index) => `${listboxId}-option-${index}`, [
3887
+ const optionId = useCallback8((index) => `${listboxId}-option-${index}`, [
3512
3888
  listboxId
3513
3889
  ]);
3514
- const highlight = useCallback7((index) => {
3890
+ const highlight = useCallback8((index) => {
3515
3891
  const total = itemsRef.current.length;
3516
3892
  if (total === 0) return;
3517
3893
  setActiveIndex((index % total + total) % total);
3518
3894
  }, []);
3519
- const moveHighlight = useCallback7((delta) => highlight(activeIndexRef.current + delta), [
3895
+ const moveHighlight = useCallback8((delta) => highlight(activeIndexRef.current + delta), [
3520
3896
  highlight
3521
3897
  ]);
3522
- const dismiss = useCallback7(() => {
3898
+ const dismiss = useCallback8(() => {
3523
3899
  const current = matchRef.current;
3524
3900
  if (current) setDismissedIndex(current.index);
3525
3901
  }, []);
3526
- const pendingCaret = useRef8(null);
3527
- const accept = useCallback7((item) => {
3902
+ const pendingCaret = useRef9(null);
3903
+ const accept = useCallback8((item) => {
3528
3904
  const current = matchRef.current;
3529
3905
  if (!current) return;
3530
3906
  const chosen = item ?? itemsRef.current[activeIndexRef.current];
@@ -3541,10 +3917,10 @@ function useComposerAutocomplete(options) {
3541
3917
  pendingCaret.current = null;
3542
3918
  inputRef.current?.setSelectionRange(position, position);
3543
3919
  });
3544
- const readCaret = useCallback7((element) => {
3920
+ const readCaret = useCallback8((element) => {
3545
3921
  setCaret(element.selectionStart ?? element.value.length);
3546
3922
  }, []);
3547
- const onKeyDown = useCallback7((event) => {
3923
+ const onKeyDown = useCallback8((event) => {
3548
3924
  if (!isOpen) return;
3549
3925
  const hasItems = itemsRef.current.length > 0;
3550
3926
  switch (event.key) {
@@ -3582,23 +3958,23 @@ function useComposerAutocomplete(options) {
3582
3958
  dismiss,
3583
3959
  moveHighlight
3584
3960
  ]);
3585
- const onChange = useCallback7((event) => {
3961
+ const onChange = useCallback8((event) => {
3586
3962
  readCaret(event.target);
3587
3963
  const next = findActiveTrigger(event.target.value, event.target.selectionStart ?? event.target.value.length, latest.current.sources);
3588
3964
  setDismissedIndex((current) => current !== null && next?.index === current ? current : null);
3589
3965
  }, [
3590
3966
  readCaret
3591
3967
  ]);
3592
- const onSelect = useCallback7((event) => readCaret(event.currentTarget), [
3968
+ const onSelect = useCallback8((event) => readCaret(event.currentTarget), [
3593
3969
  readCaret
3594
3970
  ]);
3595
- const onClick = useCallback7((event) => readCaret(event.currentTarget), [
3971
+ const onClick = useCallback8((event) => readCaret(event.currentTarget), [
3596
3972
  readCaret
3597
3973
  ]);
3598
- const setInput = useCallback7((element) => {
3974
+ const setInput = useCallback8((element) => {
3599
3975
  inputRef.current = element;
3600
3976
  }, []);
3601
- const getInputProps = useCallback7(() => ({
3977
+ const getInputProps = useCallback8(() => ({
3602
3978
  ref: setInput,
3603
3979
  onChange,
3604
3980
  onKeyDown,
@@ -3626,7 +4002,7 @@ function useComposerAutocomplete(options) {
3626
4002
  activeIndex,
3627
4003
  optionId
3628
4004
  ]);
3629
- const getListboxProps = useCallback7(() => ({
4005
+ const getListboxProps = useCallback8(() => ({
3630
4006
  id: listboxId,
3631
4007
  role: "listbox",
3632
4008
  "aria-label": match?.source.label ?? match?.source.id ?? "Suggestions"
@@ -3634,7 +4010,7 @@ function useComposerAutocomplete(options) {
3634
4010
  listboxId,
3635
4011
  match
3636
4012
  ]);
3637
- const getOptionProps = useCallback7((index) => ({
4013
+ const getOptionProps = useCallback8((index) => ({
3638
4014
  id: optionId(index),
3639
4015
  role: "option",
3640
4016
  "aria-selected": index === activeIndex
@@ -3668,7 +4044,7 @@ function isPromise(value) {
3668
4044
  __name(isPromise, "isPromise");
3669
4045
 
3670
4046
  // src/presentation/use-tool-catalog.ts
3671
- import { useCallback as useCallback8, useEffect as useEffect9, useMemo as useMemo5, useState as useState10 } from "react";
4047
+ import { useCallback as useCallback9, useEffect as useEffect11, useMemo as useMemo7, useState as useState11 } from "react";
3672
4048
  var shared = /* @__PURE__ */ new WeakMap();
3673
4049
  function load(client, agent) {
3674
4050
  let byAgent = shared.get(client);
@@ -3692,11 +4068,11 @@ function load(client, agent) {
3692
4068
  __name(load, "load");
3693
4069
  function useToolCatalog(options) {
3694
4070
  const { client, agent, enabled = true } = options;
3695
- const [entries, setEntries] = useState10([]);
3696
- const [isLoading, setLoading] = useState10(enabled);
3697
- const [error, setError] = useState10(null);
3698
- const [generation, setGeneration] = useState10(0);
3699
- useEffect9(() => {
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(() => {
3700
4076
  if (!enabled) {
3701
4077
  setLoading(false);
3702
4078
  return;
@@ -3722,14 +4098,14 @@ function useToolCatalog(options) {
3722
4098
  enabled,
3723
4099
  generation
3724
4100
  ]);
3725
- const refresh = useCallback8(() => {
4101
+ const refresh = useCallback9(() => {
3726
4102
  shared.get(client)?.delete(agent ?? "");
3727
4103
  setGeneration((current) => current + 1);
3728
4104
  }, [
3729
4105
  client,
3730
4106
  agent
3731
4107
  ]);
3732
- const catalog = useMemo5(() => toolCatalogFrom(entries), [
4108
+ const catalog = useMemo7(() => toolCatalogFrom(entries), [
3733
4109
  entries
3734
4110
  ]);
3735
4111
  return {
@@ -3743,7 +4119,7 @@ function useToolCatalog(options) {
3743
4119
  __name(useToolCatalog, "useToolCatalog");
3744
4120
 
3745
4121
  // src/approvals/countdown.ts
3746
- import { useEffect as useEffect10, useRef as useRef9, useState as useState11 } from "react";
4122
+ import { useEffect as useEffect12, useRef as useRef10, useState as useState12 } from "react";
3747
4123
  function approvalCountdown(expiresAt, now = Date.now()) {
3748
4124
  const deadline = expiresAt == null ? Number.NaN : Date.parse(expiresAt);
3749
4125
  if (!Number.isFinite(deadline)) {
@@ -3761,10 +4137,10 @@ function approvalCountdown(expiresAt, now = Date.now()) {
3761
4137
  __name(approvalCountdown, "approvalCountdown");
3762
4138
  function useApprovalCountdown(expiresAt, options = {}) {
3763
4139
  const { intervalMs = 1e3 } = options;
3764
- const nowRef = useRef9(options.now ?? Date.now);
4140
+ const nowRef = useRef10(options.now ?? Date.now);
3765
4141
  nowRef.current = options.now ?? Date.now;
3766
- const [state, setState] = useState11(() => approvalCountdown(expiresAt, nowRef.current()));
3767
- useEffect10(() => {
4142
+ const [state, setState] = useState12(() => approvalCountdown(expiresAt, nowRef.current()));
4143
+ useEffect12(() => {
3768
4144
  const read = /* @__PURE__ */ __name(() => approvalCountdown(expiresAt, nowRef.current()), "read");
3769
4145
  const first = read();
3770
4146
  setState(first);
@@ -3827,7 +4203,13 @@ function storedMessageToUiMessage(message) {
3827
4203
  type: "file",
3828
4204
  mediaType: attachment.contentType,
3829
4205
  filename: attachment.name,
3830
- 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
+ }
3831
4213
  });
3832
4214
  }
3833
4215
  const toolCallIds = new Set((message.toolCalls ?? []).map((call) => call.id));
@@ -4006,20 +4388,20 @@ __name(sumUsage, "sumUsage");
4006
4388
 
4007
4389
  // src/use-agent-chat.ts
4008
4390
  import { useChat } from "@ai-sdk/react";
4009
- import { useCallback as useCallback9, useEffect as useEffect11, useMemo as useMemo6, 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";
4010
4392
  function useAgentChat(options) {
4011
- const latest = useRef10(options);
4393
+ const latest = useRef11(options);
4012
4394
  latest.current = options;
4013
- const [runId, setRunId] = useState12(options.resumeRunId);
4014
- const runIdRef = useRef10(runId);
4395
+ const [runId, setRunId] = useState13(options.resumeRunId);
4396
+ const runIdRef = useRef11(runId);
4015
4397
  runIdRef.current = runId;
4016
- const settlingRunIdRef = useRef10(void 0);
4017
- const autoResumeRunIdRef = useRef10(void 0);
4018
- const [autoResume, setAutoResume] = useState12(false);
4019
- const [activeRunId, setActiveRunId] = useState12(null);
4020
- const regenerateNext = useRef10(false);
4021
- const createdThreadId = useRef10(void 0);
4022
- const client = useMemo6(() => {
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(() => {
4023
4405
  if (options.backend) return options.backend;
4024
4406
  if (options.client) return options.client;
4025
4407
  return new AgentClient({
@@ -4035,11 +4417,11 @@ function useAgentChat(options) {
4035
4417
  getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders")
4036
4418
  });
4037
4419
  }, []);
4038
- const [connection, setConnection] = useState12({
4420
+ const [connection, setConnection] = useState13({
4039
4421
  status: "live"
4040
4422
  });
4041
- const resyncAfterSettle = useRef10(false);
4042
- useEffect11(() => {
4423
+ const resyncAfterSettle = useRef11(false);
4424
+ useEffect13(() => {
4043
4425
  if (!options.resume || options.threadId === void 0) return;
4044
4426
  let cancelled = false;
4045
4427
  const threadId = options.threadId;
@@ -4059,7 +4441,7 @@ function useAgentChat(options) {
4059
4441
  options.resume,
4060
4442
  options.threadId
4061
4443
  ]);
4062
- const transport = useMemo6(() => {
4444
+ const transport = useMemo8(() => {
4063
4445
  function onMeta(meta) {
4064
4446
  settlingRunIdRef.current = meta.runId;
4065
4447
  setRunId(meta.runId);
@@ -4114,7 +4496,7 @@ function useAgentChat(options) {
4114
4496
  }, "onAttemptStart")
4115
4497
  });
4116
4498
  }, []);
4117
- const backgroundRef = useRef10(() => Promise.resolve());
4499
+ const backgroundRef = useRef11(() => Promise.resolve());
4118
4500
  const chat = useChat({
4119
4501
  transport,
4120
4502
  resume: options.resumeRunId !== void 0 || autoResume,
@@ -4163,7 +4545,7 @@ function useAgentChat(options) {
4163
4545
  }
4164
4546
  }, "onFinish")
4165
4547
  });
4166
- const chatRef = useRef10(chat);
4548
+ const chatRef = useRef11(chat);
4167
4549
  chatRef.current = chat;
4168
4550
  async function resyncFromThread() {
4169
4551
  const threadId = latest.current.threadId ?? createdThreadId.current;
@@ -4175,11 +4557,13 @@ function useAgentChat(options) {
4175
4557
  }
4176
4558
  }
4177
4559
  __name(resyncFromThread, "resyncFromThread");
4178
- const turnInFlight = useRef10(false);
4179
- const isTurnInFlight = useCallback9(() => turnInFlight.current || transport.isAttemptLive, [
4560
+ const turnInFlight = useRef11(false);
4561
+ const isTurnInFlight = useCallback10(() => turnInFlight.current || transport.isAttemptLive, [
4180
4562
  transport
4181
4563
  ]);
4182
- const sendMessage = useCallback9(async (...args) => {
4564
+ const sendMessage = useCallback10(async (...args) => {
4565
+ const blocked = latest.current.blocked;
4566
+ if (blocked != null) throw new QuotaBlockedError(blocked);
4183
4567
  if (isTurnInFlight()) return;
4184
4568
  turnInFlight.current = true;
4185
4569
  try {
@@ -4190,15 +4574,15 @@ function useAgentChat(options) {
4190
4574
  }, [
4191
4575
  isTurnInFlight
4192
4576
  ]);
4193
- const addToolResult = useCallback9(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
4577
+ const addToolResult = useCallback10(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
4194
4578
  tool,
4195
4579
  toolCallId,
4196
4580
  output
4197
4581
  }), []);
4198
- const [threads, setThreads] = useState12([]);
4199
- const [quota, setQuota] = useState12(null);
4200
- const [backgroundRuns, setBackgroundRuns] = useState12([]);
4201
- 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 () => {
4202
4586
  const threadId = latest.current.threadId ?? createdThreadId.current;
4203
4587
  if (threadId === void 0) {
4204
4588
  return;
@@ -4223,7 +4607,7 @@ function useAgentChat(options) {
4223
4607
  ]);
4224
4608
  backgroundRef.current = refreshBackground;
4225
4609
  const isWorking = backgroundRuns.some((run) => run.status === "running");
4226
- useEffect11(() => {
4610
+ useEffect13(() => {
4227
4611
  if (options.background !== true || options.threadId === void 0) {
4228
4612
  return;
4229
4613
  }
@@ -4233,7 +4617,7 @@ function useAgentChat(options) {
4233
4617
  options.threadId,
4234
4618
  refreshBackground
4235
4619
  ]);
4236
- useEffect11(() => {
4620
+ useEffect13(() => {
4237
4621
  const every = latest.current.backgroundPollMs ?? 5e3;
4238
4622
  if (latest.current.background !== true || !isWorking || every <= 0) {
4239
4623
  return;
@@ -4244,26 +4628,26 @@ function useAgentChat(options) {
4244
4628
  isWorking,
4245
4629
  refreshBackground
4246
4630
  ]);
4247
- const loadThreads = useCallback9(async () => {
4631
+ const loadThreads = useCallback10(async () => {
4248
4632
  const list = await client.listThreads();
4249
4633
  setThreads(list);
4250
4634
  return list;
4251
4635
  }, [
4252
4636
  client
4253
4637
  ]);
4254
- const loadThread = useCallback9((id) => client.getThread(id), [
4638
+ const loadThread = useCallback10((id) => client.getThread(id), [
4255
4639
  client
4256
4640
  ]);
4257
- const deleteThread = useCallback9(async (id) => {
4641
+ const deleteThread = useCallback10(async (id) => {
4258
4642
  await client.deleteThread(id);
4259
4643
  setThreads((current) => current.filter((thread) => thread.id !== id));
4260
4644
  }, [
4261
4645
  client
4262
4646
  ]);
4263
- const forkThread = useCallback9((threadId, messageId) => requireBackendMethod(client, "forkFromMessage")(threadId, messageId), [
4647
+ const forkThread = useCallback10((threadId, messageId) => requireBackendMethod(client, "forkFromMessage")(threadId, messageId), [
4264
4648
  client
4265
4649
  ]);
4266
- const renameThread = useCallback9(async (id, title) => {
4650
+ const renameThread = useCallback10(async (id, title) => {
4267
4651
  await client.updateThread(id, {
4268
4652
  title
4269
4653
  });
@@ -4274,24 +4658,24 @@ function useAgentChat(options) {
4274
4658
  }, [
4275
4659
  client
4276
4660
  ]);
4277
- const promoteThread = useCallback9(async (id) => {
4661
+ const promoteThread = useCallback10(async (id) => {
4278
4662
  await requireBackendMethod(client, "promoteThread")(id);
4279
4663
  await loadThreads();
4280
4664
  }, [
4281
4665
  client,
4282
4666
  loadThreads
4283
4667
  ]);
4284
- 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), [
4285
4669
  client
4286
4670
  ]);
4287
- const loadQuota = useCallback9(async () => {
4671
+ const loadQuota = useCallback10(async () => {
4288
4672
  const today = await requireBackendMethod(client, "getQuotaToday")();
4289
4673
  setQuota(today);
4290
4674
  return today;
4291
4675
  }, [
4292
4676
  client
4293
4677
  ]);
4294
- const cancel = useCallback9(async () => {
4678
+ const cancel = useCallback10(async () => {
4295
4679
  chatRef.current.stop();
4296
4680
  const activeRunId2 = runIdRef.current;
4297
4681
  if (activeRunId2) {
@@ -4303,7 +4687,7 @@ function useAgentChat(options) {
4303
4687
  }, [
4304
4688
  client
4305
4689
  ]);
4306
- const approve = useCallback9(async ({ toolCallId, remember, via }) => {
4690
+ const approve = useCallback10(async ({ toolCallId, remember, via }) => {
4307
4691
  await requireBackendMethod(client, "approveToolCall")({
4308
4692
  toolCallId,
4309
4693
  ...remember === true ? {
@@ -4316,7 +4700,7 @@ function useAgentChat(options) {
4316
4700
  }, [
4317
4701
  client
4318
4702
  ]);
4319
- const reject = useCallback9(async ({ toolCallId, reason, via }) => {
4703
+ const reject = useCallback10(async ({ toolCallId, reason, via }) => {
4320
4704
  await requireBackendMethod(client, "rejectToolCall")({
4321
4705
  toolCallId,
4322
4706
  ...reason !== void 0 ? {
@@ -4329,7 +4713,7 @@ function useAgentChat(options) {
4329
4713
  }, [
4330
4714
  client
4331
4715
  ]);
4332
- const answer = useCallback9(async ({ toolCallId, answers }) => {
4716
+ const answer = useCallback10(async ({ toolCallId, answers }) => {
4333
4717
  await requireBackendMethod(client, "answerToolCall")({
4334
4718
  toolCallId,
4335
4719
  ...answers !== void 0 ? {
@@ -4339,14 +4723,16 @@ function useAgentChat(options) {
4339
4723
  }, [
4340
4724
  client
4341
4725
  ]);
4342
- const skip = useCallback9(async ({ toolCallId }) => {
4726
+ const skip = useCallback10(async ({ toolCallId }) => {
4343
4727
  await requireBackendMethod(client, "skipToolCall")({
4344
4728
  toolCallId
4345
4729
  });
4346
4730
  }, [
4347
4731
  client
4348
4732
  ]);
4349
- const regenerate = useCallback9(() => {
4733
+ const regenerate = useCallback10(() => {
4734
+ const blocked = latest.current.blocked;
4735
+ if (blocked != null) throw new QuotaBlockedError(blocked);
4350
4736
  if (isTurnInFlight()) return;
4351
4737
  regenerateNext.current = true;
4352
4738
  turnInFlight.current = true;
@@ -4361,7 +4747,7 @@ function useAgentChat(options) {
4361
4747
  isWorking,
4362
4748
  refresh: refreshBackground
4363
4749
  };
4364
- const setThreadModel = useCallback9(async (model) => {
4750
+ const setThreadModel = useCallback10(async (model) => {
4365
4751
  const threadId = latest.current.threadId ?? createdThreadId.current;
4366
4752
  if (threadId === void 0) {
4367
4753
  throw new Error("setThreadModel: this chat has no thread yet");
@@ -4375,7 +4761,7 @@ function useAgentChat(options) {
4375
4761
  }, [
4376
4762
  client
4377
4763
  ]);
4378
- const getThreadId = useCallback9(() => latest.current.threadId ?? createdThreadId.current, []);
4764
+ const getThreadId = useCallback10(() => latest.current.threadId ?? createdThreadId.current, []);
4379
4765
  const status = connection.status === "reconnecting" ? "reconnecting" : chat.status;
4380
4766
  return {
4381
4767
  ...chat,
@@ -4418,6 +4804,16 @@ function useAgentChat(options) {
4418
4804
  };
4419
4805
  }
4420
4806
  __name(useAgentChat, "useAgentChat");
4807
+ var QuotaBlockedError = class extends Error {
4808
+ static {
4809
+ __name(this, "QuotaBlockedError");
4810
+ }
4811
+ block;
4812
+ constructor(block) {
4813
+ super(block.reason ?? `The ${block.period === "day" ? "daily" : "monthly"} quota is used up`), this.block = block;
4814
+ this.name = "QuotaBlockedError";
4815
+ }
4816
+ };
4421
4817
  async function mergeHeaders(options) {
4422
4818
  const dynamic = await options.getHeaders?.() ?? {};
4423
4819
  return {
@@ -4436,7 +4832,9 @@ export {
4436
4832
  MessageItem,
4437
4833
  MessageItemView,
4438
4834
  MessageList,
4835
+ QuotaBlockedError,
4439
4836
  SPEECH_LANGUAGES,
4837
+ acceptsFile,
4440
4838
  applyCompletion,
4441
4839
  approvalCountdown,
4442
4840
  backgroundRunsFromThread,
@@ -4447,7 +4845,10 @@ export {
4447
4845
  describeTimestamp,
4448
4846
  describeToolCall,
4449
4847
  describeUsage,
4848
+ dragHasFiles,
4450
4849
  extractMessageText,
4850
+ fileKind,
4851
+ filesFromClipboard,
4451
4852
  fillTemplate,
4452
4853
  filterAutocompleteItems,
4453
4854
  findActiveTrigger,
@@ -4457,6 +4858,7 @@ export {
4457
4858
  inferResultView,
4458
4859
  isActionCall,
4459
4860
  loadStoredSpeechLang,
4861
+ messageFiles,
4460
4862
  notifyThreads,
4461
4863
  onThreadsEvent,
4462
4864
  persistSpeechLang,
@@ -4472,11 +4874,13 @@ export {
4472
4874
  useAgentChat,
4473
4875
  useAgents,
4474
4876
  useApprovalCountdown,
4877
+ useAttachments,
4475
4878
  useChatTranscript,
4476
4879
  useComposerAutocomplete,
4477
4880
  useElapsed,
4478
4881
  useMessageFeedback,
4479
4882
  useModels,
4883
+ useQuota,
4480
4884
  useSpeechRecognition,
4481
4885
  useStickToBottom,
4482
4886
  useThreads,