@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.cjs CHANGED
@@ -40,7 +40,9 @@ __export(src_exports, {
40
40
  MessageItem: () => MessageItem,
41
41
  MessageItemView: () => MessageItemView,
42
42
  MessageList: () => MessageList,
43
+ QuotaBlockedError: () => QuotaBlockedError,
43
44
  SPEECH_LANGUAGES: () => SPEECH_LANGUAGES,
45
+ acceptsFile: () => acceptsFile,
44
46
  applyCompletion: () => applyCompletion,
45
47
  approvalCountdown: () => approvalCountdown,
46
48
  backgroundRunsFromThread: () => backgroundRunsFromThread,
@@ -51,7 +53,10 @@ __export(src_exports, {
51
53
  describeTimestamp: () => describeTimestamp,
52
54
  describeToolCall: () => describeToolCall,
53
55
  describeUsage: () => describeUsage,
56
+ dragHasFiles: () => dragHasFiles,
54
57
  extractMessageText: () => extractMessageText,
58
+ fileKind: () => fileKind,
59
+ filesFromClipboard: () => filesFromClipboard,
55
60
  fillTemplate: () => fillTemplate,
56
61
  filterAutocompleteItems: () => filterAutocompleteItems,
57
62
  findActiveTrigger: () => findActiveTrigger,
@@ -61,6 +66,7 @@ __export(src_exports, {
61
66
  inferResultView: () => inferResultView,
62
67
  isActionCall: () => isActionCall,
63
68
  loadStoredSpeechLang: () => loadStoredSpeechLang,
69
+ messageFiles: () => messageFiles,
64
70
  notifyThreads: () => notifyThreads,
65
71
  onThreadsEvent: () => onThreadsEvent,
66
72
  persistSpeechLang: () => persistSpeechLang,
@@ -76,11 +82,13 @@ __export(src_exports, {
76
82
  useAgentChat: () => useAgentChat,
77
83
  useAgents: () => useAgents,
78
84
  useApprovalCountdown: () => useApprovalCountdown,
85
+ useAttachments: () => useAttachments,
79
86
  useChatTranscript: () => useChatTranscript,
80
87
  useComposerAutocomplete: () => useComposerAutocomplete,
81
88
  useElapsed: () => useElapsed,
82
89
  useMessageFeedback: () => useMessageFeedback,
83
90
  useModels: () => useModels,
91
+ useQuota: () => useQuota,
84
92
  useSpeechRecognition: () => useSpeechRecognition,
85
93
  useStickToBottom: () => useStickToBottom,
86
94
  useThreads: () => useThreads,
@@ -215,6 +223,9 @@ var AgentClient = class {
215
223
  * what a caller then rides on `sendMessage({ text }, { body: { attachments: [...] } })`.
216
224
  */
217
225
  async uploadAttachment(file, options = {}) {
226
+ if (options.onProgress !== void 0 && this.options.fetch === void 0 && typeof XMLHttpRequest !== "undefined") {
227
+ return this.uploadWithProgress(file, options);
228
+ }
218
229
  const formData = new FormData();
219
230
  formData.append("file", file);
220
231
  const response = await this.fetchImpl()(`${this.baseUrl()}/agent/attachments`, {
@@ -229,7 +240,47 @@ var AgentClient = class {
229
240
  signal: options.signal
230
241
  } : {}
231
242
  });
232
- return this.handleResponse(response, "POST", "/agent/attachments");
243
+ const attachment = await this.handleResponse(response, "POST", "/agent/attachments");
244
+ options.onProgress?.(1);
245
+ return attachment;
246
+ }
247
+ async uploadWithProgress(file, { signal, onProgress }) {
248
+ const headers = {
249
+ accept: "application/json",
250
+ ...await this.resolveHeaders()
251
+ };
252
+ const url = `${this.baseUrl()}/agent/attachments`;
253
+ return new Promise((resolve, reject) => {
254
+ const xhr = new XMLHttpRequest();
255
+ xhr.open("POST", url);
256
+ for (const [name, value] of Object.entries(headers)) xhr.setRequestHeader(name, value);
257
+ xhr.withCredentials = this.options.credentials === "include";
258
+ xhr.upload.onprogress = (event) => {
259
+ if (event.lengthComputable && event.total > 0) onProgress?.(event.loaded / event.total);
260
+ };
261
+ xhr.onload = () => {
262
+ if (xhr.status < 200 || xhr.status >= 300) {
263
+ reject(new AgentHttpError(xhr.status, "POST", "/agent/attachments", xhr.statusText));
264
+ return;
265
+ }
266
+ onProgress?.(1);
267
+ resolve(JSON.parse(xhr.responseText));
268
+ };
269
+ xhr.onerror = () => reject(new TypeError("Network error while uploading the attachment"));
270
+ xhr.onabort = () => reject(new DOMException("The upload was aborted", "AbortError"));
271
+ if (signal !== void 0) {
272
+ if (signal.aborted) {
273
+ reject(new DOMException("The upload was aborted", "AbortError"));
274
+ return;
275
+ }
276
+ signal.addEventListener("abort", () => xhr.abort(), {
277
+ once: true
278
+ });
279
+ }
280
+ const formData = new FormData();
281
+ formData.append("file", file);
282
+ xhr.send(formData);
283
+ });
233
284
  }
234
285
  promoteThread(id) {
235
286
  return this.request("POST", `/agent/threads/${encodeURIComponent(id)}/promote`);
@@ -246,6 +297,10 @@ var AgentClient = class {
246
297
  listAgents() {
247
298
  return this.request("GET", "/agent/agents");
248
299
  }
300
+ /** `GET /agent/quota` — the caller's budget windows and the one blocking sends, if any. */
301
+ getQuota() {
302
+ return this.request("GET", "/agent/quota");
303
+ }
249
304
  getQuotaToday() {
250
305
  return this.request("GET", "/agent/quota/today");
251
306
  }
@@ -1073,6 +1128,68 @@ function delay(ms, signal) {
1073
1128
  }
1074
1129
  __name(delay, "delay");
1075
1130
 
1131
+ // src/attachments/files.ts
1132
+ function fileKind(mediaType) {
1133
+ const type = mediaType.toLowerCase();
1134
+ if (type.startsWith("image/")) return "image";
1135
+ if (type === "application/pdf") return "pdf";
1136
+ if (type.startsWith("text/") || type === "application/json") return "text";
1137
+ if (type.startsWith("audio/")) return "audio";
1138
+ if (type.startsWith("video/")) return "video";
1139
+ return "other";
1140
+ }
1141
+ __name(fileKind, "fileKind");
1142
+ function messageFiles(message) {
1143
+ const files = [];
1144
+ for (const part of message.parts) {
1145
+ if (part.type !== "file") continue;
1146
+ const file = part;
1147
+ const filename = file.filename ?? null;
1148
+ const dot = filename?.lastIndexOf(".") ?? -1;
1149
+ const mediaId = file.providerMetadata?.agent?.mediaId;
1150
+ files.push({
1151
+ url: file.url,
1152
+ mediaType: file.mediaType,
1153
+ filename,
1154
+ kind: fileKind(file.mediaType),
1155
+ extension: filename !== null && dot > 0 ? filename.slice(dot + 1).toLowerCase() : null,
1156
+ mediaId: typeof mediaId === "string" ? mediaId : null
1157
+ });
1158
+ }
1159
+ return files;
1160
+ }
1161
+ __name(messageFiles, "messageFiles");
1162
+ function acceptsFile(accept, file) {
1163
+ const rules = (typeof accept === "string" ? accept.split(",") : accept ?? []).map((rule) => rule.trim().toLowerCase()).filter((rule) => rule.length > 0);
1164
+ if (rules.length === 0) return true;
1165
+ const type = file.type.toLowerCase();
1166
+ const name = file.name.toLowerCase();
1167
+ return rules.some((rule) => {
1168
+ if (rule.startsWith(".")) return name.endsWith(rule);
1169
+ if (rule.endsWith("/*")) return type.startsWith(rule.slice(0, -1));
1170
+ return type === rule;
1171
+ });
1172
+ }
1173
+ __name(acceptsFile, "acceptsFile");
1174
+ function filesFromClipboard(data) {
1175
+ if (!data) return [];
1176
+ const files = [];
1177
+ for (const item of Array.from(data.items ?? [])) {
1178
+ if (item.kind !== "file") continue;
1179
+ const file = item.getAsFile();
1180
+ if (file !== null) files.push(file);
1181
+ }
1182
+ return files.length > 0 ? files : Array.from(data.files ?? []);
1183
+ }
1184
+ __name(filesFromClipboard, "filesFromClipboard");
1185
+ function dragHasFiles(data) {
1186
+ return Array.from(data?.types ?? []).includes("Files");
1187
+ }
1188
+ __name(dragHasFiles, "dragHasFiles");
1189
+
1190
+ // src/attachments/use-attachments.ts
1191
+ var import_react2 = require("react");
1192
+
1076
1193
  // src/backend.ts
1077
1194
  var AgentBackendUnsupportedError = class extends Error {
1078
1195
  static {
@@ -1093,19 +1210,239 @@ function requireBackendMethod(backend, name) {
1093
1210
  }
1094
1211
  __name(requireBackendMethod, "requireBackendMethod");
1095
1212
 
1213
+ // src/attachments/use-attachments.ts
1214
+ var DEFAULT_REJECTION = {
1215
+ type: /* @__PURE__ */ __name((file) => `${file.name} is not a supported file type`, "type"),
1216
+ size: /* @__PURE__ */ __name((file) => `${file.name} is too large`, "size"),
1217
+ count: /* @__PURE__ */ __name(() => "Too many files", "count")
1218
+ };
1219
+ var sequence = 0;
1220
+ function useAttachments(options) {
1221
+ const latest = (0, import_react2.useRef)(options);
1222
+ latest.current = options;
1223
+ const [items, setItems] = (0, import_react2.useState)([]);
1224
+ const itemsRef = (0, import_react2.useRef)(items);
1225
+ itemsRef.current = items;
1226
+ const controllers = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
1227
+ const [dragDepth, setDragDepth] = (0, import_react2.useState)(0);
1228
+ const patch = (0, import_react2.useCallback)((id, change) => {
1229
+ setItems((current) => current.map((item) => item.id === id ? {
1230
+ ...item,
1231
+ ...change
1232
+ } : item));
1233
+ }, []);
1234
+ const start = (0, import_react2.useCallback)((item) => {
1235
+ const { upload, backend } = latest.current;
1236
+ const run = upload ?? (backend?.uploadAttachment !== void 0 ? backend.uploadAttachment.bind(backend) : void 0);
1237
+ if (run === void 0) {
1238
+ patch(item.id, {
1239
+ status: "error",
1240
+ error: new AgentBackendUnsupportedError("uploadAttachment").message
1241
+ });
1242
+ return;
1243
+ }
1244
+ const controller = new AbortController();
1245
+ controllers.current.set(item.id, controller);
1246
+ run(item.file, {
1247
+ signal: controller.signal,
1248
+ onProgress: /* @__PURE__ */ __name((fraction) => patch(item.id, {
1249
+ progress: Math.max(0, Math.min(1, fraction))
1250
+ }), "onProgress")
1251
+ }).then((attachment) => {
1252
+ if (controller.signal.aborted) return;
1253
+ controllers.current.delete(item.id);
1254
+ patch(item.id, {
1255
+ status: "ready",
1256
+ progress: 1,
1257
+ attachment
1258
+ });
1259
+ }, (error) => {
1260
+ if (controller.signal.aborted) return;
1261
+ controllers.current.delete(item.id);
1262
+ patch(item.id, {
1263
+ status: "error",
1264
+ error: error instanceof Error ? error.message : String(error)
1265
+ });
1266
+ });
1267
+ }, [
1268
+ patch
1269
+ ]);
1270
+ const add = (0, import_react2.useCallback)((files) => {
1271
+ if (files == null) return;
1272
+ const { accept: accept2, maxBytes, maxFiles, describeRejection } = latest.current;
1273
+ const describe = /* @__PURE__ */ __name((reason, file) => describeRejection?.(reason, file) ?? DEFAULT_REJECTION[reason](file), "describe");
1274
+ let room = maxFiles === void 0 ? Number.POSITIVE_INFINITY : maxFiles - itemsRef.current.filter((item) => item.status !== "rejected").length;
1275
+ const staged = [];
1276
+ for (const file of Array.from(files)) {
1277
+ sequence += 1;
1278
+ const base = {
1279
+ id: `att-${sequence}`,
1280
+ file,
1281
+ name: file.name,
1282
+ size: file.size,
1283
+ type: file.type,
1284
+ progress: 0
1285
+ };
1286
+ const reason = !acceptsFile(accept2, file) ? "type" : maxBytes !== void 0 && file.size > maxBytes ? "size" : room <= 0 ? "count" : void 0;
1287
+ if (reason !== void 0) {
1288
+ staged.push({
1289
+ ...base,
1290
+ status: "rejected",
1291
+ error: describe(reason, file)
1292
+ });
1293
+ continue;
1294
+ }
1295
+ room -= 1;
1296
+ const previewUrl = file.type.startsWith("image/") && typeof URL.createObjectURL === "function" ? URL.createObjectURL(file) : void 0;
1297
+ staged.push({
1298
+ ...base,
1299
+ status: "uploading",
1300
+ ...previewUrl ? {
1301
+ previewUrl
1302
+ } : {}
1303
+ });
1304
+ }
1305
+ if (staged.length === 0) return;
1306
+ setItems((current) => [
1307
+ ...current,
1308
+ ...staged
1309
+ ]);
1310
+ for (const item of staged) {
1311
+ if (item.status === "uploading") start(item);
1312
+ }
1313
+ }, [
1314
+ start
1315
+ ]);
1316
+ const release = (0, import_react2.useCallback)((item) => {
1317
+ if (item === void 0) return;
1318
+ controllers.current.get(item.id)?.abort();
1319
+ controllers.current.delete(item.id);
1320
+ if (item.previewUrl !== void 0 && typeof URL.revokeObjectURL === "function") {
1321
+ URL.revokeObjectURL(item.previewUrl);
1322
+ }
1323
+ }, []);
1324
+ const remove = (0, import_react2.useCallback)((id) => {
1325
+ release(itemsRef.current.find((item) => item.id === id));
1326
+ setItems((current) => current.filter((item) => item.id !== id));
1327
+ }, [
1328
+ release
1329
+ ]);
1330
+ const clear = (0, import_react2.useCallback)(() => {
1331
+ for (const item of itemsRef.current) release(item);
1332
+ setItems([]);
1333
+ }, [
1334
+ release
1335
+ ]);
1336
+ const retry = (0, import_react2.useCallback)((id) => {
1337
+ const item = itemsRef.current.find((candidate) => candidate.id === id);
1338
+ if (item === void 0 || item.status !== "error") return;
1339
+ const { error: _previous, ...rest } = item;
1340
+ const again = {
1341
+ ...rest,
1342
+ status: "uploading",
1343
+ progress: 0
1344
+ };
1345
+ setItems((current) => current.map((candidate) => candidate.id === id ? again : candidate));
1346
+ start(again);
1347
+ }, [
1348
+ start
1349
+ ]);
1350
+ (0, import_react2.useEffect)(() => () => {
1351
+ for (const item of itemsRef.current) release(item);
1352
+ }, [
1353
+ release
1354
+ ]);
1355
+ const onPaste = (0, import_react2.useCallback)((event) => {
1356
+ const files = filesFromClipboard(event.clipboardData);
1357
+ if (files.length === 0) return;
1358
+ event.preventDefault();
1359
+ add(files);
1360
+ }, [
1361
+ add
1362
+ ]);
1363
+ const dropZoneProps = (0, import_react2.useMemo)(() => ({
1364
+ onDragEnter: /* @__PURE__ */ __name((event) => {
1365
+ if (!dragHasFiles(event.dataTransfer)) return;
1366
+ event.preventDefault();
1367
+ setDragDepth((depth) => depth + 1);
1368
+ }, "onDragEnter"),
1369
+ onDragOver: /* @__PURE__ */ __name((event) => {
1370
+ if (dragHasFiles(event.dataTransfer)) event.preventDefault();
1371
+ }, "onDragOver"),
1372
+ onDragLeave: /* @__PURE__ */ __name((event) => {
1373
+ if (!dragHasFiles(event.dataTransfer)) return;
1374
+ setDragDepth((depth) => Math.max(0, depth - 1));
1375
+ }, "onDragLeave"),
1376
+ onDrop: /* @__PURE__ */ __name((event) => {
1377
+ if (!dragHasFiles(event.dataTransfer)) return;
1378
+ event.preventDefault();
1379
+ setDragDepth(0);
1380
+ add(event.dataTransfer?.files);
1381
+ }, "onDrop")
1382
+ }), [
1383
+ add
1384
+ ]);
1385
+ const accept = options.accept;
1386
+ const inputProps = (0, import_react2.useMemo)(() => ({
1387
+ type: "file",
1388
+ accept: typeof accept === "string" ? accept : accept?.join(","),
1389
+ multiple: options.maxFiles === void 0 || options.maxFiles > 1,
1390
+ onChange: /* @__PURE__ */ __name((event) => {
1391
+ const input = event.currentTarget;
1392
+ add(input.files);
1393
+ input.value = "";
1394
+ }, "onChange")
1395
+ }), [
1396
+ accept,
1397
+ options.maxFiles,
1398
+ add
1399
+ ]);
1400
+ return (0, import_react2.useMemo)(() => {
1401
+ const ready = items.filter((item) => item.status === "ready" && item.attachment !== void 0);
1402
+ const attachments = ready.map((item) => item.attachment);
1403
+ return {
1404
+ items,
1405
+ add,
1406
+ remove,
1407
+ retry,
1408
+ clear,
1409
+ isUploading: items.some((item) => item.status === "uploading"),
1410
+ attachments,
1411
+ refs: attachments.map((attachment) => ({
1412
+ mediaId: attachment.mediaId
1413
+ })),
1414
+ inputProps,
1415
+ dropZoneProps,
1416
+ isDragging: dragDepth > 0,
1417
+ onPaste
1418
+ };
1419
+ }, [
1420
+ items,
1421
+ add,
1422
+ remove,
1423
+ retry,
1424
+ clear,
1425
+ inputProps,
1426
+ dropZoneProps,
1427
+ dragDepth,
1428
+ onPaste
1429
+ ]);
1430
+ }
1431
+ __name(useAttachments, "useAttachments");
1432
+
1096
1433
  // src/catalog/use-agents.ts
1097
- var import_react3 = require("react");
1434
+ var import_react4 = require("react");
1098
1435
 
1099
1436
  // src/catalog/use-resource.ts
1100
- var import_react2 = require("react");
1437
+ var import_react3 = require("react");
1101
1438
  function useResource(load2, key, enabled) {
1102
- const [data, setData] = (0, import_react2.useState)(void 0);
1103
- const [isLoading, setIsLoading] = (0, import_react2.useState)(enabled);
1104
- const [error, setError] = (0, import_react2.useState)(null);
1105
- const loadRef = (0, import_react2.useRef)(load2);
1439
+ const [data, setData] = (0, import_react3.useState)(void 0);
1440
+ const [isLoading, setIsLoading] = (0, import_react3.useState)(enabled);
1441
+ const [error, setError] = (0, import_react3.useState)(null);
1442
+ const loadRef = (0, import_react3.useRef)(load2);
1106
1443
  loadRef.current = load2;
1107
- const generation = (0, import_react2.useRef)(0);
1108
- const refresh = (0, import_react2.useCallback)(async () => {
1444
+ const generation = (0, import_react3.useRef)(0);
1445
+ const refresh = (0, import_react3.useCallback)(async () => {
1109
1446
  const mine = ++generation.current;
1110
1447
  setIsLoading(true);
1111
1448
  try {
@@ -1120,7 +1457,7 @@ function useResource(load2, key, enabled) {
1120
1457
  if (mine === generation.current) setIsLoading(false);
1121
1458
  }
1122
1459
  }, []);
1123
- (0, import_react2.useEffect)(() => {
1460
+ (0, import_react3.useEffect)(() => {
1124
1461
  if (!enabled) return;
1125
1462
  void refresh();
1126
1463
  }, [
@@ -1141,7 +1478,7 @@ __name(useResource, "useResource");
1141
1478
  function useAgents(options) {
1142
1479
  const { backend, enabled = true } = options;
1143
1480
  const { data, isLoading, error, refresh } = useResource(() => requireBackendMethod(backend, "listAgents")(), "", enabled);
1144
- return (0, import_react3.useMemo)(() => {
1481
+ return (0, import_react4.useMemo)(() => {
1145
1482
  const agents = data ?? [];
1146
1483
  return {
1147
1484
  agents,
@@ -1160,11 +1497,11 @@ function useAgents(options) {
1160
1497
  __name(useAgents, "useAgents");
1161
1498
 
1162
1499
  // src/catalog/use-models.ts
1163
- var import_react4 = require("react");
1500
+ var import_react5 = require("react");
1164
1501
  function useModels(options) {
1165
1502
  const { backend, agent, enabled = true } = options;
1166
1503
  const { data, isLoading, error, refresh } = useResource(() => requireBackendMethod(backend, "listModels")(agent), `${agent ?? ""}`, enabled);
1167
- return (0, import_react4.useMemo)(() => {
1504
+ return (0, import_react5.useMemo)(() => {
1168
1505
  const providers = data?.providers ?? [];
1169
1506
  const models = providers.flatMap((provider) => provider.models.map((model) => ({
1170
1507
  ...model,
@@ -1193,28 +1530,96 @@ function useModels(options) {
1193
1530
  }
1194
1531
  __name(useModels, "useModels");
1195
1532
 
1533
+ // src/quota/use-quota.ts
1534
+ var import_react6 = require("react");
1535
+
1536
+ // src/threads/threads-events.ts
1537
+ var listeners = /* @__PURE__ */ new WeakMap();
1538
+ function notifyThreads(backend, event) {
1539
+ for (const listener of listeners.get(backend) ?? []) {
1540
+ listener(event);
1541
+ }
1542
+ }
1543
+ __name(notifyThreads, "notifyThreads");
1544
+ function onThreadsEvent(backend, listener) {
1545
+ let set = listeners.get(backend);
1546
+ if (set === void 0) {
1547
+ set = /* @__PURE__ */ new Set();
1548
+ listeners.set(backend, set);
1549
+ }
1550
+ set.add(listener);
1551
+ return () => {
1552
+ set.delete(listener);
1553
+ };
1554
+ }
1555
+ __name(onThreadsEvent, "onThreadsEvent");
1556
+
1557
+ // src/quota/use-quota.ts
1558
+ function useQuota(options) {
1559
+ const { backend, enabled = true, pollMs } = options;
1560
+ const { data, isLoading, error, refresh } = useResource(() => requireBackendMethod(backend, "getQuota")(), "", enabled);
1561
+ (0, import_react6.useEffect)(() => {
1562
+ if (!enabled) return;
1563
+ return onThreadsEvent(backend, (event) => {
1564
+ if (event.type === "changed") void refresh();
1565
+ });
1566
+ }, [
1567
+ backend,
1568
+ enabled,
1569
+ refresh
1570
+ ]);
1571
+ (0, import_react6.useEffect)(() => {
1572
+ if (!enabled || pollMs === void 0 || pollMs <= 0) return;
1573
+ const timer = setInterval(() => void refresh(), pollMs);
1574
+ return () => clearInterval(timer);
1575
+ }, [
1576
+ enabled,
1577
+ pollMs,
1578
+ refresh
1579
+ ]);
1580
+ return (0, import_react6.useMemo)(() => {
1581
+ const windows = data?.windows ?? [];
1582
+ return {
1583
+ report: data,
1584
+ windows,
1585
+ day: windows.find((window2) => window2.period === "day"),
1586
+ month: windows.find((window2) => window2.period === "month"),
1587
+ blocked: data?.blocked ?? null,
1588
+ isLoading,
1589
+ error,
1590
+ refresh
1591
+ };
1592
+ }, [
1593
+ data,
1594
+ isLoading,
1595
+ error,
1596
+ refresh
1597
+ ]);
1598
+ }
1599
+ __name(useQuota, "useQuota");
1600
+
1196
1601
  // src/feedback/use-message-feedback.ts
1197
- var import_react5 = require("react");
1602
+ var import_react7 = require("react");
1198
1603
  function useMessageFeedback(options) {
1199
- const latest = (0, import_react5.useRef)(options);
1604
+ const latest = (0, import_react7.useRef)(options);
1200
1605
  latest.current = options;
1201
- const [local, setLocal] = (0, import_react5.useState)(/* @__PURE__ */ new Map());
1202
- const [pending, setPending] = (0, import_react5.useState)(/* @__PURE__ */ new Set());
1203
- const [error, setError] = (0, import_react5.useState)(null);
1204
- const localRef = (0, import_react5.useRef)(local);
1606
+ const [local, setLocal] = (0, import_react7.useState)(/* @__PURE__ */ new Map());
1607
+ const [pending, setPending] = (0, import_react7.useState)(/* @__PURE__ */ new Set());
1608
+ const [error, setError] = (0, import_react7.useState)(null);
1609
+ const localRef = (0, import_react7.useRef)(local);
1205
1610
  localRef.current = local;
1206
- const resolved = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
1207
- const feedbackOf = (0, import_react5.useCallback)((message) => {
1611
+ const resolved = (0, import_react7.useRef)(/* @__PURE__ */ new Map());
1612
+ const feedbackOf = (0, import_react7.useCallback)((message) => {
1208
1613
  const mine = local.get(message.id);
1209
1614
  if (mine !== void 0) return mine.feedback;
1210
1615
  return metadataOf(message).feedback ?? null;
1211
1616
  }, [
1212
1617
  local
1213
1618
  ]);
1214
- const isPending = (0, import_react5.useCallback)((message) => pending.has(message.id), [
1619
+ const isPending = (0, import_react7.useCallback)((message) => pending.has(message.id), [
1215
1620
  pending
1216
1621
  ]);
1217
- const storedIdFor = (0, import_react5.useCallback)(async (message) => {
1622
+ const storedIdFor = (0, import_react7.useCallback)(async (message) => {
1218
1623
  const runId = metadataOf(message).runId;
1219
1624
  if (runId === void 0) return message.id;
1220
1625
  const known = resolved.current.get(message.id);
@@ -1234,7 +1639,7 @@ function useMessageFeedback(options) {
1234
1639
  resolved.current.set(message.id, row.id);
1235
1640
  return row.id;
1236
1641
  }, []);
1237
- const rate = (0, import_react5.useCallback)(async (message, value, comment) => {
1642
+ const rate = (0, import_react7.useCallback)(async (message, value, comment) => {
1238
1643
  const setFeedback = requireBackendMethod(latest.current.backend, "setMessageFeedback");
1239
1644
  const had = localRef.current.get(message.id);
1240
1645
  const optimistic = value === null ? null : {
@@ -1280,7 +1685,7 @@ function useMessageFeedback(options) {
1280
1685
  }, [
1281
1686
  storedIdFor
1282
1687
  ]);
1283
- const toggle = (0, import_react5.useCallback)((message, value) => {
1688
+ const toggle = (0, import_react7.useCallback)((message, value) => {
1284
1689
  const mine = localRef.current.get(message.id);
1285
1690
  const current = mine !== void 0 ? mine.feedback : metadataOf(message).feedback ?? null;
1286
1691
  return rate(message, current?.value === value ? null : value);
@@ -1302,38 +1707,17 @@ function metadataOf(message) {
1302
1707
  }
1303
1708
  __name(metadataOf, "metadataOf");
1304
1709
 
1305
- // src/threads/threads-events.ts
1306
- var listeners = /* @__PURE__ */ new WeakMap();
1307
- function notifyThreads(backend, event) {
1308
- for (const listener of listeners.get(backend) ?? []) {
1309
- listener(event);
1310
- }
1311
- }
1312
- __name(notifyThreads, "notifyThreads");
1313
- function onThreadsEvent(backend, listener) {
1314
- let set = listeners.get(backend);
1315
- if (set === void 0) {
1316
- set = /* @__PURE__ */ new Set();
1317
- listeners.set(backend, set);
1318
- }
1319
- set.add(listener);
1320
- return () => {
1321
- set.delete(listener);
1322
- };
1323
- }
1324
- __name(onThreadsEvent, "onThreadsEvent");
1325
-
1326
1710
  // src/threads/use-threads.ts
1327
- var import_react6 = require("react");
1711
+ var import_react8 = require("react");
1328
1712
  function useThreads(options) {
1329
1713
  const { backend, enabled = true } = options;
1330
- const [threads, setThreads] = (0, import_react6.useState)([]);
1331
- const [isLoading, setIsLoading] = (0, import_react6.useState)(enabled);
1332
- const [error, setError] = (0, import_react6.useState)(null);
1333
- const threadsRef = (0, import_react6.useRef)(threads);
1714
+ const [threads, setThreads] = (0, import_react8.useState)([]);
1715
+ const [isLoading, setIsLoading] = (0, import_react8.useState)(enabled);
1716
+ const [error, setError] = (0, import_react8.useState)(null);
1717
+ const threadsRef = (0, import_react8.useRef)(threads);
1334
1718
  threadsRef.current = threads;
1335
- const generation = (0, import_react6.useRef)(0);
1336
- const refresh = (0, import_react6.useCallback)(async () => {
1719
+ const generation = (0, import_react8.useRef)(0);
1720
+ const refresh = (0, import_react8.useCallback)(async () => {
1337
1721
  const mine = ++generation.current;
1338
1722
  setIsLoading(true);
1339
1723
  try {
@@ -1350,7 +1734,7 @@ function useThreads(options) {
1350
1734
  }, [
1351
1735
  backend
1352
1736
  ]);
1353
- (0, import_react6.useEffect)(() => {
1737
+ (0, import_react8.useEffect)(() => {
1354
1738
  if (!enabled) return;
1355
1739
  void refresh();
1356
1740
  return onThreadsEvent(backend, (event) => {
@@ -1370,7 +1754,7 @@ function useThreads(options) {
1370
1754
  enabled,
1371
1755
  refresh
1372
1756
  ]);
1373
- const rename = (0, import_react6.useCallback)(async (id, title) => {
1757
+ const rename = (0, import_react8.useCallback)(async (id, title) => {
1374
1758
  const before = threadsRef.current;
1375
1759
  setThreads((current) => current.map((thread) => thread.id === id ? {
1376
1760
  ...thread,
@@ -1392,7 +1776,7 @@ function useThreads(options) {
1392
1776
  }, [
1393
1777
  backend
1394
1778
  ]);
1395
- const remove = (0, import_react6.useCallback)(async (id) => {
1779
+ const remove = (0, import_react8.useCallback)(async (id) => {
1396
1780
  const before = threadsRef.current;
1397
1781
  setThreads((current) => current.filter((thread) => thread.id !== id));
1398
1782
  try {
@@ -1467,10 +1851,10 @@ function backgroundRunsFromThread(messages) {
1467
1851
  __name(backgroundRunsFromThread, "backgroundRunsFromThread");
1468
1852
 
1469
1853
  // src/components/chat-input.tsx
1470
- var import_react8 = __toESM(require("react"), 1);
1854
+ var import_react10 = __toESM(require("react"), 1);
1471
1855
 
1472
1856
  // src/components/use-speech-recognition.ts
1473
- var import_react7 = require("react");
1857
+ var import_react9 = require("react");
1474
1858
  function getRecognitionCtor() {
1475
1859
  if (typeof window === "undefined") return null;
1476
1860
  const scope = window;
@@ -1478,15 +1862,15 @@ function getRecognitionCtor() {
1478
1862
  }
1479
1863
  __name(getRecognitionCtor, "getRecognitionCtor");
1480
1864
  function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTranscript }) {
1481
- const recognitionRef = (0, import_react7.useRef)(null);
1482
- const [isListening, setIsListening] = (0, import_react7.useState)(false);
1483
- const ctor = (0, import_react7.useMemo)(() => getRecognitionCtor(), []);
1865
+ const recognitionRef = (0, import_react9.useRef)(null);
1866
+ const [isListening, setIsListening] = (0, import_react9.useState)(false);
1867
+ const ctor = (0, import_react9.useMemo)(() => getRecognitionCtor(), []);
1484
1868
  const isSupported = ctor !== null;
1485
- const finalRef = (0, import_react7.useRef)(onFinalTranscript);
1486
- const interimRef = (0, import_react7.useRef)(onInterimTranscript);
1869
+ const finalRef = (0, import_react9.useRef)(onFinalTranscript);
1870
+ const interimRef = (0, import_react9.useRef)(onInterimTranscript);
1487
1871
  finalRef.current = onFinalTranscript;
1488
1872
  interimRef.current = onInterimTranscript;
1489
- (0, import_react7.useEffect)(() => {
1873
+ (0, import_react9.useEffect)(() => {
1490
1874
  if (!ctor) return;
1491
1875
  const recognition = new ctor();
1492
1876
  recognition.lang = lang;
@@ -1525,7 +1909,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
1525
1909
  ctor,
1526
1910
  lang
1527
1911
  ]);
1528
- const start = (0, import_react7.useCallback)(() => {
1912
+ const start = (0, import_react9.useCallback)(() => {
1529
1913
  const recognition = recognitionRef.current;
1530
1914
  if (!recognition) return;
1531
1915
  try {
@@ -1535,7 +1919,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
1535
1919
  console.warn("SpeechRecognition start failed", error);
1536
1920
  }
1537
1921
  }, []);
1538
- const stop = (0, import_react7.useCallback)(() => {
1922
+ const stop = (0, import_react9.useCallback)(() => {
1539
1923
  const recognition = recognitionRef.current;
1540
1924
  if (!recognition) return;
1541
1925
  try {
@@ -1554,8 +1938,8 @@ __name(useSpeechRecognition, "useSpeechRecognition");
1554
1938
 
1555
1939
  // src/components/chat-input.tsx
1556
1940
  function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = true, speechLang = "en-US", sendLabel = "Send", onStop, isStreaming = false, stopLabel = "Stop generating", className, textareaClassName, sendButtonClassName, micButtonClassName, stopButtonClassName, renderMic }) {
1557
- const [value, setValue] = (0, import_react8.useState)("");
1558
- const [interim, setInterim] = (0, import_react8.useState)("");
1941
+ const [value, setValue] = (0, import_react10.useState)("");
1942
+ const [interim, setInterim] = (0, import_react10.useState)("");
1559
1943
  const speech = useSpeechRecognition({
1560
1944
  lang: speechLang,
1561
1945
  onFinalTranscript: /* @__PURE__ */ __name((text) => {
@@ -1566,7 +1950,7 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
1566
1950
  }, "onFinalTranscript"),
1567
1951
  onInterimTranscript: /* @__PURE__ */ __name((text) => setInterim(text), "onInterimTranscript")
1568
1952
  });
1569
- (0, import_react8.useEffect)(() => {
1953
+ (0, import_react10.useEffect)(() => {
1570
1954
  if (!speech.isListening) setInterim("");
1571
1955
  }, [
1572
1956
  speech.isListening
@@ -1594,9 +1978,9 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
1594
1978
  const displayValue = speech.isListening && interim ? `${value}${value && !value.endsWith(" ") ? " " : ""}${interim}` : value;
1595
1979
  const showMic = enableSpeech && speech.isSupported;
1596
1980
  const showStop = isStreaming && onStop !== void 0;
1597
- return /* @__PURE__ */ import_react8.default.createElement("div", {
1981
+ return /* @__PURE__ */ import_react10.default.createElement("div", {
1598
1982
  className
1599
- }, /* @__PURE__ */ import_react8.default.createElement("textarea", {
1983
+ }, /* @__PURE__ */ import_react10.default.createElement("textarea", {
1600
1984
  className: textareaClassName,
1601
1985
  rows,
1602
1986
  value: displayValue,
@@ -1607,17 +1991,17 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
1607
1991
  }, "onChange"),
1608
1992
  onKeyDown: handleKeyDown,
1609
1993
  placeholder: placeholder ?? "Ask something (Enter to send, Shift+Enter for newline)"
1610
- }), /* @__PURE__ */ import_react8.default.createElement("button", {
1994
+ }), /* @__PURE__ */ import_react10.default.createElement("button", {
1611
1995
  type: "button",
1612
1996
  className: sendButtonClassName,
1613
1997
  onClick: submit,
1614
1998
  disabled: disabled || !value.trim()
1615
- }, sendLabel), showStop ? /* @__PURE__ */ import_react8.default.createElement("button", {
1999
+ }, sendLabel), showStop ? /* @__PURE__ */ import_react10.default.createElement("button", {
1616
2000
  type: "button",
1617
2001
  className: stopButtonClassName,
1618
2002
  onClick: /* @__PURE__ */ __name(() => void onStop(), "onClick"),
1619
2003
  "aria-label": "Stop generating"
1620
- }, stopLabel) : null, showMic ? /* @__PURE__ */ import_react8.default.createElement("button", {
2004
+ }, stopLabel) : null, showMic ? /* @__PURE__ */ import_react10.default.createElement("button", {
1621
2005
  type: "button",
1622
2006
  className: micButtonClassName,
1623
2007
  onClick: toggleMic,
@@ -1630,10 +2014,10 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
1630
2014
  __name(ChatInput, "ChatInput");
1631
2015
 
1632
2016
  // src/components/message-item.tsx
1633
- var import_react11 = __toESM(require("react"), 1);
2017
+ var import_react13 = __toESM(require("react"), 1);
1634
2018
 
1635
2019
  // src/transcript/use-chat-transcript.ts
1636
- var import_react10 = require("react");
2020
+ var import_react12 = require("react");
1637
2021
 
1638
2022
  // src/transcript/model.ts
1639
2023
  var import_nestjs_agent_core2 = require("@dudousxd/nestjs-agent-core");
@@ -2610,11 +2994,11 @@ function formatCostUsd(cost) {
2610
2994
  __name(formatCostUsd, "formatCostUsd");
2611
2995
 
2612
2996
  // src/transcript/use-stick-to-bottom.ts
2613
- var import_react9 = require("react");
2997
+ var import_react11 = require("react");
2614
2998
  function useStickToBottom({ contentKey, threshold = 32 }) {
2615
- const containerRef = (0, import_react9.useRef)(null);
2616
- const [isAtBottom, setIsAtBottom] = (0, import_react9.useState)(true);
2617
- const measure = (0, import_react9.useCallback)(() => {
2999
+ const containerRef = (0, import_react11.useRef)(null);
3000
+ const [isAtBottom, setIsAtBottom] = (0, import_react11.useState)(true);
3001
+ const measure = (0, import_react11.useCallback)(() => {
2618
3002
  const element = containerRef.current;
2619
3003
  if (!element) {
2620
3004
  return;
@@ -2624,7 +3008,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
2624
3008
  }, [
2625
3009
  threshold
2626
3010
  ]);
2627
- const scrollToBottom = (0, import_react9.useCallback)(() => {
3011
+ const scrollToBottom = (0, import_react11.useCallback)(() => {
2628
3012
  const element = containerRef.current;
2629
3013
  if (!element) {
2630
3014
  return;
@@ -2632,7 +3016,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
2632
3016
  element.scrollTop = element.scrollHeight;
2633
3017
  setIsAtBottom(true);
2634
3018
  }, []);
2635
- const setContainer = (0, import_react9.useCallback)((element) => {
3019
+ const setContainer = (0, import_react11.useCallback)((element) => {
2636
3020
  containerRef.current = element;
2637
3021
  if (element) {
2638
3022
  measure();
@@ -2640,7 +3024,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
2640
3024
  }, [
2641
3025
  measure
2642
3026
  ]);
2643
- (0, import_react9.useEffect)(() => {
3027
+ (0, import_react11.useEffect)(() => {
2644
3028
  if (isAtBottom) {
2645
3029
  scrollToBottom();
2646
3030
  }
@@ -2649,7 +3033,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
2649
3033
  isAtBottom,
2650
3034
  scrollToBottom
2651
3035
  ]);
2652
- const getContainerProps = (0, import_react9.useCallback)(() => ({
3036
+ const getContainerProps = (0, import_react11.useCallback)(() => ({
2653
3037
  ref: setContainer,
2654
3038
  onScroll: measure
2655
3039
  }), [
@@ -2671,14 +3055,14 @@ var DEFAULT_LOAD_EARLIER_STEP = 50;
2671
3055
  var DEFAULT_COPY_RESET_MS = 1500;
2672
3056
  function useChatTranscript(options) {
2673
3057
  const { messages, status, initialVisibleCount = DEFAULT_VISIBLE_COUNT, loadEarlierStep = DEFAULT_LOAD_EARLIER_STEP, followUps, stickToBottomThreshold } = options;
2674
- const latest = (0, import_react10.useRef)(options);
3058
+ const latest = (0, import_react12.useRef)(options);
2675
3059
  latest.current = options;
2676
- const [visibleCount, setVisibleCount] = (0, import_react10.useState)(initialVisibleCount);
2677
- const [stopRequested, setStopRequested] = (0, import_react10.useState)(false);
3060
+ const [visibleCount, setVisibleCount] = (0, import_react12.useState)(initialVisibleCount);
3061
+ const [stopRequested, setStopRequested] = (0, import_react12.useState)(false);
2678
3062
  const isStreaming = status === "streaming" || status === "reconnecting";
2679
3063
  const isBusy = status === "submitted" || isStreaming;
2680
3064
  const isEmpty = messages.length === 0;
2681
- (0, import_react10.useEffect)(() => {
3065
+ (0, import_react12.useEffect)(() => {
2682
3066
  if (!isBusy) {
2683
3067
  setStopRequested(false);
2684
3068
  }
@@ -2695,7 +3079,7 @@ function useChatTranscript(options) {
2695
3079
  streamingMessageId,
2696
3080
  options
2697
3081
  });
2698
- const stop = (0, import_react10.useCallback)(() => {
3082
+ const stop = (0, import_react12.useCallback)(() => {
2699
3083
  const current = latest.current;
2700
3084
  if (current.status !== "submitted" && current.status !== "streaming" && current.status !== "reconnecting") {
2701
3085
  return;
@@ -2703,8 +3087,8 @@ function useChatTranscript(options) {
2703
3087
  setStopRequested(true);
2704
3088
  void current.onStop?.();
2705
3089
  }, []);
2706
- const loadEarlier = (0, import_react10.useCallback)(() => setVisibleCount((count) => count + (latest.current.loadEarlierStep ?? DEFAULT_LOAD_EARLIER_STEP)), []);
2707
- const showAll = (0, import_react10.useCallback)(() => setVisibleCount(latest.current.messages.length), []);
3090
+ const loadEarlier = (0, import_react12.useCallback)(() => setVisibleCount((count) => count + (latest.current.loadEarlierStep ?? DEFAULT_LOAD_EARLIER_STEP)), []);
3091
+ const showAll = (0, import_react12.useCallback)(() => setVisibleCount(latest.current.messages.length), []);
2708
3092
  const scroll = useStickToBottom({
2709
3093
  contentKey: contentSignature(messages),
2710
3094
  ...stickToBottomThreshold !== void 0 ? {
@@ -2740,7 +3124,7 @@ function useChatTranscript(options) {
2740
3124
  __name(useChatTranscript, "useChatTranscript");
2741
3125
  function useTranscriptItem(options) {
2742
3126
  const { message, isStreaming = false } = options;
2743
- const messagesRef = (0, import_react10.useRef)([
3127
+ const messagesRef = (0, import_react12.useRef)([
2744
3128
  message
2745
3129
  ]);
2746
3130
  if (messagesRef.current[0] !== message) {
@@ -2758,13 +3142,13 @@ function useTranscriptItem(options) {
2758
3142
  }
2759
3143
  __name(useTranscriptItem, "useTranscriptItem");
2760
3144
  function useTranscriptItems({ messages, visibleStart, streamingMessageId, options }) {
2761
- const [drafts, setDrafts] = (0, import_react10.useState)(() => /* @__PURE__ */ new Map());
2762
- const [copied, setCopied] = (0, import_react10.useState)(() => /* @__PURE__ */ new Set());
2763
- const [openReasoning, setOpenReasoning] = (0, import_react10.useState)(() => /* @__PURE__ */ new Map());
2764
- const [picks, setPicks] = (0, import_react10.useState)(() => /* @__PURE__ */ new Map());
2765
- const [settling, setSettling] = (0, import_react10.useState)(() => /* @__PURE__ */ new Map());
2766
- const [settleErrors, setSettleErrors] = (0, import_react10.useState)(() => /* @__PURE__ */ new Map());
2767
- const latest = (0, import_react10.useRef)({
3145
+ const [drafts, setDrafts] = (0, import_react12.useState)(() => /* @__PURE__ */ new Map());
3146
+ const [copied, setCopied] = (0, import_react12.useState)(() => /* @__PURE__ */ new Set());
3147
+ const [openReasoning, setOpenReasoning] = (0, import_react12.useState)(() => /* @__PURE__ */ new Map());
3148
+ const [picks, setPicks] = (0, import_react12.useState)(() => /* @__PURE__ */ new Map());
3149
+ const [settling, setSettling] = (0, import_react12.useState)(() => /* @__PURE__ */ new Map());
3150
+ const [settleErrors, setSettleErrors] = (0, import_react12.useState)(() => /* @__PURE__ */ new Map());
3151
+ const latest = (0, import_react12.useRef)({
2768
3152
  options,
2769
3153
  drafts,
2770
3154
  picks,
@@ -2776,23 +3160,23 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2776
3160
  picks,
2777
3161
  messages
2778
3162
  };
2779
- const copyTimers = (0, import_react10.useRef)(/* @__PURE__ */ new Map());
2780
- (0, import_react10.useEffect)(() => () => {
3163
+ const copyTimers = (0, import_react12.useRef)(/* @__PURE__ */ new Map());
3164
+ (0, import_react12.useEffect)(() => () => {
2781
3165
  for (const timer of copyTimers.current.values()) {
2782
3166
  clearTimeout(timer);
2783
3167
  }
2784
3168
  copyTimers.current.clear();
2785
3169
  }, []);
2786
- const callbackCache = (0, import_react10.useRef)(/* @__PURE__ */ new Map());
2787
- const reasoningToggleCache = (0, import_react10.useRef)(/* @__PURE__ */ new Map());
2788
- const toggleReasoning = (0, import_react10.useCallback)((key, open) => {
3170
+ const callbackCache = (0, import_react12.useRef)(/* @__PURE__ */ new Map());
3171
+ const reasoningToggleCache = (0, import_react12.useRef)(/* @__PURE__ */ new Map());
3172
+ const toggleReasoning = (0, import_react12.useCallback)((key, open) => {
2789
3173
  setOpenReasoning((current) => {
2790
3174
  const next = new Map(current);
2791
3175
  next.set(key, open ?? !(current.get(key) ?? false));
2792
3176
  return next;
2793
3177
  });
2794
3178
  }, []);
2795
- const stableToggle = (0, import_react10.useCallback)((key) => {
3179
+ const stableToggle = (0, import_react12.useCallback)((key) => {
2796
3180
  let cached = reasoningToggleCache.current.get(key);
2797
3181
  if (!cached) {
2798
3182
  cached = /* @__PURE__ */ __name((open) => toggleReasoning(key, open), "cached");
@@ -2802,10 +3186,10 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2802
3186
  }, [
2803
3187
  toggleReasoning
2804
3188
  ]);
2805
- const pick = (0, import_react10.useCallback)((toolCallId, questionId, values) => {
3189
+ const pick = (0, import_react12.useCallback)((toolCallId, questionId, values) => {
2806
3190
  setPicks((current) => new Map(current).set(pickKey(toolCallId, questionId), values));
2807
3191
  }, []);
2808
- const settle = (0, import_react10.useCallback)((toolCallId, action, send) => {
3192
+ const settle = (0, import_react12.useCallback)((toolCallId, action, send) => {
2809
3193
  setSettleErrors((current) => {
2810
3194
  if (!current.has(toolCallId)) {
2811
3195
  return current;
@@ -2829,7 +3213,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2829
3213
  failed(error);
2830
3214
  }
2831
3215
  }, []);
2832
- const answer = (0, import_react10.useCallback)((toolCallId) => {
3216
+ const answer = (0, import_react12.useCallback)((toolCallId) => {
2833
3217
  const current = latest.current;
2834
3218
  const onAnswer = current.options.onAnswer;
2835
3219
  if (!onAnswer) {
@@ -2839,7 +3223,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2839
3223
  }, [
2840
3224
  settle
2841
3225
  ]);
2842
- const skip = (0, import_react10.useCallback)((toolCallId) => {
3226
+ const skip = (0, import_react12.useCallback)((toolCallId) => {
2843
3227
  const onSkip = latest.current.options.onSkip;
2844
3228
  if (onSkip) {
2845
3229
  settle(toolCallId, "skip", () => onSkip(toolCallId));
@@ -2847,7 +3231,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2847
3231
  }, [
2848
3232
  settle
2849
3233
  ]);
2850
- const approve = (0, import_react10.useCallback)((toolCallId, options2) => {
3234
+ const approve = (0, import_react12.useCallback)((toolCallId, options2) => {
2851
3235
  const onApprove = latest.current.options.onApprove;
2852
3236
  if (onApprove) {
2853
3237
  settle(toolCallId, "approve", () => options2 === void 0 ? onApprove(toolCallId) : onApprove(toolCallId, options2));
@@ -2855,7 +3239,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2855
3239
  }, [
2856
3240
  settle
2857
3241
  ]);
2858
- const reject = (0, import_react10.useCallback)((toolCallId) => {
3242
+ const reject = (0, import_react12.useCallback)((toolCallId) => {
2859
3243
  const onReject = latest.current.options.onReject;
2860
3244
  if (onReject) {
2861
3245
  settle(toolCallId, "reject", () => onReject(toolCallId));
@@ -2863,7 +3247,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2863
3247
  }, [
2864
3248
  settle
2865
3249
  ]);
2866
- const callbacksFor = (0, import_react10.useCallback)((id) => {
3250
+ const callbacksFor = (0, import_react12.useCallback)((id) => {
2867
3251
  const cached = callbackCache.current.get(id);
2868
3252
  if (cached) {
2869
3253
  return cached;
@@ -3105,7 +3489,7 @@ function MessageItem({ message, isStreaming = false, editable, onEditSubmit, reg
3105
3489
  getCreatedAt: /* @__PURE__ */ __name(() => createdAt, "getCreatedAt")
3106
3490
  } : {}
3107
3491
  });
3108
- return /* @__PURE__ */ import_react11.default.createElement(MessageItemView, {
3492
+ return /* @__PURE__ */ import_react13.default.createElement(MessageItemView, {
3109
3493
  item,
3110
3494
  ...slots
3111
3495
  });
@@ -3113,51 +3497,51 @@ function MessageItem({ message, isStreaming = false, editable, onEditSubmit, reg
3113
3497
  __name(MessageItem, "MessageItem");
3114
3498
  function MessageItemView({ item, ...slots }) {
3115
3499
  const { classNames } = slots;
3116
- const actionRow = /* @__PURE__ */ import_react11.default.createElement("div", {
3500
+ const actionRow = /* @__PURE__ */ import_react13.default.createElement("div", {
3117
3501
  className: classNames?.actions,
3118
3502
  "data-actions-for": item.role
3119
- }, item.copy.available ? /* @__PURE__ */ import_react11.default.createElement("button", {
3503
+ }, item.copy.available ? /* @__PURE__ */ import_react13.default.createElement("button", {
3120
3504
  type: "button",
3121
3505
  onClick: item.copy.copy,
3122
3506
  className: classNames?.actionButton,
3123
3507
  title: item.copy.copied ? "Copied!" : "Copy message text"
3124
- }, item.copy.copied ? "Copied" : "Copy") : null, item.fork.available ? /* @__PURE__ */ import_react11.default.createElement("button", {
3508
+ }, item.copy.copied ? "Copied" : "Copy") : null, item.fork.available ? /* @__PURE__ */ import_react13.default.createElement("button", {
3125
3509
  type: "button",
3126
3510
  onClick: item.fork.run,
3127
3511
  className: classNames?.actionButton,
3128
3512
  title: "Fork thread from this message"
3129
- }, "Fork") : null, item.regenerate.available ? /* @__PURE__ */ import_react11.default.createElement("button", {
3513
+ }, "Fork") : null, item.regenerate.available ? /* @__PURE__ */ import_react13.default.createElement("button", {
3130
3514
  type: "button",
3131
3515
  onClick: item.regenerate.run,
3132
3516
  className: classNames?.actionButton,
3133
3517
  title: "Regenerate response"
3134
- }, "Regenerate") : null, slots.meta, !item.isUser && item.usage ? /* @__PURE__ */ import_react11.default.createElement("span", {
3518
+ }, "Regenerate") : null, slots.meta, !item.isUser && item.usage ? /* @__PURE__ */ import_react13.default.createElement("span", {
3135
3519
  className: classNames?.usage,
3136
3520
  title: `Input ${item.usage.inputTokens.toLocaleString()} \xB7 Output ${item.usage.outputTokens.toLocaleString()} tokens`
3137
- }, item.usage.costLabel, " \xB7 ", item.usage.tokensLabel) : null, item.timestamp ? /* @__PURE__ */ import_react11.default.createElement("span", {
3521
+ }, item.usage.costLabel, " \xB7 ", item.usage.tokensLabel) : null, item.timestamp ? /* @__PURE__ */ import_react13.default.createElement("span", {
3138
3522
  className: classNames?.timestamp,
3139
3523
  title: item.timestamp.absolute
3140
3524
  }, item.timestamp.relative) : null);
3141
3525
  if (item.edit.available) {
3142
- return /* @__PURE__ */ import_react11.default.createElement(EditableUserBubble, {
3526
+ return /* @__PURE__ */ import_react13.default.createElement(EditableUserBubble, {
3143
3527
  item,
3144
3528
  slots,
3145
3529
  actions: actionRow
3146
3530
  });
3147
3531
  }
3148
3532
  const className = joinClasses(classNames?.root, classNames?.byRole?.[item.role]);
3149
- return /* @__PURE__ */ import_react11.default.createElement("div", {
3533
+ return /* @__PURE__ */ import_react13.default.createElement("div", {
3150
3534
  className,
3151
3535
  "data-role": item.role,
3152
3536
  "data-streaming": item.isStreaming ? "true" : void 0
3153
- }, /* @__PURE__ */ import_react11.default.createElement("div", null, renderBlocks(item, slots)), actionRow);
3537
+ }, /* @__PURE__ */ import_react13.default.createElement("div", null, renderBlocks(item, slots)), actionRow);
3154
3538
  }
3155
3539
  __name(MessageItemView, "MessageItemView");
3156
3540
  function renderBlocks(item, slots) {
3157
3541
  const nodes = [];
3158
3542
  for (const block of item.blocks) {
3159
3543
  if (block.kind === "text") {
3160
- nodes.push(/* @__PURE__ */ import_react11.default.createElement("div", {
3544
+ nodes.push(/* @__PURE__ */ import_react13.default.createElement("div", {
3161
3545
  key: block.key,
3162
3546
  className: slots.classNames?.text
3163
3547
  }, slots.renderText ? slots.renderText(block.text, {
@@ -3178,7 +3562,7 @@ function renderBlocks(item, slots) {
3178
3562
  continue;
3179
3563
  }
3180
3564
  if (block.kind === "ui" && slots.renderUi) {
3181
- nodes.push(/* @__PURE__ */ import_react11.default.createElement("div", {
3565
+ nodes.push(/* @__PURE__ */ import_react13.default.createElement("div", {
3182
3566
  key: block.key,
3183
3567
  "data-slot": "ui",
3184
3568
  "data-component": block.component
@@ -3189,18 +3573,18 @@ function renderBlocks(item, slots) {
3189
3573
  }
3190
3574
  __name(renderBlocks, "renderBlocks");
3191
3575
  function renderFilesBlock(block, slots) {
3192
- return /* @__PURE__ */ import_react11.default.createElement("div", {
3576
+ return /* @__PURE__ */ import_react13.default.createElement("div", {
3193
3577
  key: block.key,
3194
3578
  className: slots.classNames?.files,
3195
3579
  "data-files": "true"
3196
- }, slots.renderFiles ? slots.renderFiles(block.files) : block.files.map((file) => /* @__PURE__ */ import_react11.default.createElement("a", {
3580
+ }, slots.renderFiles ? slots.renderFiles(block.files) : block.files.map((file) => /* @__PURE__ */ import_react13.default.createElement("a", {
3197
3581
  key: file.url,
3198
3582
  href: file.url,
3199
3583
  target: "_blank",
3200
3584
  rel: "noreferrer",
3201
3585
  className: slots.classNames?.file,
3202
3586
  title: file.filename ?? file.mediaType
3203
- }, file.isImage ? /* @__PURE__ */ import_react11.default.createElement("img", {
3587
+ }, file.isImage ? /* @__PURE__ */ import_react13.default.createElement("img", {
3204
3588
  src: file.url,
3205
3589
  alt: file.filename ?? "image",
3206
3590
  className: slots.classNames?.fileImage
@@ -3208,17 +3592,17 @@ function renderFilesBlock(block, slots) {
3208
3592
  }
3209
3593
  __name(renderFilesBlock, "renderFilesBlock");
3210
3594
  function renderReasoningBlock(block, slots) {
3211
- return /* @__PURE__ */ import_react11.default.createElement("div", {
3595
+ return /* @__PURE__ */ import_react13.default.createElement("div", {
3212
3596
  key: block.key,
3213
3597
  className: slots.classNames?.reasoning,
3214
3598
  "data-reasoning": "true",
3215
3599
  "data-streaming": block.isStreaming ? "true" : void 0
3216
- }, /* @__PURE__ */ import_react11.default.createElement("button", {
3600
+ }, /* @__PURE__ */ import_react13.default.createElement("button", {
3217
3601
  type: "button",
3218
3602
  className: slots.classNames?.reasoningToggle,
3219
3603
  onClick: /* @__PURE__ */ __name(() => block.toggle(), "onClick"),
3220
3604
  "aria-expanded": block.isOpen
3221
- }, slots.reasoningLabel ?? "Reasoning"), block.isOpen ? /* @__PURE__ */ import_react11.default.createElement("div", {
3605
+ }, slots.reasoningLabel ?? "Reasoning"), block.isOpen ? /* @__PURE__ */ import_react13.default.createElement("div", {
3222
3606
  className: slots.classNames?.reasoningText
3223
3607
  }, slots.renderReasoning ? slots.renderReasoning(block.text, {
3224
3608
  isStreaming: block.isStreaming,
@@ -3229,7 +3613,7 @@ __name(renderReasoningBlock, "renderReasoningBlock");
3229
3613
  function renderToolBlock(item, block, slots) {
3230
3614
  if (slots.renderToolGroup) {
3231
3615
  return [
3232
- /* @__PURE__ */ import_react11.default.createElement(import_react11.default.Fragment, {
3616
+ /* @__PURE__ */ import_react13.default.createElement(import_react13.default.Fragment, {
3233
3617
  key: block.key
3234
3618
  }, slots.renderToolGroup(block.parts, block.key))
3235
3619
  ];
@@ -3240,7 +3624,7 @@ function renderToolBlock(item, block, slots) {
3240
3624
  const renderToolPart = slots.renderToolPart;
3241
3625
  return block.parts.map((part) => {
3242
3626
  const partKey = `${item.id}-${part.toolCallId}`;
3243
- return /* @__PURE__ */ import_react11.default.createElement(import_react11.default.Fragment, {
3627
+ return /* @__PURE__ */ import_react13.default.createElement(import_react13.default.Fragment, {
3244
3628
  key: partKey
3245
3629
  }, renderToolPart(part, partKey));
3246
3630
  });
@@ -3250,14 +3634,14 @@ function EditableUserBubble({ item, slots, actions }) {
3250
3634
  const { classNames } = slots;
3251
3635
  const { edit } = item;
3252
3636
  if (!edit.isEditing) {
3253
- return /* @__PURE__ */ import_react11.default.createElement("div", {
3637
+ return /* @__PURE__ */ import_react13.default.createElement("div", {
3254
3638
  className: joinClasses(classNames?.root, classNames?.byRole?.user),
3255
3639
  "data-role": "user"
3256
- }, /* @__PURE__ */ import_react11.default.createElement("div", {
3640
+ }, /* @__PURE__ */ import_react13.default.createElement("div", {
3257
3641
  className: classNames?.text
3258
3642
  }, slots.renderText ? slots.renderText(item.text, {
3259
3643
  isStreaming: false
3260
- }) : item.text), /* @__PURE__ */ import_react11.default.createElement("button", {
3644
+ }) : item.text), /* @__PURE__ */ import_react13.default.createElement("button", {
3261
3645
  type: "button",
3262
3646
  onClick: edit.start,
3263
3647
  className: classNames?.actionButton,
@@ -3265,18 +3649,18 @@ function EditableUserBubble({ item, slots, actions }) {
3265
3649
  }, "Edit"), actions);
3266
3650
  }
3267
3651
  const textareaProps = edit.getTextareaProps();
3268
- return /* @__PURE__ */ import_react11.default.createElement("div", {
3652
+ return /* @__PURE__ */ import_react13.default.createElement("div", {
3269
3653
  "data-editing": "true"
3270
- }, /* @__PURE__ */ import_react11.default.createElement("textarea", {
3654
+ }, /* @__PURE__ */ import_react13.default.createElement("textarea", {
3271
3655
  ...textareaProps,
3272
3656
  rows: Math.min(8, edit.draft.split("\n").length + 1),
3273
3657
  className: classNames?.editTextarea
3274
- }), /* @__PURE__ */ import_react11.default.createElement("div", {
3658
+ }), /* @__PURE__ */ import_react13.default.createElement("div", {
3275
3659
  className: classNames?.editActions
3276
- }, /* @__PURE__ */ import_react11.default.createElement("button", {
3660
+ }, /* @__PURE__ */ import_react13.default.createElement("button", {
3277
3661
  type: "button",
3278
3662
  onClick: edit.cancel
3279
- }, "Cancel"), /* @__PURE__ */ import_react11.default.createElement("button", {
3663
+ }, "Cancel"), /* @__PURE__ */ import_react13.default.createElement("button", {
3280
3664
  type: "button",
3281
3665
  onClick: edit.save,
3282
3666
  disabled: !edit.canSave
@@ -3520,35 +3904,35 @@ function toItem(entry) {
3520
3904
  __name(toItem, "toItem");
3521
3905
 
3522
3906
  // src/composer/use-composer-autocomplete.ts
3523
- var import_react12 = require("react");
3907
+ var import_react14 = require("react");
3524
3908
  function useComposerAutocomplete(options) {
3525
3909
  const { value, sources, acceptOnTab = true } = options;
3526
- const latest = (0, import_react12.useRef)(options);
3910
+ const latest = (0, import_react14.useRef)(options);
3527
3911
  latest.current = options;
3528
- const inputRef = (0, import_react12.useRef)(null);
3529
- const [caret, setCaret] = (0, import_react12.useState)(value.length);
3530
- const [items, setItems] = (0, import_react12.useState)([]);
3531
- const [isLoading, setLoading] = (0, import_react12.useState)(false);
3532
- const [error, setError] = (0, import_react12.useState)(null);
3533
- const [activeIndex, setActiveIndex] = (0, import_react12.useState)(0);
3534
- const [dismissedIndex, setDismissedIndex] = (0, import_react12.useState)(null);
3912
+ const inputRef = (0, import_react14.useRef)(null);
3913
+ const [caret, setCaret] = (0, import_react14.useState)(value.length);
3914
+ const [items, setItems] = (0, import_react14.useState)([]);
3915
+ const [isLoading, setLoading] = (0, import_react14.useState)(false);
3916
+ const [error, setError] = (0, import_react14.useState)(null);
3917
+ const [activeIndex, setActiveIndex] = (0, import_react14.useState)(0);
3918
+ const [dismissedIndex, setDismissedIndex] = (0, import_react14.useState)(null);
3535
3919
  const safeCaret = Math.min(caret, value.length);
3536
- const match = (0, import_react12.useMemo)(() => findActiveTrigger(value, safeCaret, sources), [
3920
+ const match = (0, import_react14.useMemo)(() => findActiveTrigger(value, safeCaret, sources), [
3537
3921
  value,
3538
3922
  safeCaret,
3539
3923
  sources
3540
3924
  ]);
3541
3925
  const isActive = match !== null && dismissedIndex !== match.index;
3542
- const matchRef = (0, import_react12.useRef)(match);
3926
+ const matchRef = (0, import_react14.useRef)(match);
3543
3927
  matchRef.current = match;
3544
- const caretRef = (0, import_react12.useRef)(safeCaret);
3928
+ const caretRef = (0, import_react14.useRef)(safeCaret);
3545
3929
  caretRef.current = safeCaret;
3546
- const itemsRef = (0, import_react12.useRef)(items);
3930
+ const itemsRef = (0, import_react14.useRef)(items);
3547
3931
  itemsRef.current = items;
3548
- const activeIndexRef = (0, import_react12.useRef)(activeIndex);
3932
+ const activeIndexRef = (0, import_react14.useRef)(activeIndex);
3549
3933
  activeIndexRef.current = activeIndex;
3550
3934
  const requestKey = isActive && match ? `${match.source.id}\0${match.query}` : null;
3551
- (0, import_react12.useEffect)(() => {
3935
+ (0, import_react14.useEffect)(() => {
3552
3936
  const current = matchRef.current;
3553
3937
  if (requestKey === null || current === null) {
3554
3938
  setItems([]);
@@ -3596,25 +3980,25 @@ function useComposerAutocomplete(options) {
3596
3980
  ]);
3597
3981
  const isOpen = isActive;
3598
3982
  const activeItem = isOpen ? items[activeIndex] ?? null : null;
3599
- const reactId = (0, import_react12.useId)();
3983
+ const reactId = (0, import_react14.useId)();
3600
3984
  const listboxId = options.id ?? `agent-autocomplete-${reactId}`;
3601
- const optionId = (0, import_react12.useCallback)((index) => `${listboxId}-option-${index}`, [
3985
+ const optionId = (0, import_react14.useCallback)((index) => `${listboxId}-option-${index}`, [
3602
3986
  listboxId
3603
3987
  ]);
3604
- const highlight = (0, import_react12.useCallback)((index) => {
3988
+ const highlight = (0, import_react14.useCallback)((index) => {
3605
3989
  const total = itemsRef.current.length;
3606
3990
  if (total === 0) return;
3607
3991
  setActiveIndex((index % total + total) % total);
3608
3992
  }, []);
3609
- const moveHighlight = (0, import_react12.useCallback)((delta) => highlight(activeIndexRef.current + delta), [
3993
+ const moveHighlight = (0, import_react14.useCallback)((delta) => highlight(activeIndexRef.current + delta), [
3610
3994
  highlight
3611
3995
  ]);
3612
- const dismiss = (0, import_react12.useCallback)(() => {
3996
+ const dismiss = (0, import_react14.useCallback)(() => {
3613
3997
  const current = matchRef.current;
3614
3998
  if (current) setDismissedIndex(current.index);
3615
3999
  }, []);
3616
- const pendingCaret = (0, import_react12.useRef)(null);
3617
- const accept = (0, import_react12.useCallback)((item) => {
4000
+ const pendingCaret = (0, import_react14.useRef)(null);
4001
+ const accept = (0, import_react14.useCallback)((item) => {
3618
4002
  const current = matchRef.current;
3619
4003
  if (!current) return;
3620
4004
  const chosen = item ?? itemsRef.current[activeIndexRef.current];
@@ -3625,16 +4009,16 @@ function useComposerAutocomplete(options) {
3625
4009
  setDismissedIndex(null);
3626
4010
  pendingCaret.current = edit.caret;
3627
4011
  }, []);
3628
- (0, import_react12.useLayoutEffect)(() => {
4012
+ (0, import_react14.useLayoutEffect)(() => {
3629
4013
  const position = pendingCaret.current;
3630
4014
  if (position === null) return;
3631
4015
  pendingCaret.current = null;
3632
4016
  inputRef.current?.setSelectionRange(position, position);
3633
4017
  });
3634
- const readCaret = (0, import_react12.useCallback)((element) => {
4018
+ const readCaret = (0, import_react14.useCallback)((element) => {
3635
4019
  setCaret(element.selectionStart ?? element.value.length);
3636
4020
  }, []);
3637
- const onKeyDown = (0, import_react12.useCallback)((event) => {
4021
+ const onKeyDown = (0, import_react14.useCallback)((event) => {
3638
4022
  if (!isOpen) return;
3639
4023
  const hasItems = itemsRef.current.length > 0;
3640
4024
  switch (event.key) {
@@ -3672,23 +4056,23 @@ function useComposerAutocomplete(options) {
3672
4056
  dismiss,
3673
4057
  moveHighlight
3674
4058
  ]);
3675
- const onChange = (0, import_react12.useCallback)((event) => {
4059
+ const onChange = (0, import_react14.useCallback)((event) => {
3676
4060
  readCaret(event.target);
3677
4061
  const next = findActiveTrigger(event.target.value, event.target.selectionStart ?? event.target.value.length, latest.current.sources);
3678
4062
  setDismissedIndex((current) => current !== null && next?.index === current ? current : null);
3679
4063
  }, [
3680
4064
  readCaret
3681
4065
  ]);
3682
- const onSelect = (0, import_react12.useCallback)((event) => readCaret(event.currentTarget), [
4066
+ const onSelect = (0, import_react14.useCallback)((event) => readCaret(event.currentTarget), [
3683
4067
  readCaret
3684
4068
  ]);
3685
- const onClick = (0, import_react12.useCallback)((event) => readCaret(event.currentTarget), [
4069
+ const onClick = (0, import_react14.useCallback)((event) => readCaret(event.currentTarget), [
3686
4070
  readCaret
3687
4071
  ]);
3688
- const setInput = (0, import_react12.useCallback)((element) => {
4072
+ const setInput = (0, import_react14.useCallback)((element) => {
3689
4073
  inputRef.current = element;
3690
4074
  }, []);
3691
- const getInputProps = (0, import_react12.useCallback)(() => ({
4075
+ const getInputProps = (0, import_react14.useCallback)(() => ({
3692
4076
  ref: setInput,
3693
4077
  onChange,
3694
4078
  onKeyDown,
@@ -3716,7 +4100,7 @@ function useComposerAutocomplete(options) {
3716
4100
  activeIndex,
3717
4101
  optionId
3718
4102
  ]);
3719
- const getListboxProps = (0, import_react12.useCallback)(() => ({
4103
+ const getListboxProps = (0, import_react14.useCallback)(() => ({
3720
4104
  id: listboxId,
3721
4105
  role: "listbox",
3722
4106
  "aria-label": match?.source.label ?? match?.source.id ?? "Suggestions"
@@ -3724,7 +4108,7 @@ function useComposerAutocomplete(options) {
3724
4108
  listboxId,
3725
4109
  match
3726
4110
  ]);
3727
- const getOptionProps = (0, import_react12.useCallback)((index) => ({
4111
+ const getOptionProps = (0, import_react14.useCallback)((index) => ({
3728
4112
  id: optionId(index),
3729
4113
  role: "option",
3730
4114
  "aria-selected": index === activeIndex
@@ -3758,7 +4142,7 @@ function isPromise(value) {
3758
4142
  __name(isPromise, "isPromise");
3759
4143
 
3760
4144
  // src/presentation/use-tool-catalog.ts
3761
- var import_react13 = require("react");
4145
+ var import_react15 = require("react");
3762
4146
  var shared = /* @__PURE__ */ new WeakMap();
3763
4147
  function load(client, agent) {
3764
4148
  let byAgent = shared.get(client);
@@ -3782,11 +4166,11 @@ function load(client, agent) {
3782
4166
  __name(load, "load");
3783
4167
  function useToolCatalog(options) {
3784
4168
  const { client, agent, enabled = true } = options;
3785
- const [entries, setEntries] = (0, import_react13.useState)([]);
3786
- const [isLoading, setLoading] = (0, import_react13.useState)(enabled);
3787
- const [error, setError] = (0, import_react13.useState)(null);
3788
- const [generation, setGeneration] = (0, import_react13.useState)(0);
3789
- (0, import_react13.useEffect)(() => {
4169
+ const [entries, setEntries] = (0, import_react15.useState)([]);
4170
+ const [isLoading, setLoading] = (0, import_react15.useState)(enabled);
4171
+ const [error, setError] = (0, import_react15.useState)(null);
4172
+ const [generation, setGeneration] = (0, import_react15.useState)(0);
4173
+ (0, import_react15.useEffect)(() => {
3790
4174
  if (!enabled) {
3791
4175
  setLoading(false);
3792
4176
  return;
@@ -3812,14 +4196,14 @@ function useToolCatalog(options) {
3812
4196
  enabled,
3813
4197
  generation
3814
4198
  ]);
3815
- const refresh = (0, import_react13.useCallback)(() => {
4199
+ const refresh = (0, import_react15.useCallback)(() => {
3816
4200
  shared.get(client)?.delete(agent ?? "");
3817
4201
  setGeneration((current) => current + 1);
3818
4202
  }, [
3819
4203
  client,
3820
4204
  agent
3821
4205
  ]);
3822
- const catalog = (0, import_react13.useMemo)(() => toolCatalogFrom(entries), [
4206
+ const catalog = (0, import_react15.useMemo)(() => toolCatalogFrom(entries), [
3823
4207
  entries
3824
4208
  ]);
3825
4209
  return {
@@ -3833,7 +4217,7 @@ function useToolCatalog(options) {
3833
4217
  __name(useToolCatalog, "useToolCatalog");
3834
4218
 
3835
4219
  // src/approvals/countdown.ts
3836
- var import_react14 = require("react");
4220
+ var import_react16 = require("react");
3837
4221
  function approvalCountdown(expiresAt, now = Date.now()) {
3838
4222
  const deadline = expiresAt == null ? Number.NaN : Date.parse(expiresAt);
3839
4223
  if (!Number.isFinite(deadline)) {
@@ -3851,10 +4235,10 @@ function approvalCountdown(expiresAt, now = Date.now()) {
3851
4235
  __name(approvalCountdown, "approvalCountdown");
3852
4236
  function useApprovalCountdown(expiresAt, options = {}) {
3853
4237
  const { intervalMs = 1e3 } = options;
3854
- const nowRef = (0, import_react14.useRef)(options.now ?? Date.now);
4238
+ const nowRef = (0, import_react16.useRef)(options.now ?? Date.now);
3855
4239
  nowRef.current = options.now ?? Date.now;
3856
- const [state, setState] = (0, import_react14.useState)(() => approvalCountdown(expiresAt, nowRef.current()));
3857
- (0, import_react14.useEffect)(() => {
4240
+ const [state, setState] = (0, import_react16.useState)(() => approvalCountdown(expiresAt, nowRef.current()));
4241
+ (0, import_react16.useEffect)(() => {
3858
4242
  const read = /* @__PURE__ */ __name(() => approvalCountdown(expiresAt, nowRef.current()), "read");
3859
4243
  const first = read();
3860
4244
  setState(first);
@@ -3917,7 +4301,13 @@ function storedMessageToUiMessage(message) {
3917
4301
  type: "file",
3918
4302
  mediaType: attachment.contentType,
3919
4303
  filename: attachment.name,
3920
- url: attachment.url
4304
+ url: attachment.url,
4305
+ // The stored id rides along, so a replayed file can be referenced again (`messageFiles`).
4306
+ providerMetadata: {
4307
+ agent: {
4308
+ mediaId: attachment.mediaId
4309
+ }
4310
+ }
3921
4311
  });
3922
4312
  }
3923
4313
  const toolCallIds = new Set((message.toolCalls ?? []).map((call) => call.id));
@@ -4095,21 +4485,21 @@ function sumUsage(rows) {
4095
4485
  __name(sumUsage, "sumUsage");
4096
4486
 
4097
4487
  // src/use-agent-chat.ts
4098
- var import_react15 = require("@ai-sdk/react");
4099
- var import_react16 = require("react");
4488
+ var import_react17 = require("@ai-sdk/react");
4489
+ var import_react18 = require("react");
4100
4490
  function useAgentChat(options) {
4101
- const latest = (0, import_react16.useRef)(options);
4491
+ const latest = (0, import_react18.useRef)(options);
4102
4492
  latest.current = options;
4103
- const [runId, setRunId] = (0, import_react16.useState)(options.resumeRunId);
4104
- const runIdRef = (0, import_react16.useRef)(runId);
4493
+ const [runId, setRunId] = (0, import_react18.useState)(options.resumeRunId);
4494
+ const runIdRef = (0, import_react18.useRef)(runId);
4105
4495
  runIdRef.current = runId;
4106
- const settlingRunIdRef = (0, import_react16.useRef)(void 0);
4107
- const autoResumeRunIdRef = (0, import_react16.useRef)(void 0);
4108
- const [autoResume, setAutoResume] = (0, import_react16.useState)(false);
4109
- const [activeRunId, setActiveRunId] = (0, import_react16.useState)(null);
4110
- const regenerateNext = (0, import_react16.useRef)(false);
4111
- const createdThreadId = (0, import_react16.useRef)(void 0);
4112
- const client = (0, import_react16.useMemo)(() => {
4496
+ const settlingRunIdRef = (0, import_react18.useRef)(void 0);
4497
+ const autoResumeRunIdRef = (0, import_react18.useRef)(void 0);
4498
+ const [autoResume, setAutoResume] = (0, import_react18.useState)(false);
4499
+ const [activeRunId, setActiveRunId] = (0, import_react18.useState)(null);
4500
+ const regenerateNext = (0, import_react18.useRef)(false);
4501
+ const createdThreadId = (0, import_react18.useRef)(void 0);
4502
+ const client = (0, import_react18.useMemo)(() => {
4113
4503
  if (options.backend) return options.backend;
4114
4504
  if (options.client) return options.client;
4115
4505
  return new AgentClient({
@@ -4125,11 +4515,11 @@ function useAgentChat(options) {
4125
4515
  getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders")
4126
4516
  });
4127
4517
  }, []);
4128
- const [connection, setConnection] = (0, import_react16.useState)({
4518
+ const [connection, setConnection] = (0, import_react18.useState)({
4129
4519
  status: "live"
4130
4520
  });
4131
- const resyncAfterSettle = (0, import_react16.useRef)(false);
4132
- (0, import_react16.useEffect)(() => {
4521
+ const resyncAfterSettle = (0, import_react18.useRef)(false);
4522
+ (0, import_react18.useEffect)(() => {
4133
4523
  if (!options.resume || options.threadId === void 0) return;
4134
4524
  let cancelled = false;
4135
4525
  const threadId = options.threadId;
@@ -4149,7 +4539,7 @@ function useAgentChat(options) {
4149
4539
  options.resume,
4150
4540
  options.threadId
4151
4541
  ]);
4152
- const transport = (0, import_react16.useMemo)(() => {
4542
+ const transport = (0, import_react18.useMemo)(() => {
4153
4543
  function onMeta(meta) {
4154
4544
  settlingRunIdRef.current = meta.runId;
4155
4545
  setRunId(meta.runId);
@@ -4204,8 +4594,8 @@ function useAgentChat(options) {
4204
4594
  }, "onAttemptStart")
4205
4595
  });
4206
4596
  }, []);
4207
- const backgroundRef = (0, import_react16.useRef)(() => Promise.resolve());
4208
- const chat = (0, import_react15.useChat)({
4597
+ const backgroundRef = (0, import_react18.useRef)(() => Promise.resolve());
4598
+ const chat = (0, import_react17.useChat)({
4209
4599
  transport,
4210
4600
  resume: options.resumeRunId !== void 0 || autoResume,
4211
4601
  ...options.threadId !== void 0 ? {
@@ -4253,7 +4643,7 @@ function useAgentChat(options) {
4253
4643
  }
4254
4644
  }, "onFinish")
4255
4645
  });
4256
- const chatRef = (0, import_react16.useRef)(chat);
4646
+ const chatRef = (0, import_react18.useRef)(chat);
4257
4647
  chatRef.current = chat;
4258
4648
  async function resyncFromThread() {
4259
4649
  const threadId = latest.current.threadId ?? createdThreadId.current;
@@ -4265,11 +4655,13 @@ function useAgentChat(options) {
4265
4655
  }
4266
4656
  }
4267
4657
  __name(resyncFromThread, "resyncFromThread");
4268
- const turnInFlight = (0, import_react16.useRef)(false);
4269
- const isTurnInFlight = (0, import_react16.useCallback)(() => turnInFlight.current || transport.isAttemptLive, [
4658
+ const turnInFlight = (0, import_react18.useRef)(false);
4659
+ const isTurnInFlight = (0, import_react18.useCallback)(() => turnInFlight.current || transport.isAttemptLive, [
4270
4660
  transport
4271
4661
  ]);
4272
- const sendMessage = (0, import_react16.useCallback)(async (...args) => {
4662
+ const sendMessage = (0, import_react18.useCallback)(async (...args) => {
4663
+ const blocked = latest.current.blocked;
4664
+ if (blocked != null) throw new QuotaBlockedError(blocked);
4273
4665
  if (isTurnInFlight()) return;
4274
4666
  turnInFlight.current = true;
4275
4667
  try {
@@ -4280,15 +4672,15 @@ function useAgentChat(options) {
4280
4672
  }, [
4281
4673
  isTurnInFlight
4282
4674
  ]);
4283
- const addToolResult = (0, import_react16.useCallback)(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
4675
+ const addToolResult = (0, import_react18.useCallback)(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
4284
4676
  tool,
4285
4677
  toolCallId,
4286
4678
  output
4287
4679
  }), []);
4288
- const [threads, setThreads] = (0, import_react16.useState)([]);
4289
- const [quota, setQuota] = (0, import_react16.useState)(null);
4290
- const [backgroundRuns, setBackgroundRuns] = (0, import_react16.useState)([]);
4291
- const refreshBackground = (0, import_react16.useCallback)(async () => {
4680
+ const [threads, setThreads] = (0, import_react18.useState)([]);
4681
+ const [quota, setQuota] = (0, import_react18.useState)(null);
4682
+ const [backgroundRuns, setBackgroundRuns] = (0, import_react18.useState)([]);
4683
+ const refreshBackground = (0, import_react18.useCallback)(async () => {
4292
4684
  const threadId = latest.current.threadId ?? createdThreadId.current;
4293
4685
  if (threadId === void 0) {
4294
4686
  return;
@@ -4313,7 +4705,7 @@ function useAgentChat(options) {
4313
4705
  ]);
4314
4706
  backgroundRef.current = refreshBackground;
4315
4707
  const isWorking = backgroundRuns.some((run) => run.status === "running");
4316
- (0, import_react16.useEffect)(() => {
4708
+ (0, import_react18.useEffect)(() => {
4317
4709
  if (options.background !== true || options.threadId === void 0) {
4318
4710
  return;
4319
4711
  }
@@ -4323,7 +4715,7 @@ function useAgentChat(options) {
4323
4715
  options.threadId,
4324
4716
  refreshBackground
4325
4717
  ]);
4326
- (0, import_react16.useEffect)(() => {
4718
+ (0, import_react18.useEffect)(() => {
4327
4719
  const every = latest.current.backgroundPollMs ?? 5e3;
4328
4720
  if (latest.current.background !== true || !isWorking || every <= 0) {
4329
4721
  return;
@@ -4334,26 +4726,26 @@ function useAgentChat(options) {
4334
4726
  isWorking,
4335
4727
  refreshBackground
4336
4728
  ]);
4337
- const loadThreads = (0, import_react16.useCallback)(async () => {
4729
+ const loadThreads = (0, import_react18.useCallback)(async () => {
4338
4730
  const list = await client.listThreads();
4339
4731
  setThreads(list);
4340
4732
  return list;
4341
4733
  }, [
4342
4734
  client
4343
4735
  ]);
4344
- const loadThread = (0, import_react16.useCallback)((id) => client.getThread(id), [
4736
+ const loadThread = (0, import_react18.useCallback)((id) => client.getThread(id), [
4345
4737
  client
4346
4738
  ]);
4347
- const deleteThread = (0, import_react16.useCallback)(async (id) => {
4739
+ const deleteThread = (0, import_react18.useCallback)(async (id) => {
4348
4740
  await client.deleteThread(id);
4349
4741
  setThreads((current) => current.filter((thread) => thread.id !== id));
4350
4742
  }, [
4351
4743
  client
4352
4744
  ]);
4353
- const forkThread = (0, import_react16.useCallback)((threadId, messageId) => requireBackendMethod(client, "forkFromMessage")(threadId, messageId), [
4745
+ const forkThread = (0, import_react18.useCallback)((threadId, messageId) => requireBackendMethod(client, "forkFromMessage")(threadId, messageId), [
4354
4746
  client
4355
4747
  ]);
4356
- const renameThread = (0, import_react16.useCallback)(async (id, title) => {
4748
+ const renameThread = (0, import_react18.useCallback)(async (id, title) => {
4357
4749
  await client.updateThread(id, {
4358
4750
  title
4359
4751
  });
@@ -4364,24 +4756,24 @@ function useAgentChat(options) {
4364
4756
  }, [
4365
4757
  client
4366
4758
  ]);
4367
- const promoteThread = (0, import_react16.useCallback)(async (id) => {
4759
+ const promoteThread = (0, import_react18.useCallback)(async (id) => {
4368
4760
  await requireBackendMethod(client, "promoteThread")(id);
4369
4761
  await loadThreads();
4370
4762
  }, [
4371
4763
  client,
4372
4764
  loadThreads
4373
4765
  ]);
4374
- const truncateFromMessage = (0, import_react16.useCallback)((threadId, messageId) => requireBackendMethod(client, "truncateFromMessage")(threadId, messageId).then(() => void 0), [
4766
+ const truncateFromMessage = (0, import_react18.useCallback)((threadId, messageId) => requireBackendMethod(client, "truncateFromMessage")(threadId, messageId).then(() => void 0), [
4375
4767
  client
4376
4768
  ]);
4377
- const loadQuota = (0, import_react16.useCallback)(async () => {
4769
+ const loadQuota = (0, import_react18.useCallback)(async () => {
4378
4770
  const today = await requireBackendMethod(client, "getQuotaToday")();
4379
4771
  setQuota(today);
4380
4772
  return today;
4381
4773
  }, [
4382
4774
  client
4383
4775
  ]);
4384
- const cancel = (0, import_react16.useCallback)(async () => {
4776
+ const cancel = (0, import_react18.useCallback)(async () => {
4385
4777
  chatRef.current.stop();
4386
4778
  const activeRunId2 = runIdRef.current;
4387
4779
  if (activeRunId2) {
@@ -4393,7 +4785,7 @@ function useAgentChat(options) {
4393
4785
  }, [
4394
4786
  client
4395
4787
  ]);
4396
- const approve = (0, import_react16.useCallback)(async ({ toolCallId, remember, via }) => {
4788
+ const approve = (0, import_react18.useCallback)(async ({ toolCallId, remember, via }) => {
4397
4789
  await requireBackendMethod(client, "approveToolCall")({
4398
4790
  toolCallId,
4399
4791
  ...remember === true ? {
@@ -4406,7 +4798,7 @@ function useAgentChat(options) {
4406
4798
  }, [
4407
4799
  client
4408
4800
  ]);
4409
- const reject = (0, import_react16.useCallback)(async ({ toolCallId, reason, via }) => {
4801
+ const reject = (0, import_react18.useCallback)(async ({ toolCallId, reason, via }) => {
4410
4802
  await requireBackendMethod(client, "rejectToolCall")({
4411
4803
  toolCallId,
4412
4804
  ...reason !== void 0 ? {
@@ -4419,7 +4811,7 @@ function useAgentChat(options) {
4419
4811
  }, [
4420
4812
  client
4421
4813
  ]);
4422
- const answer = (0, import_react16.useCallback)(async ({ toolCallId, answers }) => {
4814
+ const answer = (0, import_react18.useCallback)(async ({ toolCallId, answers }) => {
4423
4815
  await requireBackendMethod(client, "answerToolCall")({
4424
4816
  toolCallId,
4425
4817
  ...answers !== void 0 ? {
@@ -4429,14 +4821,16 @@ function useAgentChat(options) {
4429
4821
  }, [
4430
4822
  client
4431
4823
  ]);
4432
- const skip = (0, import_react16.useCallback)(async ({ toolCallId }) => {
4824
+ const skip = (0, import_react18.useCallback)(async ({ toolCallId }) => {
4433
4825
  await requireBackendMethod(client, "skipToolCall")({
4434
4826
  toolCallId
4435
4827
  });
4436
4828
  }, [
4437
4829
  client
4438
4830
  ]);
4439
- const regenerate = (0, import_react16.useCallback)(() => {
4831
+ const regenerate = (0, import_react18.useCallback)(() => {
4832
+ const blocked = latest.current.blocked;
4833
+ if (blocked != null) throw new QuotaBlockedError(blocked);
4440
4834
  if (isTurnInFlight()) return;
4441
4835
  regenerateNext.current = true;
4442
4836
  turnInFlight.current = true;
@@ -4451,7 +4845,7 @@ function useAgentChat(options) {
4451
4845
  isWorking,
4452
4846
  refresh: refreshBackground
4453
4847
  };
4454
- const setThreadModel = (0, import_react16.useCallback)(async (model) => {
4848
+ const setThreadModel = (0, import_react18.useCallback)(async (model) => {
4455
4849
  const threadId = latest.current.threadId ?? createdThreadId.current;
4456
4850
  if (threadId === void 0) {
4457
4851
  throw new Error("setThreadModel: this chat has no thread yet");
@@ -4465,7 +4859,7 @@ function useAgentChat(options) {
4465
4859
  }, [
4466
4860
  client
4467
4861
  ]);
4468
- const getThreadId = (0, import_react16.useCallback)(() => latest.current.threadId ?? createdThreadId.current, []);
4862
+ const getThreadId = (0, import_react18.useCallback)(() => latest.current.threadId ?? createdThreadId.current, []);
4469
4863
  const status = connection.status === "reconnecting" ? "reconnecting" : chat.status;
4470
4864
  return {
4471
4865
  ...chat,
@@ -4508,6 +4902,16 @@ function useAgentChat(options) {
4508
4902
  };
4509
4903
  }
4510
4904
  __name(useAgentChat, "useAgentChat");
4905
+ var QuotaBlockedError = class extends Error {
4906
+ static {
4907
+ __name(this, "QuotaBlockedError");
4908
+ }
4909
+ block;
4910
+ constructor(block) {
4911
+ super(block.reason ?? `The ${block.period === "day" ? "daily" : "monthly"} quota is used up`), this.block = block;
4912
+ this.name = "QuotaBlockedError";
4913
+ }
4914
+ };
4511
4915
  async function mergeHeaders(options) {
4512
4916
  const dynamic = await options.getHeaders?.() ?? {};
4513
4917
  return {
@@ -4527,7 +4931,9 @@ __name(mergeHeaders, "mergeHeaders");
4527
4931
  MessageItem,
4528
4932
  MessageItemView,
4529
4933
  MessageList,
4934
+ QuotaBlockedError,
4530
4935
  SPEECH_LANGUAGES,
4936
+ acceptsFile,
4531
4937
  applyCompletion,
4532
4938
  approvalCountdown,
4533
4939
  backgroundRunsFromThread,
@@ -4538,7 +4944,10 @@ __name(mergeHeaders, "mergeHeaders");
4538
4944
  describeTimestamp,
4539
4945
  describeToolCall,
4540
4946
  describeUsage,
4947
+ dragHasFiles,
4541
4948
  extractMessageText,
4949
+ fileKind,
4950
+ filesFromClipboard,
4542
4951
  fillTemplate,
4543
4952
  filterAutocompleteItems,
4544
4953
  findActiveTrigger,
@@ -4548,6 +4957,7 @@ __name(mergeHeaders, "mergeHeaders");
4548
4957
  inferResultView,
4549
4958
  isActionCall,
4550
4959
  loadStoredSpeechLang,
4960
+ messageFiles,
4551
4961
  notifyThreads,
4552
4962
  onThreadsEvent,
4553
4963
  persistSpeechLang,
@@ -4563,11 +4973,13 @@ __name(mergeHeaders, "mergeHeaders");
4563
4973
  useAgentChat,
4564
4974
  useAgents,
4565
4975
  useApprovalCountdown,
4976
+ useAttachments,
4566
4977
  useChatTranscript,
4567
4978
  useComposerAutocomplete,
4568
4979
  useElapsed,
4569
4980
  useMessageFeedback,
4570
4981
  useModels,
4982
+ useQuota,
4571
4983
  useSpeechRecognition,
4572
4984
  useStickToBottom,
4573
4985
  useThreads,