@dudousxd/nestjs-agent-react 0.17.0 → 0.19.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
@@ -35,6 +35,7 @@ __export(src_exports, {
35
35
  AgentChatTransport: () => AgentChatTransport,
36
36
  AgentClient: () => AgentClient,
37
37
  AgentHttpError: () => AgentHttpError,
38
+ AmbientRenderUiContext: () => AmbientRenderUiContext,
38
39
  ChatInput: () => ChatInput,
39
40
  DEFAULT_SPEECH_LANG: () => DEFAULT_SPEECH_LANG,
40
41
  MessageItem: () => MessageItem,
@@ -42,6 +43,7 @@ __export(src_exports, {
42
43
  MessageList: () => MessageList,
43
44
  QuotaBlockedError: () => QuotaBlockedError,
44
45
  SPEECH_LANGUAGES: () => SPEECH_LANGUAGES,
46
+ acceptsFile: () => acceptsFile,
45
47
  applyCompletion: () => applyCompletion,
46
48
  approvalCountdown: () => approvalCountdown,
47
49
  backgroundRunsFromThread: () => backgroundRunsFromThread,
@@ -52,7 +54,10 @@ __export(src_exports, {
52
54
  describeTimestamp: () => describeTimestamp,
53
55
  describeToolCall: () => describeToolCall,
54
56
  describeUsage: () => describeUsage,
57
+ dragHasFiles: () => dragHasFiles,
55
58
  extractMessageText: () => extractMessageText,
59
+ fileKind: () => fileKind,
60
+ filesFromClipboard: () => filesFromClipboard,
56
61
  fillTemplate: () => fillTemplate,
57
62
  filterAutocompleteItems: () => filterAutocompleteItems,
58
63
  findActiveTrigger: () => findActiveTrigger,
@@ -62,6 +67,7 @@ __export(src_exports, {
62
67
  inferResultView: () => inferResultView,
63
68
  isActionCall: () => isActionCall,
64
69
  loadStoredSpeechLang: () => loadStoredSpeechLang,
70
+ messageFiles: () => messageFiles,
65
71
  notifyThreads: () => notifyThreads,
66
72
  onThreadsEvent: () => onThreadsEvent,
67
73
  persistSpeechLang: () => persistSpeechLang,
@@ -76,7 +82,9 @@ __export(src_exports, {
76
82
  toolCatalogFrom: () => toolCatalogFrom,
77
83
  useAgentChat: () => useAgentChat,
78
84
  useAgents: () => useAgents,
85
+ useAmbientRenderUi: () => useAmbientRenderUi,
79
86
  useApprovalCountdown: () => useApprovalCountdown,
87
+ useAttachments: () => useAttachments,
80
88
  useChatTranscript: () => useChatTranscript,
81
89
  useComposerAutocomplete: () => useComposerAutocomplete,
82
90
  useElapsed: () => useElapsed,
@@ -217,6 +225,9 @@ var AgentClient = class {
217
225
  * what a caller then rides on `sendMessage({ text }, { body: { attachments: [...] } })`.
218
226
  */
219
227
  async uploadAttachment(file, options = {}) {
228
+ if (options.onProgress !== void 0 && this.options.fetch === void 0 && typeof XMLHttpRequest !== "undefined") {
229
+ return this.uploadWithProgress(file, options);
230
+ }
220
231
  const formData = new FormData();
221
232
  formData.append("file", file);
222
233
  const response = await this.fetchImpl()(`${this.baseUrl()}/agent/attachments`, {
@@ -231,7 +242,47 @@ var AgentClient = class {
231
242
  signal: options.signal
232
243
  } : {}
233
244
  });
234
- return this.handleResponse(response, "POST", "/agent/attachments");
245
+ const attachment = await this.handleResponse(response, "POST", "/agent/attachments");
246
+ options.onProgress?.(1);
247
+ return attachment;
248
+ }
249
+ async uploadWithProgress(file, { signal, onProgress }) {
250
+ const headers = {
251
+ accept: "application/json",
252
+ ...await this.resolveHeaders()
253
+ };
254
+ const url = `${this.baseUrl()}/agent/attachments`;
255
+ return new Promise((resolve, reject) => {
256
+ const xhr = new XMLHttpRequest();
257
+ xhr.open("POST", url);
258
+ for (const [name, value] of Object.entries(headers)) xhr.setRequestHeader(name, value);
259
+ xhr.withCredentials = this.options.credentials === "include";
260
+ xhr.upload.onprogress = (event) => {
261
+ if (event.lengthComputable && event.total > 0) onProgress?.(event.loaded / event.total);
262
+ };
263
+ xhr.onload = () => {
264
+ if (xhr.status < 200 || xhr.status >= 300) {
265
+ reject(new AgentHttpError(xhr.status, "POST", "/agent/attachments", xhr.statusText));
266
+ return;
267
+ }
268
+ onProgress?.(1);
269
+ resolve(JSON.parse(xhr.responseText));
270
+ };
271
+ xhr.onerror = () => reject(new TypeError("Network error while uploading the attachment"));
272
+ xhr.onabort = () => reject(new DOMException("The upload was aborted", "AbortError"));
273
+ if (signal !== void 0) {
274
+ if (signal.aborted) {
275
+ reject(new DOMException("The upload was aborted", "AbortError"));
276
+ return;
277
+ }
278
+ signal.addEventListener("abort", () => xhr.abort(), {
279
+ once: true
280
+ });
281
+ }
282
+ const formData = new FormData();
283
+ formData.append("file", file);
284
+ xhr.send(formData);
285
+ });
235
286
  }
236
287
  promoteThread(id) {
237
288
  return this.request("POST", `/agent/threads/${encodeURIComponent(id)}/promote`);
@@ -1079,6 +1130,68 @@ function delay(ms, signal) {
1079
1130
  }
1080
1131
  __name(delay, "delay");
1081
1132
 
1133
+ // src/attachments/files.ts
1134
+ function fileKind(mediaType) {
1135
+ const type = mediaType.toLowerCase();
1136
+ if (type.startsWith("image/")) return "image";
1137
+ if (type === "application/pdf") return "pdf";
1138
+ if (type.startsWith("text/") || type === "application/json") return "text";
1139
+ if (type.startsWith("audio/")) return "audio";
1140
+ if (type.startsWith("video/")) return "video";
1141
+ return "other";
1142
+ }
1143
+ __name(fileKind, "fileKind");
1144
+ function messageFiles(message) {
1145
+ const files = [];
1146
+ for (const part of message.parts) {
1147
+ if (part.type !== "file") continue;
1148
+ const file = part;
1149
+ const filename = file.filename ?? null;
1150
+ const dot = filename?.lastIndexOf(".") ?? -1;
1151
+ const mediaId = file.providerMetadata?.agent?.mediaId;
1152
+ files.push({
1153
+ url: file.url,
1154
+ mediaType: file.mediaType,
1155
+ filename,
1156
+ kind: fileKind(file.mediaType),
1157
+ extension: filename !== null && dot > 0 ? filename.slice(dot + 1).toLowerCase() : null,
1158
+ mediaId: typeof mediaId === "string" ? mediaId : null
1159
+ });
1160
+ }
1161
+ return files;
1162
+ }
1163
+ __name(messageFiles, "messageFiles");
1164
+ function acceptsFile(accept, file) {
1165
+ const rules = (typeof accept === "string" ? accept.split(",") : accept ?? []).map((rule) => rule.trim().toLowerCase()).filter((rule) => rule.length > 0);
1166
+ if (rules.length === 0) return true;
1167
+ const type = file.type.toLowerCase();
1168
+ const name = file.name.toLowerCase();
1169
+ return rules.some((rule) => {
1170
+ if (rule.startsWith(".")) return name.endsWith(rule);
1171
+ if (rule.endsWith("/*")) return type.startsWith(rule.slice(0, -1));
1172
+ return type === rule;
1173
+ });
1174
+ }
1175
+ __name(acceptsFile, "acceptsFile");
1176
+ function filesFromClipboard(data) {
1177
+ if (!data) return [];
1178
+ const files = [];
1179
+ for (const item of Array.from(data.items ?? [])) {
1180
+ if (item.kind !== "file") continue;
1181
+ const file = item.getAsFile();
1182
+ if (file !== null) files.push(file);
1183
+ }
1184
+ return files.length > 0 ? files : Array.from(data.files ?? []);
1185
+ }
1186
+ __name(filesFromClipboard, "filesFromClipboard");
1187
+ function dragHasFiles(data) {
1188
+ return Array.from(data?.types ?? []).includes("Files");
1189
+ }
1190
+ __name(dragHasFiles, "dragHasFiles");
1191
+
1192
+ // src/attachments/use-attachments.ts
1193
+ var import_react2 = require("react");
1194
+
1082
1195
  // src/backend.ts
1083
1196
  var AgentBackendUnsupportedError = class extends Error {
1084
1197
  static {
@@ -1099,19 +1212,239 @@ function requireBackendMethod(backend, name) {
1099
1212
  }
1100
1213
  __name(requireBackendMethod, "requireBackendMethod");
1101
1214
 
1215
+ // src/attachments/use-attachments.ts
1216
+ var DEFAULT_REJECTION = {
1217
+ type: /* @__PURE__ */ __name((file) => `${file.name} is not a supported file type`, "type"),
1218
+ size: /* @__PURE__ */ __name((file) => `${file.name} is too large`, "size"),
1219
+ count: /* @__PURE__ */ __name(() => "Too many files", "count")
1220
+ };
1221
+ var sequence = 0;
1222
+ function useAttachments(options) {
1223
+ const latest = (0, import_react2.useRef)(options);
1224
+ latest.current = options;
1225
+ const [items, setItems] = (0, import_react2.useState)([]);
1226
+ const itemsRef = (0, import_react2.useRef)(items);
1227
+ itemsRef.current = items;
1228
+ const controllers = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
1229
+ const [dragDepth, setDragDepth] = (0, import_react2.useState)(0);
1230
+ const patch = (0, import_react2.useCallback)((id, change) => {
1231
+ setItems((current) => current.map((item) => item.id === id ? {
1232
+ ...item,
1233
+ ...change
1234
+ } : item));
1235
+ }, []);
1236
+ const start = (0, import_react2.useCallback)((item) => {
1237
+ const { upload, backend } = latest.current;
1238
+ const run = upload ?? (backend?.uploadAttachment !== void 0 ? backend.uploadAttachment.bind(backend) : void 0);
1239
+ if (run === void 0) {
1240
+ patch(item.id, {
1241
+ status: "error",
1242
+ error: new AgentBackendUnsupportedError("uploadAttachment").message
1243
+ });
1244
+ return;
1245
+ }
1246
+ const controller = new AbortController();
1247
+ controllers.current.set(item.id, controller);
1248
+ run(item.file, {
1249
+ signal: controller.signal,
1250
+ onProgress: /* @__PURE__ */ __name((fraction) => patch(item.id, {
1251
+ progress: Math.max(0, Math.min(1, fraction))
1252
+ }), "onProgress")
1253
+ }).then((attachment) => {
1254
+ if (controller.signal.aborted) return;
1255
+ controllers.current.delete(item.id);
1256
+ patch(item.id, {
1257
+ status: "ready",
1258
+ progress: 1,
1259
+ attachment
1260
+ });
1261
+ }, (error) => {
1262
+ if (controller.signal.aborted) return;
1263
+ controllers.current.delete(item.id);
1264
+ patch(item.id, {
1265
+ status: "error",
1266
+ error: error instanceof Error ? error.message : String(error)
1267
+ });
1268
+ });
1269
+ }, [
1270
+ patch
1271
+ ]);
1272
+ const add = (0, import_react2.useCallback)((files) => {
1273
+ if (files == null) return;
1274
+ const { accept: accept2, maxBytes, maxFiles, describeRejection } = latest.current;
1275
+ const describe = /* @__PURE__ */ __name((reason, file) => describeRejection?.(reason, file) ?? DEFAULT_REJECTION[reason](file), "describe");
1276
+ let room = maxFiles === void 0 ? Number.POSITIVE_INFINITY : maxFiles - itemsRef.current.filter((item) => item.status !== "rejected").length;
1277
+ const staged = [];
1278
+ for (const file of Array.from(files)) {
1279
+ sequence += 1;
1280
+ const base = {
1281
+ id: `att-${sequence}`,
1282
+ file,
1283
+ name: file.name,
1284
+ size: file.size,
1285
+ type: file.type,
1286
+ progress: 0
1287
+ };
1288
+ const reason = !acceptsFile(accept2, file) ? "type" : maxBytes !== void 0 && file.size > maxBytes ? "size" : room <= 0 ? "count" : void 0;
1289
+ if (reason !== void 0) {
1290
+ staged.push({
1291
+ ...base,
1292
+ status: "rejected",
1293
+ error: describe(reason, file)
1294
+ });
1295
+ continue;
1296
+ }
1297
+ room -= 1;
1298
+ const previewUrl = file.type.startsWith("image/") && typeof URL.createObjectURL === "function" ? URL.createObjectURL(file) : void 0;
1299
+ staged.push({
1300
+ ...base,
1301
+ status: "uploading",
1302
+ ...previewUrl ? {
1303
+ previewUrl
1304
+ } : {}
1305
+ });
1306
+ }
1307
+ if (staged.length === 0) return;
1308
+ setItems((current) => [
1309
+ ...current,
1310
+ ...staged
1311
+ ]);
1312
+ for (const item of staged) {
1313
+ if (item.status === "uploading") start(item);
1314
+ }
1315
+ }, [
1316
+ start
1317
+ ]);
1318
+ const release = (0, import_react2.useCallback)((item) => {
1319
+ if (item === void 0) return;
1320
+ controllers.current.get(item.id)?.abort();
1321
+ controllers.current.delete(item.id);
1322
+ if (item.previewUrl !== void 0 && typeof URL.revokeObjectURL === "function") {
1323
+ URL.revokeObjectURL(item.previewUrl);
1324
+ }
1325
+ }, []);
1326
+ const remove = (0, import_react2.useCallback)((id) => {
1327
+ release(itemsRef.current.find((item) => item.id === id));
1328
+ setItems((current) => current.filter((item) => item.id !== id));
1329
+ }, [
1330
+ release
1331
+ ]);
1332
+ const clear = (0, import_react2.useCallback)(() => {
1333
+ for (const item of itemsRef.current) release(item);
1334
+ setItems([]);
1335
+ }, [
1336
+ release
1337
+ ]);
1338
+ const retry = (0, import_react2.useCallback)((id) => {
1339
+ const item = itemsRef.current.find((candidate) => candidate.id === id);
1340
+ if (item === void 0 || item.status !== "error") return;
1341
+ const { error: _previous, ...rest } = item;
1342
+ const again = {
1343
+ ...rest,
1344
+ status: "uploading",
1345
+ progress: 0
1346
+ };
1347
+ setItems((current) => current.map((candidate) => candidate.id === id ? again : candidate));
1348
+ start(again);
1349
+ }, [
1350
+ start
1351
+ ]);
1352
+ (0, import_react2.useEffect)(() => () => {
1353
+ for (const item of itemsRef.current) release(item);
1354
+ }, [
1355
+ release
1356
+ ]);
1357
+ const onPaste = (0, import_react2.useCallback)((event) => {
1358
+ const files = filesFromClipboard(event.clipboardData);
1359
+ if (files.length === 0) return;
1360
+ event.preventDefault();
1361
+ add(files);
1362
+ }, [
1363
+ add
1364
+ ]);
1365
+ const dropZoneProps = (0, import_react2.useMemo)(() => ({
1366
+ onDragEnter: /* @__PURE__ */ __name((event) => {
1367
+ if (!dragHasFiles(event.dataTransfer)) return;
1368
+ event.preventDefault();
1369
+ setDragDepth((depth) => depth + 1);
1370
+ }, "onDragEnter"),
1371
+ onDragOver: /* @__PURE__ */ __name((event) => {
1372
+ if (dragHasFiles(event.dataTransfer)) event.preventDefault();
1373
+ }, "onDragOver"),
1374
+ onDragLeave: /* @__PURE__ */ __name((event) => {
1375
+ if (!dragHasFiles(event.dataTransfer)) return;
1376
+ setDragDepth((depth) => Math.max(0, depth - 1));
1377
+ }, "onDragLeave"),
1378
+ onDrop: /* @__PURE__ */ __name((event) => {
1379
+ if (!dragHasFiles(event.dataTransfer)) return;
1380
+ event.preventDefault();
1381
+ setDragDepth(0);
1382
+ add(event.dataTransfer?.files);
1383
+ }, "onDrop")
1384
+ }), [
1385
+ add
1386
+ ]);
1387
+ const accept = options.accept;
1388
+ const inputProps = (0, import_react2.useMemo)(() => ({
1389
+ type: "file",
1390
+ accept: typeof accept === "string" ? accept : accept?.join(","),
1391
+ multiple: options.maxFiles === void 0 || options.maxFiles > 1,
1392
+ onChange: /* @__PURE__ */ __name((event) => {
1393
+ const input = event.currentTarget;
1394
+ add(input.files);
1395
+ input.value = "";
1396
+ }, "onChange")
1397
+ }), [
1398
+ accept,
1399
+ options.maxFiles,
1400
+ add
1401
+ ]);
1402
+ return (0, import_react2.useMemo)(() => {
1403
+ const ready = items.filter((item) => item.status === "ready" && item.attachment !== void 0);
1404
+ const attachments = ready.map((item) => item.attachment);
1405
+ return {
1406
+ items,
1407
+ add,
1408
+ remove,
1409
+ retry,
1410
+ clear,
1411
+ isUploading: items.some((item) => item.status === "uploading"),
1412
+ attachments,
1413
+ refs: attachments.map((attachment) => ({
1414
+ mediaId: attachment.mediaId
1415
+ })),
1416
+ inputProps,
1417
+ dropZoneProps,
1418
+ isDragging: dragDepth > 0,
1419
+ onPaste
1420
+ };
1421
+ }, [
1422
+ items,
1423
+ add,
1424
+ remove,
1425
+ retry,
1426
+ clear,
1427
+ inputProps,
1428
+ dropZoneProps,
1429
+ dragDepth,
1430
+ onPaste
1431
+ ]);
1432
+ }
1433
+ __name(useAttachments, "useAttachments");
1434
+
1102
1435
  // src/catalog/use-agents.ts
1103
- var import_react3 = require("react");
1436
+ var import_react4 = require("react");
1104
1437
 
1105
1438
  // src/catalog/use-resource.ts
1106
- var import_react2 = require("react");
1439
+ var import_react3 = require("react");
1107
1440
  function useResource(load2, key, enabled) {
1108
- const [data, setData] = (0, import_react2.useState)(void 0);
1109
- const [isLoading, setIsLoading] = (0, import_react2.useState)(enabled);
1110
- const [error, setError] = (0, import_react2.useState)(null);
1111
- const loadRef = (0, import_react2.useRef)(load2);
1441
+ const [data, setData] = (0, import_react3.useState)(void 0);
1442
+ const [isLoading, setIsLoading] = (0, import_react3.useState)(enabled);
1443
+ const [error, setError] = (0, import_react3.useState)(null);
1444
+ const loadRef = (0, import_react3.useRef)(load2);
1112
1445
  loadRef.current = load2;
1113
- const generation = (0, import_react2.useRef)(0);
1114
- const refresh = (0, import_react2.useCallback)(async () => {
1446
+ const generation = (0, import_react3.useRef)(0);
1447
+ const refresh = (0, import_react3.useCallback)(async () => {
1115
1448
  const mine = ++generation.current;
1116
1449
  setIsLoading(true);
1117
1450
  try {
@@ -1126,7 +1459,7 @@ function useResource(load2, key, enabled) {
1126
1459
  if (mine === generation.current) setIsLoading(false);
1127
1460
  }
1128
1461
  }, []);
1129
- (0, import_react2.useEffect)(() => {
1462
+ (0, import_react3.useEffect)(() => {
1130
1463
  if (!enabled) return;
1131
1464
  void refresh();
1132
1465
  }, [
@@ -1147,7 +1480,7 @@ __name(useResource, "useResource");
1147
1480
  function useAgents(options) {
1148
1481
  const { backend, enabled = true } = options;
1149
1482
  const { data, isLoading, error, refresh } = useResource(() => requireBackendMethod(backend, "listAgents")(), "", enabled);
1150
- return (0, import_react3.useMemo)(() => {
1483
+ return (0, import_react4.useMemo)(() => {
1151
1484
  const agents = data ?? [];
1152
1485
  return {
1153
1486
  agents,
@@ -1166,11 +1499,11 @@ function useAgents(options) {
1166
1499
  __name(useAgents, "useAgents");
1167
1500
 
1168
1501
  // src/catalog/use-models.ts
1169
- var import_react4 = require("react");
1502
+ var import_react5 = require("react");
1170
1503
  function useModels(options) {
1171
1504
  const { backend, agent, enabled = true } = options;
1172
1505
  const { data, isLoading, error, refresh } = useResource(() => requireBackendMethod(backend, "listModels")(agent), `${agent ?? ""}`, enabled);
1173
- return (0, import_react4.useMemo)(() => {
1506
+ return (0, import_react5.useMemo)(() => {
1174
1507
  const providers = data?.providers ?? [];
1175
1508
  const models = providers.flatMap((provider) => provider.models.map((model) => ({
1176
1509
  ...model,
@@ -1200,7 +1533,7 @@ function useModels(options) {
1200
1533
  __name(useModels, "useModels");
1201
1534
 
1202
1535
  // src/quota/use-quota.ts
1203
- var import_react5 = require("react");
1536
+ var import_react6 = require("react");
1204
1537
 
1205
1538
  // src/threads/threads-events.ts
1206
1539
  var listeners = /* @__PURE__ */ new WeakMap();
@@ -1227,7 +1560,7 @@ __name(onThreadsEvent, "onThreadsEvent");
1227
1560
  function useQuota(options) {
1228
1561
  const { backend, enabled = true, pollMs } = options;
1229
1562
  const { data, isLoading, error, refresh } = useResource(() => requireBackendMethod(backend, "getQuota")(), "", enabled);
1230
- (0, import_react5.useEffect)(() => {
1563
+ (0, import_react6.useEffect)(() => {
1231
1564
  if (!enabled) return;
1232
1565
  return onThreadsEvent(backend, (event) => {
1233
1566
  if (event.type === "changed") void refresh();
@@ -1237,7 +1570,7 @@ function useQuota(options) {
1237
1570
  enabled,
1238
1571
  refresh
1239
1572
  ]);
1240
- (0, import_react5.useEffect)(() => {
1573
+ (0, import_react6.useEffect)(() => {
1241
1574
  if (!enabled || pollMs === void 0 || pollMs <= 0) return;
1242
1575
  const timer = setInterval(() => void refresh(), pollMs);
1243
1576
  return () => clearInterval(timer);
@@ -1246,7 +1579,7 @@ function useQuota(options) {
1246
1579
  pollMs,
1247
1580
  refresh
1248
1581
  ]);
1249
- return (0, import_react5.useMemo)(() => {
1582
+ return (0, import_react6.useMemo)(() => {
1250
1583
  const windows = data?.windows ?? [];
1251
1584
  return {
1252
1585
  report: data,
@@ -1268,27 +1601,27 @@ function useQuota(options) {
1268
1601
  __name(useQuota, "useQuota");
1269
1602
 
1270
1603
  // src/feedback/use-message-feedback.ts
1271
- var import_react6 = require("react");
1604
+ var import_react7 = require("react");
1272
1605
  function useMessageFeedback(options) {
1273
- const latest = (0, import_react6.useRef)(options);
1606
+ const latest = (0, import_react7.useRef)(options);
1274
1607
  latest.current = options;
1275
- const [local, setLocal] = (0, import_react6.useState)(/* @__PURE__ */ new Map());
1276
- const [pending, setPending] = (0, import_react6.useState)(/* @__PURE__ */ new Set());
1277
- const [error, setError] = (0, import_react6.useState)(null);
1278
- const localRef = (0, import_react6.useRef)(local);
1608
+ const [local, setLocal] = (0, import_react7.useState)(/* @__PURE__ */ new Map());
1609
+ const [pending, setPending] = (0, import_react7.useState)(/* @__PURE__ */ new Set());
1610
+ const [error, setError] = (0, import_react7.useState)(null);
1611
+ const localRef = (0, import_react7.useRef)(local);
1279
1612
  localRef.current = local;
1280
- const resolved = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
1281
- const feedbackOf = (0, import_react6.useCallback)((message) => {
1613
+ const resolved = (0, import_react7.useRef)(/* @__PURE__ */ new Map());
1614
+ const feedbackOf = (0, import_react7.useCallback)((message) => {
1282
1615
  const mine = local.get(message.id);
1283
1616
  if (mine !== void 0) return mine.feedback;
1284
1617
  return metadataOf(message).feedback ?? null;
1285
1618
  }, [
1286
1619
  local
1287
1620
  ]);
1288
- const isPending = (0, import_react6.useCallback)((message) => pending.has(message.id), [
1621
+ const isPending = (0, import_react7.useCallback)((message) => pending.has(message.id), [
1289
1622
  pending
1290
1623
  ]);
1291
- const storedIdFor = (0, import_react6.useCallback)(async (message) => {
1624
+ const storedIdFor = (0, import_react7.useCallback)(async (message) => {
1292
1625
  const runId = metadataOf(message).runId;
1293
1626
  if (runId === void 0) return message.id;
1294
1627
  const known = resolved.current.get(message.id);
@@ -1308,7 +1641,7 @@ function useMessageFeedback(options) {
1308
1641
  resolved.current.set(message.id, row.id);
1309
1642
  return row.id;
1310
1643
  }, []);
1311
- const rate = (0, import_react6.useCallback)(async (message, value, comment) => {
1644
+ const rate = (0, import_react7.useCallback)(async (message, value, comment) => {
1312
1645
  const setFeedback = requireBackendMethod(latest.current.backend, "setMessageFeedback");
1313
1646
  const had = localRef.current.get(message.id);
1314
1647
  const optimistic = value === null ? null : {
@@ -1354,7 +1687,7 @@ function useMessageFeedback(options) {
1354
1687
  }, [
1355
1688
  storedIdFor
1356
1689
  ]);
1357
- const toggle = (0, import_react6.useCallback)((message, value) => {
1690
+ const toggle = (0, import_react7.useCallback)((message, value) => {
1358
1691
  const mine = localRef.current.get(message.id);
1359
1692
  const current = mine !== void 0 ? mine.feedback : metadataOf(message).feedback ?? null;
1360
1693
  return rate(message, current?.value === value ? null : value);
@@ -1377,16 +1710,16 @@ function metadataOf(message) {
1377
1710
  __name(metadataOf, "metadataOf");
1378
1711
 
1379
1712
  // src/threads/use-threads.ts
1380
- var import_react7 = require("react");
1713
+ var import_react8 = require("react");
1381
1714
  function useThreads(options) {
1382
1715
  const { backend, enabled = true } = options;
1383
- const [threads, setThreads] = (0, import_react7.useState)([]);
1384
- const [isLoading, setIsLoading] = (0, import_react7.useState)(enabled);
1385
- const [error, setError] = (0, import_react7.useState)(null);
1386
- const threadsRef = (0, import_react7.useRef)(threads);
1716
+ const [threads, setThreads] = (0, import_react8.useState)([]);
1717
+ const [isLoading, setIsLoading] = (0, import_react8.useState)(enabled);
1718
+ const [error, setError] = (0, import_react8.useState)(null);
1719
+ const threadsRef = (0, import_react8.useRef)(threads);
1387
1720
  threadsRef.current = threads;
1388
- const generation = (0, import_react7.useRef)(0);
1389
- const refresh = (0, import_react7.useCallback)(async () => {
1721
+ const generation = (0, import_react8.useRef)(0);
1722
+ const refresh = (0, import_react8.useCallback)(async () => {
1390
1723
  const mine = ++generation.current;
1391
1724
  setIsLoading(true);
1392
1725
  try {
@@ -1403,7 +1736,7 @@ function useThreads(options) {
1403
1736
  }, [
1404
1737
  backend
1405
1738
  ]);
1406
- (0, import_react7.useEffect)(() => {
1739
+ (0, import_react8.useEffect)(() => {
1407
1740
  if (!enabled) return;
1408
1741
  void refresh();
1409
1742
  return onThreadsEvent(backend, (event) => {
@@ -1423,7 +1756,7 @@ function useThreads(options) {
1423
1756
  enabled,
1424
1757
  refresh
1425
1758
  ]);
1426
- const rename = (0, import_react7.useCallback)(async (id, title) => {
1759
+ const rename = (0, import_react8.useCallback)(async (id, title) => {
1427
1760
  const before = threadsRef.current;
1428
1761
  setThreads((current) => current.map((thread) => thread.id === id ? {
1429
1762
  ...thread,
@@ -1445,7 +1778,7 @@ function useThreads(options) {
1445
1778
  }, [
1446
1779
  backend
1447
1780
  ]);
1448
- const remove = (0, import_react7.useCallback)(async (id) => {
1781
+ const remove = (0, import_react8.useCallback)(async (id) => {
1449
1782
  const before = threadsRef.current;
1450
1783
  setThreads((current) => current.filter((thread) => thread.id !== id));
1451
1784
  try {
@@ -1519,11 +1852,29 @@ function backgroundRunsFromThread(messages) {
1519
1852
  }
1520
1853
  __name(backgroundRunsFromThread, "backgroundRunsFromThread");
1521
1854
 
1855
+ // src/components/ambient-ui.ts
1856
+ var import_react9 = require("react");
1857
+ function sharedContext(key) {
1858
+ const symbol = Symbol.for(key);
1859
+ const holder = globalThis;
1860
+ const existing = holder[symbol];
1861
+ if (existing !== void 0) return existing;
1862
+ const created = (0, import_react9.createContext)(null);
1863
+ holder[symbol] = created;
1864
+ return created;
1865
+ }
1866
+ __name(sharedContext, "sharedContext");
1867
+ var AmbientRenderUiContext = sharedContext("@dudousxd/nestjs-agent-react:ambient-render-ui");
1868
+ function useAmbientRenderUi() {
1869
+ return (0, import_react9.useContext)(AmbientRenderUiContext);
1870
+ }
1871
+ __name(useAmbientRenderUi, "useAmbientRenderUi");
1872
+
1522
1873
  // src/components/chat-input.tsx
1523
- var import_react9 = __toESM(require("react"), 1);
1874
+ var import_react11 = __toESM(require("react"), 1);
1524
1875
 
1525
1876
  // src/components/use-speech-recognition.ts
1526
- var import_react8 = require("react");
1877
+ var import_react10 = require("react");
1527
1878
  function getRecognitionCtor() {
1528
1879
  if (typeof window === "undefined") return null;
1529
1880
  const scope = window;
@@ -1531,15 +1882,15 @@ function getRecognitionCtor() {
1531
1882
  }
1532
1883
  __name(getRecognitionCtor, "getRecognitionCtor");
1533
1884
  function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTranscript }) {
1534
- const recognitionRef = (0, import_react8.useRef)(null);
1535
- const [isListening, setIsListening] = (0, import_react8.useState)(false);
1536
- const ctor = (0, import_react8.useMemo)(() => getRecognitionCtor(), []);
1885
+ const recognitionRef = (0, import_react10.useRef)(null);
1886
+ const [isListening, setIsListening] = (0, import_react10.useState)(false);
1887
+ const ctor = (0, import_react10.useMemo)(() => getRecognitionCtor(), []);
1537
1888
  const isSupported = ctor !== null;
1538
- const finalRef = (0, import_react8.useRef)(onFinalTranscript);
1539
- const interimRef = (0, import_react8.useRef)(onInterimTranscript);
1889
+ const finalRef = (0, import_react10.useRef)(onFinalTranscript);
1890
+ const interimRef = (0, import_react10.useRef)(onInterimTranscript);
1540
1891
  finalRef.current = onFinalTranscript;
1541
1892
  interimRef.current = onInterimTranscript;
1542
- (0, import_react8.useEffect)(() => {
1893
+ (0, import_react10.useEffect)(() => {
1543
1894
  if (!ctor) return;
1544
1895
  const recognition = new ctor();
1545
1896
  recognition.lang = lang;
@@ -1578,7 +1929,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
1578
1929
  ctor,
1579
1930
  lang
1580
1931
  ]);
1581
- const start = (0, import_react8.useCallback)(() => {
1932
+ const start = (0, import_react10.useCallback)(() => {
1582
1933
  const recognition = recognitionRef.current;
1583
1934
  if (!recognition) return;
1584
1935
  try {
@@ -1588,7 +1939,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
1588
1939
  console.warn("SpeechRecognition start failed", error);
1589
1940
  }
1590
1941
  }, []);
1591
- const stop = (0, import_react8.useCallback)(() => {
1942
+ const stop = (0, import_react10.useCallback)(() => {
1592
1943
  const recognition = recognitionRef.current;
1593
1944
  if (!recognition) return;
1594
1945
  try {
@@ -1607,8 +1958,8 @@ __name(useSpeechRecognition, "useSpeechRecognition");
1607
1958
 
1608
1959
  // src/components/chat-input.tsx
1609
1960
  function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = true, speechLang = "en-US", sendLabel = "Send", onStop, isStreaming = false, stopLabel = "Stop generating", className, textareaClassName, sendButtonClassName, micButtonClassName, stopButtonClassName, renderMic }) {
1610
- const [value, setValue] = (0, import_react9.useState)("");
1611
- const [interim, setInterim] = (0, import_react9.useState)("");
1961
+ const [value, setValue] = (0, import_react11.useState)("");
1962
+ const [interim, setInterim] = (0, import_react11.useState)("");
1612
1963
  const speech = useSpeechRecognition({
1613
1964
  lang: speechLang,
1614
1965
  onFinalTranscript: /* @__PURE__ */ __name((text) => {
@@ -1619,7 +1970,7 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
1619
1970
  }, "onFinalTranscript"),
1620
1971
  onInterimTranscript: /* @__PURE__ */ __name((text) => setInterim(text), "onInterimTranscript")
1621
1972
  });
1622
- (0, import_react9.useEffect)(() => {
1973
+ (0, import_react11.useEffect)(() => {
1623
1974
  if (!speech.isListening) setInterim("");
1624
1975
  }, [
1625
1976
  speech.isListening
@@ -1647,9 +1998,9 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
1647
1998
  const displayValue = speech.isListening && interim ? `${value}${value && !value.endsWith(" ") ? " " : ""}${interim}` : value;
1648
1999
  const showMic = enableSpeech && speech.isSupported;
1649
2000
  const showStop = isStreaming && onStop !== void 0;
1650
- return /* @__PURE__ */ import_react9.default.createElement("div", {
2001
+ return /* @__PURE__ */ import_react11.default.createElement("div", {
1651
2002
  className
1652
- }, /* @__PURE__ */ import_react9.default.createElement("textarea", {
2003
+ }, /* @__PURE__ */ import_react11.default.createElement("textarea", {
1653
2004
  className: textareaClassName,
1654
2005
  rows,
1655
2006
  value: displayValue,
@@ -1660,17 +2011,17 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
1660
2011
  }, "onChange"),
1661
2012
  onKeyDown: handleKeyDown,
1662
2013
  placeholder: placeholder ?? "Ask something (Enter to send, Shift+Enter for newline)"
1663
- }), /* @__PURE__ */ import_react9.default.createElement("button", {
2014
+ }), /* @__PURE__ */ import_react11.default.createElement("button", {
1664
2015
  type: "button",
1665
2016
  className: sendButtonClassName,
1666
2017
  onClick: submit,
1667
2018
  disabled: disabled || !value.trim()
1668
- }, sendLabel), showStop ? /* @__PURE__ */ import_react9.default.createElement("button", {
2019
+ }, sendLabel), showStop ? /* @__PURE__ */ import_react11.default.createElement("button", {
1669
2020
  type: "button",
1670
2021
  className: stopButtonClassName,
1671
2022
  onClick: /* @__PURE__ */ __name(() => void onStop(), "onClick"),
1672
2023
  "aria-label": "Stop generating"
1673
- }, stopLabel) : null, showMic ? /* @__PURE__ */ import_react9.default.createElement("button", {
2024
+ }, stopLabel) : null, showMic ? /* @__PURE__ */ import_react11.default.createElement("button", {
1674
2025
  type: "button",
1675
2026
  className: micButtonClassName,
1676
2027
  onClick: toggleMic,
@@ -1683,10 +2034,10 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
1683
2034
  __name(ChatInput, "ChatInput");
1684
2035
 
1685
2036
  // src/components/message-item.tsx
1686
- var import_react12 = __toESM(require("react"), 1);
2037
+ var import_react14 = __toESM(require("react"), 1);
1687
2038
 
1688
2039
  // src/transcript/use-chat-transcript.ts
1689
- var import_react11 = require("react");
2040
+ var import_react13 = require("react");
1690
2041
 
1691
2042
  // src/transcript/model.ts
1692
2043
  var import_nestjs_agent_core2 = require("@dudousxd/nestjs-agent-core");
@@ -2663,11 +3014,11 @@ function formatCostUsd(cost) {
2663
3014
  __name(formatCostUsd, "formatCostUsd");
2664
3015
 
2665
3016
  // src/transcript/use-stick-to-bottom.ts
2666
- var import_react10 = require("react");
3017
+ var import_react12 = require("react");
2667
3018
  function useStickToBottom({ contentKey, threshold = 32 }) {
2668
- const containerRef = (0, import_react10.useRef)(null);
2669
- const [isAtBottom, setIsAtBottom] = (0, import_react10.useState)(true);
2670
- const measure = (0, import_react10.useCallback)(() => {
3019
+ const containerRef = (0, import_react12.useRef)(null);
3020
+ const [isAtBottom, setIsAtBottom] = (0, import_react12.useState)(true);
3021
+ const measure = (0, import_react12.useCallback)(() => {
2671
3022
  const element = containerRef.current;
2672
3023
  if (!element) {
2673
3024
  return;
@@ -2677,7 +3028,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
2677
3028
  }, [
2678
3029
  threshold
2679
3030
  ]);
2680
- const scrollToBottom = (0, import_react10.useCallback)(() => {
3031
+ const scrollToBottom = (0, import_react12.useCallback)(() => {
2681
3032
  const element = containerRef.current;
2682
3033
  if (!element) {
2683
3034
  return;
@@ -2685,7 +3036,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
2685
3036
  element.scrollTop = element.scrollHeight;
2686
3037
  setIsAtBottom(true);
2687
3038
  }, []);
2688
- const setContainer = (0, import_react10.useCallback)((element) => {
3039
+ const setContainer = (0, import_react12.useCallback)((element) => {
2689
3040
  containerRef.current = element;
2690
3041
  if (element) {
2691
3042
  measure();
@@ -2693,7 +3044,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
2693
3044
  }, [
2694
3045
  measure
2695
3046
  ]);
2696
- (0, import_react10.useEffect)(() => {
3047
+ (0, import_react12.useEffect)(() => {
2697
3048
  if (isAtBottom) {
2698
3049
  scrollToBottom();
2699
3050
  }
@@ -2702,7 +3053,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
2702
3053
  isAtBottom,
2703
3054
  scrollToBottom
2704
3055
  ]);
2705
- const getContainerProps = (0, import_react10.useCallback)(() => ({
3056
+ const getContainerProps = (0, import_react12.useCallback)(() => ({
2706
3057
  ref: setContainer,
2707
3058
  onScroll: measure
2708
3059
  }), [
@@ -2724,14 +3075,14 @@ var DEFAULT_LOAD_EARLIER_STEP = 50;
2724
3075
  var DEFAULT_COPY_RESET_MS = 1500;
2725
3076
  function useChatTranscript(options) {
2726
3077
  const { messages, status, initialVisibleCount = DEFAULT_VISIBLE_COUNT, loadEarlierStep = DEFAULT_LOAD_EARLIER_STEP, followUps, stickToBottomThreshold } = options;
2727
- const latest = (0, import_react11.useRef)(options);
3078
+ const latest = (0, import_react13.useRef)(options);
2728
3079
  latest.current = options;
2729
- const [visibleCount, setVisibleCount] = (0, import_react11.useState)(initialVisibleCount);
2730
- const [stopRequested, setStopRequested] = (0, import_react11.useState)(false);
3080
+ const [visibleCount, setVisibleCount] = (0, import_react13.useState)(initialVisibleCount);
3081
+ const [stopRequested, setStopRequested] = (0, import_react13.useState)(false);
2731
3082
  const isStreaming = status === "streaming" || status === "reconnecting";
2732
3083
  const isBusy = status === "submitted" || isStreaming;
2733
3084
  const isEmpty = messages.length === 0;
2734
- (0, import_react11.useEffect)(() => {
3085
+ (0, import_react13.useEffect)(() => {
2735
3086
  if (!isBusy) {
2736
3087
  setStopRequested(false);
2737
3088
  }
@@ -2748,7 +3099,7 @@ function useChatTranscript(options) {
2748
3099
  streamingMessageId,
2749
3100
  options
2750
3101
  });
2751
- const stop = (0, import_react11.useCallback)(() => {
3102
+ const stop = (0, import_react13.useCallback)(() => {
2752
3103
  const current = latest.current;
2753
3104
  if (current.status !== "submitted" && current.status !== "streaming" && current.status !== "reconnecting") {
2754
3105
  return;
@@ -2756,8 +3107,8 @@ function useChatTranscript(options) {
2756
3107
  setStopRequested(true);
2757
3108
  void current.onStop?.();
2758
3109
  }, []);
2759
- const loadEarlier = (0, import_react11.useCallback)(() => setVisibleCount((count) => count + (latest.current.loadEarlierStep ?? DEFAULT_LOAD_EARLIER_STEP)), []);
2760
- const showAll = (0, import_react11.useCallback)(() => setVisibleCount(latest.current.messages.length), []);
3110
+ const loadEarlier = (0, import_react13.useCallback)(() => setVisibleCount((count) => count + (latest.current.loadEarlierStep ?? DEFAULT_LOAD_EARLIER_STEP)), []);
3111
+ const showAll = (0, import_react13.useCallback)(() => setVisibleCount(latest.current.messages.length), []);
2761
3112
  const scroll = useStickToBottom({
2762
3113
  contentKey: contentSignature(messages),
2763
3114
  ...stickToBottomThreshold !== void 0 ? {
@@ -2793,7 +3144,7 @@ function useChatTranscript(options) {
2793
3144
  __name(useChatTranscript, "useChatTranscript");
2794
3145
  function useTranscriptItem(options) {
2795
3146
  const { message, isStreaming = false } = options;
2796
- const messagesRef = (0, import_react11.useRef)([
3147
+ const messagesRef = (0, import_react13.useRef)([
2797
3148
  message
2798
3149
  ]);
2799
3150
  if (messagesRef.current[0] !== message) {
@@ -2811,13 +3162,13 @@ function useTranscriptItem(options) {
2811
3162
  }
2812
3163
  __name(useTranscriptItem, "useTranscriptItem");
2813
3164
  function useTranscriptItems({ messages, visibleStart, streamingMessageId, options }) {
2814
- const [drafts, setDrafts] = (0, import_react11.useState)(() => /* @__PURE__ */ new Map());
2815
- const [copied, setCopied] = (0, import_react11.useState)(() => /* @__PURE__ */ new Set());
2816
- const [openReasoning, setOpenReasoning] = (0, import_react11.useState)(() => /* @__PURE__ */ new Map());
2817
- const [picks, setPicks] = (0, import_react11.useState)(() => /* @__PURE__ */ new Map());
2818
- const [settling, setSettling] = (0, import_react11.useState)(() => /* @__PURE__ */ new Map());
2819
- const [settleErrors, setSettleErrors] = (0, import_react11.useState)(() => /* @__PURE__ */ new Map());
2820
- const latest = (0, import_react11.useRef)({
3165
+ const [drafts, setDrafts] = (0, import_react13.useState)(() => /* @__PURE__ */ new Map());
3166
+ const [copied, setCopied] = (0, import_react13.useState)(() => /* @__PURE__ */ new Set());
3167
+ const [openReasoning, setOpenReasoning] = (0, import_react13.useState)(() => /* @__PURE__ */ new Map());
3168
+ const [picks, setPicks] = (0, import_react13.useState)(() => /* @__PURE__ */ new Map());
3169
+ const [settling, setSettling] = (0, import_react13.useState)(() => /* @__PURE__ */ new Map());
3170
+ const [settleErrors, setSettleErrors] = (0, import_react13.useState)(() => /* @__PURE__ */ new Map());
3171
+ const latest = (0, import_react13.useRef)({
2821
3172
  options,
2822
3173
  drafts,
2823
3174
  picks,
@@ -2829,23 +3180,23 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2829
3180
  picks,
2830
3181
  messages
2831
3182
  };
2832
- const copyTimers = (0, import_react11.useRef)(/* @__PURE__ */ new Map());
2833
- (0, import_react11.useEffect)(() => () => {
3183
+ const copyTimers = (0, import_react13.useRef)(/* @__PURE__ */ new Map());
3184
+ (0, import_react13.useEffect)(() => () => {
2834
3185
  for (const timer of copyTimers.current.values()) {
2835
3186
  clearTimeout(timer);
2836
3187
  }
2837
3188
  copyTimers.current.clear();
2838
3189
  }, []);
2839
- const callbackCache = (0, import_react11.useRef)(/* @__PURE__ */ new Map());
2840
- const reasoningToggleCache = (0, import_react11.useRef)(/* @__PURE__ */ new Map());
2841
- const toggleReasoning = (0, import_react11.useCallback)((key, open) => {
3190
+ const callbackCache = (0, import_react13.useRef)(/* @__PURE__ */ new Map());
3191
+ const reasoningToggleCache = (0, import_react13.useRef)(/* @__PURE__ */ new Map());
3192
+ const toggleReasoning = (0, import_react13.useCallback)((key, open) => {
2842
3193
  setOpenReasoning((current) => {
2843
3194
  const next = new Map(current);
2844
3195
  next.set(key, open ?? !(current.get(key) ?? false));
2845
3196
  return next;
2846
3197
  });
2847
3198
  }, []);
2848
- const stableToggle = (0, import_react11.useCallback)((key) => {
3199
+ const stableToggle = (0, import_react13.useCallback)((key) => {
2849
3200
  let cached = reasoningToggleCache.current.get(key);
2850
3201
  if (!cached) {
2851
3202
  cached = /* @__PURE__ */ __name((open) => toggleReasoning(key, open), "cached");
@@ -2855,10 +3206,10 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2855
3206
  }, [
2856
3207
  toggleReasoning
2857
3208
  ]);
2858
- const pick = (0, import_react11.useCallback)((toolCallId, questionId, values) => {
3209
+ const pick = (0, import_react13.useCallback)((toolCallId, questionId, values) => {
2859
3210
  setPicks((current) => new Map(current).set(pickKey(toolCallId, questionId), values));
2860
3211
  }, []);
2861
- const settle = (0, import_react11.useCallback)((toolCallId, action, send) => {
3212
+ const settle = (0, import_react13.useCallback)((toolCallId, action, send) => {
2862
3213
  setSettleErrors((current) => {
2863
3214
  if (!current.has(toolCallId)) {
2864
3215
  return current;
@@ -2882,7 +3233,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2882
3233
  failed(error);
2883
3234
  }
2884
3235
  }, []);
2885
- const answer = (0, import_react11.useCallback)((toolCallId) => {
3236
+ const answer = (0, import_react13.useCallback)((toolCallId) => {
2886
3237
  const current = latest.current;
2887
3238
  const onAnswer = current.options.onAnswer;
2888
3239
  if (!onAnswer) {
@@ -2892,7 +3243,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2892
3243
  }, [
2893
3244
  settle
2894
3245
  ]);
2895
- const skip = (0, import_react11.useCallback)((toolCallId) => {
3246
+ const skip = (0, import_react13.useCallback)((toolCallId) => {
2896
3247
  const onSkip = latest.current.options.onSkip;
2897
3248
  if (onSkip) {
2898
3249
  settle(toolCallId, "skip", () => onSkip(toolCallId));
@@ -2900,7 +3251,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2900
3251
  }, [
2901
3252
  settle
2902
3253
  ]);
2903
- const approve = (0, import_react11.useCallback)((toolCallId, options2) => {
3254
+ const approve = (0, import_react13.useCallback)((toolCallId, options2) => {
2904
3255
  const onApprove = latest.current.options.onApprove;
2905
3256
  if (onApprove) {
2906
3257
  settle(toolCallId, "approve", () => options2 === void 0 ? onApprove(toolCallId) : onApprove(toolCallId, options2));
@@ -2908,7 +3259,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2908
3259
  }, [
2909
3260
  settle
2910
3261
  ]);
2911
- const reject = (0, import_react11.useCallback)((toolCallId) => {
3262
+ const reject = (0, import_react13.useCallback)((toolCallId) => {
2912
3263
  const onReject = latest.current.options.onReject;
2913
3264
  if (onReject) {
2914
3265
  settle(toolCallId, "reject", () => onReject(toolCallId));
@@ -2916,7 +3267,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2916
3267
  }, [
2917
3268
  settle
2918
3269
  ]);
2919
- const callbacksFor = (0, import_react11.useCallback)((id) => {
3270
+ const callbacksFor = (0, import_react13.useCallback)((id) => {
2920
3271
  const cached = callbackCache.current.get(id);
2921
3272
  if (cached) {
2922
3273
  return cached;
@@ -3158,59 +3509,64 @@ function MessageItem({ message, isStreaming = false, editable, onEditSubmit, reg
3158
3509
  getCreatedAt: /* @__PURE__ */ __name(() => createdAt, "getCreatedAt")
3159
3510
  } : {}
3160
3511
  });
3161
- return /* @__PURE__ */ import_react12.default.createElement(MessageItemView, {
3512
+ return /* @__PURE__ */ import_react14.default.createElement(MessageItemView, {
3162
3513
  item,
3163
3514
  ...slots
3164
3515
  });
3165
3516
  }
3166
3517
  __name(MessageItem, "MessageItem");
3167
- function MessageItemView({ item, ...slots }) {
3518
+ function MessageItemView({ item, ...given }) {
3519
+ const ambientRenderUi = useAmbientRenderUi();
3520
+ const slots = given.renderUi === void 0 && ambientRenderUi !== null ? {
3521
+ ...given,
3522
+ renderUi: ambientRenderUi
3523
+ } : given;
3168
3524
  const { classNames } = slots;
3169
- const actionRow = /* @__PURE__ */ import_react12.default.createElement("div", {
3525
+ const actionRow = /* @__PURE__ */ import_react14.default.createElement("div", {
3170
3526
  className: classNames?.actions,
3171
3527
  "data-actions-for": item.role
3172
- }, item.copy.available ? /* @__PURE__ */ import_react12.default.createElement("button", {
3528
+ }, item.copy.available ? /* @__PURE__ */ import_react14.default.createElement("button", {
3173
3529
  type: "button",
3174
3530
  onClick: item.copy.copy,
3175
3531
  className: classNames?.actionButton,
3176
3532
  title: item.copy.copied ? "Copied!" : "Copy message text"
3177
- }, item.copy.copied ? "Copied" : "Copy") : null, item.fork.available ? /* @__PURE__ */ import_react12.default.createElement("button", {
3533
+ }, item.copy.copied ? "Copied" : "Copy") : null, item.fork.available ? /* @__PURE__ */ import_react14.default.createElement("button", {
3178
3534
  type: "button",
3179
3535
  onClick: item.fork.run,
3180
3536
  className: classNames?.actionButton,
3181
3537
  title: "Fork thread from this message"
3182
- }, "Fork") : null, item.regenerate.available ? /* @__PURE__ */ import_react12.default.createElement("button", {
3538
+ }, "Fork") : null, item.regenerate.available ? /* @__PURE__ */ import_react14.default.createElement("button", {
3183
3539
  type: "button",
3184
3540
  onClick: item.regenerate.run,
3185
3541
  className: classNames?.actionButton,
3186
3542
  title: "Regenerate response"
3187
- }, "Regenerate") : null, slots.meta, !item.isUser && item.usage ? /* @__PURE__ */ import_react12.default.createElement("span", {
3543
+ }, "Regenerate") : null, slots.meta, !item.isUser && item.usage ? /* @__PURE__ */ import_react14.default.createElement("span", {
3188
3544
  className: classNames?.usage,
3189
3545
  title: `Input ${item.usage.inputTokens.toLocaleString()} \xB7 Output ${item.usage.outputTokens.toLocaleString()} tokens`
3190
- }, item.usage.costLabel, " \xB7 ", item.usage.tokensLabel) : null, item.timestamp ? /* @__PURE__ */ import_react12.default.createElement("span", {
3546
+ }, item.usage.costLabel, " \xB7 ", item.usage.tokensLabel) : null, item.timestamp ? /* @__PURE__ */ import_react14.default.createElement("span", {
3191
3547
  className: classNames?.timestamp,
3192
3548
  title: item.timestamp.absolute
3193
3549
  }, item.timestamp.relative) : null);
3194
3550
  if (item.edit.available) {
3195
- return /* @__PURE__ */ import_react12.default.createElement(EditableUserBubble, {
3551
+ return /* @__PURE__ */ import_react14.default.createElement(EditableUserBubble, {
3196
3552
  item,
3197
3553
  slots,
3198
3554
  actions: actionRow
3199
3555
  });
3200
3556
  }
3201
3557
  const className = joinClasses(classNames?.root, classNames?.byRole?.[item.role]);
3202
- return /* @__PURE__ */ import_react12.default.createElement("div", {
3558
+ return /* @__PURE__ */ import_react14.default.createElement("div", {
3203
3559
  className,
3204
3560
  "data-role": item.role,
3205
3561
  "data-streaming": item.isStreaming ? "true" : void 0
3206
- }, /* @__PURE__ */ import_react12.default.createElement("div", null, renderBlocks(item, slots)), actionRow);
3562
+ }, /* @__PURE__ */ import_react14.default.createElement("div", null, renderBlocks(item, slots)), actionRow);
3207
3563
  }
3208
3564
  __name(MessageItemView, "MessageItemView");
3209
3565
  function renderBlocks(item, slots) {
3210
3566
  const nodes = [];
3211
3567
  for (const block of item.blocks) {
3212
3568
  if (block.kind === "text") {
3213
- nodes.push(/* @__PURE__ */ import_react12.default.createElement("div", {
3569
+ nodes.push(/* @__PURE__ */ import_react14.default.createElement("div", {
3214
3570
  key: block.key,
3215
3571
  className: slots.classNames?.text
3216
3572
  }, slots.renderText ? slots.renderText(block.text, {
@@ -3231,7 +3587,7 @@ function renderBlocks(item, slots) {
3231
3587
  continue;
3232
3588
  }
3233
3589
  if (block.kind === "ui" && slots.renderUi) {
3234
- nodes.push(/* @__PURE__ */ import_react12.default.createElement("div", {
3590
+ nodes.push(/* @__PURE__ */ import_react14.default.createElement("div", {
3235
3591
  key: block.key,
3236
3592
  "data-slot": "ui",
3237
3593
  "data-component": block.component
@@ -3242,18 +3598,18 @@ function renderBlocks(item, slots) {
3242
3598
  }
3243
3599
  __name(renderBlocks, "renderBlocks");
3244
3600
  function renderFilesBlock(block, slots) {
3245
- return /* @__PURE__ */ import_react12.default.createElement("div", {
3601
+ return /* @__PURE__ */ import_react14.default.createElement("div", {
3246
3602
  key: block.key,
3247
3603
  className: slots.classNames?.files,
3248
3604
  "data-files": "true"
3249
- }, slots.renderFiles ? slots.renderFiles(block.files) : block.files.map((file) => /* @__PURE__ */ import_react12.default.createElement("a", {
3605
+ }, slots.renderFiles ? slots.renderFiles(block.files) : block.files.map((file) => /* @__PURE__ */ import_react14.default.createElement("a", {
3250
3606
  key: file.url,
3251
3607
  href: file.url,
3252
3608
  target: "_blank",
3253
3609
  rel: "noreferrer",
3254
3610
  className: slots.classNames?.file,
3255
3611
  title: file.filename ?? file.mediaType
3256
- }, file.isImage ? /* @__PURE__ */ import_react12.default.createElement("img", {
3612
+ }, file.isImage ? /* @__PURE__ */ import_react14.default.createElement("img", {
3257
3613
  src: file.url,
3258
3614
  alt: file.filename ?? "image",
3259
3615
  className: slots.classNames?.fileImage
@@ -3261,17 +3617,17 @@ function renderFilesBlock(block, slots) {
3261
3617
  }
3262
3618
  __name(renderFilesBlock, "renderFilesBlock");
3263
3619
  function renderReasoningBlock(block, slots) {
3264
- return /* @__PURE__ */ import_react12.default.createElement("div", {
3620
+ return /* @__PURE__ */ import_react14.default.createElement("div", {
3265
3621
  key: block.key,
3266
3622
  className: slots.classNames?.reasoning,
3267
3623
  "data-reasoning": "true",
3268
3624
  "data-streaming": block.isStreaming ? "true" : void 0
3269
- }, /* @__PURE__ */ import_react12.default.createElement("button", {
3625
+ }, /* @__PURE__ */ import_react14.default.createElement("button", {
3270
3626
  type: "button",
3271
3627
  className: slots.classNames?.reasoningToggle,
3272
3628
  onClick: /* @__PURE__ */ __name(() => block.toggle(), "onClick"),
3273
3629
  "aria-expanded": block.isOpen
3274
- }, slots.reasoningLabel ?? "Reasoning"), block.isOpen ? /* @__PURE__ */ import_react12.default.createElement("div", {
3630
+ }, slots.reasoningLabel ?? "Reasoning"), block.isOpen ? /* @__PURE__ */ import_react14.default.createElement("div", {
3275
3631
  className: slots.classNames?.reasoningText
3276
3632
  }, slots.renderReasoning ? slots.renderReasoning(block.text, {
3277
3633
  isStreaming: block.isStreaming,
@@ -3282,7 +3638,7 @@ __name(renderReasoningBlock, "renderReasoningBlock");
3282
3638
  function renderToolBlock(item, block, slots) {
3283
3639
  if (slots.renderToolGroup) {
3284
3640
  return [
3285
- /* @__PURE__ */ import_react12.default.createElement(import_react12.default.Fragment, {
3641
+ /* @__PURE__ */ import_react14.default.createElement(import_react14.default.Fragment, {
3286
3642
  key: block.key
3287
3643
  }, slots.renderToolGroup(block.parts, block.key))
3288
3644
  ];
@@ -3293,7 +3649,7 @@ function renderToolBlock(item, block, slots) {
3293
3649
  const renderToolPart = slots.renderToolPart;
3294
3650
  return block.parts.map((part) => {
3295
3651
  const partKey = `${item.id}-${part.toolCallId}`;
3296
- return /* @__PURE__ */ import_react12.default.createElement(import_react12.default.Fragment, {
3652
+ return /* @__PURE__ */ import_react14.default.createElement(import_react14.default.Fragment, {
3297
3653
  key: partKey
3298
3654
  }, renderToolPart(part, partKey));
3299
3655
  });
@@ -3303,14 +3659,14 @@ function EditableUserBubble({ item, slots, actions }) {
3303
3659
  const { classNames } = slots;
3304
3660
  const { edit } = item;
3305
3661
  if (!edit.isEditing) {
3306
- return /* @__PURE__ */ import_react12.default.createElement("div", {
3662
+ return /* @__PURE__ */ import_react14.default.createElement("div", {
3307
3663
  className: joinClasses(classNames?.root, classNames?.byRole?.user),
3308
3664
  "data-role": "user"
3309
- }, /* @__PURE__ */ import_react12.default.createElement("div", {
3665
+ }, /* @__PURE__ */ import_react14.default.createElement("div", {
3310
3666
  className: classNames?.text
3311
3667
  }, slots.renderText ? slots.renderText(item.text, {
3312
3668
  isStreaming: false
3313
- }) : item.text), /* @__PURE__ */ import_react12.default.createElement("button", {
3669
+ }) : item.text), /* @__PURE__ */ import_react14.default.createElement("button", {
3314
3670
  type: "button",
3315
3671
  onClick: edit.start,
3316
3672
  className: classNames?.actionButton,
@@ -3318,18 +3674,18 @@ function EditableUserBubble({ item, slots, actions }) {
3318
3674
  }, "Edit"), actions);
3319
3675
  }
3320
3676
  const textareaProps = edit.getTextareaProps();
3321
- return /* @__PURE__ */ import_react12.default.createElement("div", {
3677
+ return /* @__PURE__ */ import_react14.default.createElement("div", {
3322
3678
  "data-editing": "true"
3323
- }, /* @__PURE__ */ import_react12.default.createElement("textarea", {
3679
+ }, /* @__PURE__ */ import_react14.default.createElement("textarea", {
3324
3680
  ...textareaProps,
3325
3681
  rows: Math.min(8, edit.draft.split("\n").length + 1),
3326
3682
  className: classNames?.editTextarea
3327
- }), /* @__PURE__ */ import_react12.default.createElement("div", {
3683
+ }), /* @__PURE__ */ import_react14.default.createElement("div", {
3328
3684
  className: classNames?.editActions
3329
- }, /* @__PURE__ */ import_react12.default.createElement("button", {
3685
+ }, /* @__PURE__ */ import_react14.default.createElement("button", {
3330
3686
  type: "button",
3331
3687
  onClick: edit.cancel
3332
- }, "Cancel"), /* @__PURE__ */ import_react12.default.createElement("button", {
3688
+ }, "Cancel"), /* @__PURE__ */ import_react14.default.createElement("button", {
3333
3689
  type: "button",
3334
3690
  onClick: edit.save,
3335
3691
  disabled: !edit.canSave
@@ -3573,35 +3929,35 @@ function toItem(entry) {
3573
3929
  __name(toItem, "toItem");
3574
3930
 
3575
3931
  // src/composer/use-composer-autocomplete.ts
3576
- var import_react13 = require("react");
3932
+ var import_react15 = require("react");
3577
3933
  function useComposerAutocomplete(options) {
3578
3934
  const { value, sources, acceptOnTab = true } = options;
3579
- const latest = (0, import_react13.useRef)(options);
3935
+ const latest = (0, import_react15.useRef)(options);
3580
3936
  latest.current = options;
3581
- const inputRef = (0, import_react13.useRef)(null);
3582
- const [caret, setCaret] = (0, import_react13.useState)(value.length);
3583
- const [items, setItems] = (0, import_react13.useState)([]);
3584
- const [isLoading, setLoading] = (0, import_react13.useState)(false);
3585
- const [error, setError] = (0, import_react13.useState)(null);
3586
- const [activeIndex, setActiveIndex] = (0, import_react13.useState)(0);
3587
- const [dismissedIndex, setDismissedIndex] = (0, import_react13.useState)(null);
3937
+ const inputRef = (0, import_react15.useRef)(null);
3938
+ const [caret, setCaret] = (0, import_react15.useState)(value.length);
3939
+ const [items, setItems] = (0, import_react15.useState)([]);
3940
+ const [isLoading, setLoading] = (0, import_react15.useState)(false);
3941
+ const [error, setError] = (0, import_react15.useState)(null);
3942
+ const [activeIndex, setActiveIndex] = (0, import_react15.useState)(0);
3943
+ const [dismissedIndex, setDismissedIndex] = (0, import_react15.useState)(null);
3588
3944
  const safeCaret = Math.min(caret, value.length);
3589
- const match = (0, import_react13.useMemo)(() => findActiveTrigger(value, safeCaret, sources), [
3945
+ const match = (0, import_react15.useMemo)(() => findActiveTrigger(value, safeCaret, sources), [
3590
3946
  value,
3591
3947
  safeCaret,
3592
3948
  sources
3593
3949
  ]);
3594
3950
  const isActive = match !== null && dismissedIndex !== match.index;
3595
- const matchRef = (0, import_react13.useRef)(match);
3951
+ const matchRef = (0, import_react15.useRef)(match);
3596
3952
  matchRef.current = match;
3597
- const caretRef = (0, import_react13.useRef)(safeCaret);
3953
+ const caretRef = (0, import_react15.useRef)(safeCaret);
3598
3954
  caretRef.current = safeCaret;
3599
- const itemsRef = (0, import_react13.useRef)(items);
3955
+ const itemsRef = (0, import_react15.useRef)(items);
3600
3956
  itemsRef.current = items;
3601
- const activeIndexRef = (0, import_react13.useRef)(activeIndex);
3957
+ const activeIndexRef = (0, import_react15.useRef)(activeIndex);
3602
3958
  activeIndexRef.current = activeIndex;
3603
3959
  const requestKey = isActive && match ? `${match.source.id}\0${match.query}` : null;
3604
- (0, import_react13.useEffect)(() => {
3960
+ (0, import_react15.useEffect)(() => {
3605
3961
  const current = matchRef.current;
3606
3962
  if (requestKey === null || current === null) {
3607
3963
  setItems([]);
@@ -3649,25 +4005,25 @@ function useComposerAutocomplete(options) {
3649
4005
  ]);
3650
4006
  const isOpen = isActive;
3651
4007
  const activeItem = isOpen ? items[activeIndex] ?? null : null;
3652
- const reactId = (0, import_react13.useId)();
4008
+ const reactId = (0, import_react15.useId)();
3653
4009
  const listboxId = options.id ?? `agent-autocomplete-${reactId}`;
3654
- const optionId = (0, import_react13.useCallback)((index) => `${listboxId}-option-${index}`, [
4010
+ const optionId = (0, import_react15.useCallback)((index) => `${listboxId}-option-${index}`, [
3655
4011
  listboxId
3656
4012
  ]);
3657
- const highlight = (0, import_react13.useCallback)((index) => {
4013
+ const highlight = (0, import_react15.useCallback)((index) => {
3658
4014
  const total = itemsRef.current.length;
3659
4015
  if (total === 0) return;
3660
4016
  setActiveIndex((index % total + total) % total);
3661
4017
  }, []);
3662
- const moveHighlight = (0, import_react13.useCallback)((delta) => highlight(activeIndexRef.current + delta), [
4018
+ const moveHighlight = (0, import_react15.useCallback)((delta) => highlight(activeIndexRef.current + delta), [
3663
4019
  highlight
3664
4020
  ]);
3665
- const dismiss = (0, import_react13.useCallback)(() => {
4021
+ const dismiss = (0, import_react15.useCallback)(() => {
3666
4022
  const current = matchRef.current;
3667
4023
  if (current) setDismissedIndex(current.index);
3668
4024
  }, []);
3669
- const pendingCaret = (0, import_react13.useRef)(null);
3670
- const accept = (0, import_react13.useCallback)((item) => {
4025
+ const pendingCaret = (0, import_react15.useRef)(null);
4026
+ const accept = (0, import_react15.useCallback)((item) => {
3671
4027
  const current = matchRef.current;
3672
4028
  if (!current) return;
3673
4029
  const chosen = item ?? itemsRef.current[activeIndexRef.current];
@@ -3678,16 +4034,16 @@ function useComposerAutocomplete(options) {
3678
4034
  setDismissedIndex(null);
3679
4035
  pendingCaret.current = edit.caret;
3680
4036
  }, []);
3681
- (0, import_react13.useLayoutEffect)(() => {
4037
+ (0, import_react15.useLayoutEffect)(() => {
3682
4038
  const position = pendingCaret.current;
3683
4039
  if (position === null) return;
3684
4040
  pendingCaret.current = null;
3685
4041
  inputRef.current?.setSelectionRange(position, position);
3686
4042
  });
3687
- const readCaret = (0, import_react13.useCallback)((element) => {
4043
+ const readCaret = (0, import_react15.useCallback)((element) => {
3688
4044
  setCaret(element.selectionStart ?? element.value.length);
3689
4045
  }, []);
3690
- const onKeyDown = (0, import_react13.useCallback)((event) => {
4046
+ const onKeyDown = (0, import_react15.useCallback)((event) => {
3691
4047
  if (!isOpen) return;
3692
4048
  const hasItems = itemsRef.current.length > 0;
3693
4049
  switch (event.key) {
@@ -3725,23 +4081,23 @@ function useComposerAutocomplete(options) {
3725
4081
  dismiss,
3726
4082
  moveHighlight
3727
4083
  ]);
3728
- const onChange = (0, import_react13.useCallback)((event) => {
4084
+ const onChange = (0, import_react15.useCallback)((event) => {
3729
4085
  readCaret(event.target);
3730
4086
  const next = findActiveTrigger(event.target.value, event.target.selectionStart ?? event.target.value.length, latest.current.sources);
3731
4087
  setDismissedIndex((current) => current !== null && next?.index === current ? current : null);
3732
4088
  }, [
3733
4089
  readCaret
3734
4090
  ]);
3735
- const onSelect = (0, import_react13.useCallback)((event) => readCaret(event.currentTarget), [
4091
+ const onSelect = (0, import_react15.useCallback)((event) => readCaret(event.currentTarget), [
3736
4092
  readCaret
3737
4093
  ]);
3738
- const onClick = (0, import_react13.useCallback)((event) => readCaret(event.currentTarget), [
4094
+ const onClick = (0, import_react15.useCallback)((event) => readCaret(event.currentTarget), [
3739
4095
  readCaret
3740
4096
  ]);
3741
- const setInput = (0, import_react13.useCallback)((element) => {
4097
+ const setInput = (0, import_react15.useCallback)((element) => {
3742
4098
  inputRef.current = element;
3743
4099
  }, []);
3744
- const getInputProps = (0, import_react13.useCallback)(() => ({
4100
+ const getInputProps = (0, import_react15.useCallback)(() => ({
3745
4101
  ref: setInput,
3746
4102
  onChange,
3747
4103
  onKeyDown,
@@ -3769,7 +4125,7 @@ function useComposerAutocomplete(options) {
3769
4125
  activeIndex,
3770
4126
  optionId
3771
4127
  ]);
3772
- const getListboxProps = (0, import_react13.useCallback)(() => ({
4128
+ const getListboxProps = (0, import_react15.useCallback)(() => ({
3773
4129
  id: listboxId,
3774
4130
  role: "listbox",
3775
4131
  "aria-label": match?.source.label ?? match?.source.id ?? "Suggestions"
@@ -3777,7 +4133,7 @@ function useComposerAutocomplete(options) {
3777
4133
  listboxId,
3778
4134
  match
3779
4135
  ]);
3780
- const getOptionProps = (0, import_react13.useCallback)((index) => ({
4136
+ const getOptionProps = (0, import_react15.useCallback)((index) => ({
3781
4137
  id: optionId(index),
3782
4138
  role: "option",
3783
4139
  "aria-selected": index === activeIndex
@@ -3811,7 +4167,7 @@ function isPromise(value) {
3811
4167
  __name(isPromise, "isPromise");
3812
4168
 
3813
4169
  // src/presentation/use-tool-catalog.ts
3814
- var import_react14 = require("react");
4170
+ var import_react16 = require("react");
3815
4171
  var shared = /* @__PURE__ */ new WeakMap();
3816
4172
  function load(client, agent) {
3817
4173
  let byAgent = shared.get(client);
@@ -3835,11 +4191,11 @@ function load(client, agent) {
3835
4191
  __name(load, "load");
3836
4192
  function useToolCatalog(options) {
3837
4193
  const { client, agent, enabled = true } = options;
3838
- const [entries, setEntries] = (0, import_react14.useState)([]);
3839
- const [isLoading, setLoading] = (0, import_react14.useState)(enabled);
3840
- const [error, setError] = (0, import_react14.useState)(null);
3841
- const [generation, setGeneration] = (0, import_react14.useState)(0);
3842
- (0, import_react14.useEffect)(() => {
4194
+ const [entries, setEntries] = (0, import_react16.useState)([]);
4195
+ const [isLoading, setLoading] = (0, import_react16.useState)(enabled);
4196
+ const [error, setError] = (0, import_react16.useState)(null);
4197
+ const [generation, setGeneration] = (0, import_react16.useState)(0);
4198
+ (0, import_react16.useEffect)(() => {
3843
4199
  if (!enabled) {
3844
4200
  setLoading(false);
3845
4201
  return;
@@ -3865,14 +4221,14 @@ function useToolCatalog(options) {
3865
4221
  enabled,
3866
4222
  generation
3867
4223
  ]);
3868
- const refresh = (0, import_react14.useCallback)(() => {
4224
+ const refresh = (0, import_react16.useCallback)(() => {
3869
4225
  shared.get(client)?.delete(agent ?? "");
3870
4226
  setGeneration((current) => current + 1);
3871
4227
  }, [
3872
4228
  client,
3873
4229
  agent
3874
4230
  ]);
3875
- const catalog = (0, import_react14.useMemo)(() => toolCatalogFrom(entries), [
4231
+ const catalog = (0, import_react16.useMemo)(() => toolCatalogFrom(entries), [
3876
4232
  entries
3877
4233
  ]);
3878
4234
  return {
@@ -3886,7 +4242,7 @@ function useToolCatalog(options) {
3886
4242
  __name(useToolCatalog, "useToolCatalog");
3887
4243
 
3888
4244
  // src/approvals/countdown.ts
3889
- var import_react15 = require("react");
4245
+ var import_react17 = require("react");
3890
4246
  function approvalCountdown(expiresAt, now = Date.now()) {
3891
4247
  const deadline = expiresAt == null ? Number.NaN : Date.parse(expiresAt);
3892
4248
  if (!Number.isFinite(deadline)) {
@@ -3904,10 +4260,10 @@ function approvalCountdown(expiresAt, now = Date.now()) {
3904
4260
  __name(approvalCountdown, "approvalCountdown");
3905
4261
  function useApprovalCountdown(expiresAt, options = {}) {
3906
4262
  const { intervalMs = 1e3 } = options;
3907
- const nowRef = (0, import_react15.useRef)(options.now ?? Date.now);
4263
+ const nowRef = (0, import_react17.useRef)(options.now ?? Date.now);
3908
4264
  nowRef.current = options.now ?? Date.now;
3909
- const [state, setState] = (0, import_react15.useState)(() => approvalCountdown(expiresAt, nowRef.current()));
3910
- (0, import_react15.useEffect)(() => {
4265
+ const [state, setState] = (0, import_react17.useState)(() => approvalCountdown(expiresAt, nowRef.current()));
4266
+ (0, import_react17.useEffect)(() => {
3911
4267
  const read = /* @__PURE__ */ __name(() => approvalCountdown(expiresAt, nowRef.current()), "read");
3912
4268
  const first = read();
3913
4269
  setState(first);
@@ -3970,7 +4326,13 @@ function storedMessageToUiMessage(message) {
3970
4326
  type: "file",
3971
4327
  mediaType: attachment.contentType,
3972
4328
  filename: attachment.name,
3973
- url: attachment.url
4329
+ url: attachment.url,
4330
+ // The stored id rides along, so a replayed file can be referenced again (`messageFiles`).
4331
+ providerMetadata: {
4332
+ agent: {
4333
+ mediaId: attachment.mediaId
4334
+ }
4335
+ }
3974
4336
  });
3975
4337
  }
3976
4338
  const toolCallIds = new Set((message.toolCalls ?? []).map((call) => call.id));
@@ -4148,21 +4510,21 @@ function sumUsage(rows) {
4148
4510
  __name(sumUsage, "sumUsage");
4149
4511
 
4150
4512
  // src/use-agent-chat.ts
4151
- var import_react16 = require("@ai-sdk/react");
4152
- var import_react17 = require("react");
4513
+ var import_react18 = require("@ai-sdk/react");
4514
+ var import_react19 = require("react");
4153
4515
  function useAgentChat(options) {
4154
- const latest = (0, import_react17.useRef)(options);
4516
+ const latest = (0, import_react19.useRef)(options);
4155
4517
  latest.current = options;
4156
- const [runId, setRunId] = (0, import_react17.useState)(options.resumeRunId);
4157
- const runIdRef = (0, import_react17.useRef)(runId);
4518
+ const [runId, setRunId] = (0, import_react19.useState)(options.resumeRunId);
4519
+ const runIdRef = (0, import_react19.useRef)(runId);
4158
4520
  runIdRef.current = runId;
4159
- const settlingRunIdRef = (0, import_react17.useRef)(void 0);
4160
- const autoResumeRunIdRef = (0, import_react17.useRef)(void 0);
4161
- const [autoResume, setAutoResume] = (0, import_react17.useState)(false);
4162
- const [activeRunId, setActiveRunId] = (0, import_react17.useState)(null);
4163
- const regenerateNext = (0, import_react17.useRef)(false);
4164
- const createdThreadId = (0, import_react17.useRef)(void 0);
4165
- const client = (0, import_react17.useMemo)(() => {
4521
+ const settlingRunIdRef = (0, import_react19.useRef)(void 0);
4522
+ const autoResumeRunIdRef = (0, import_react19.useRef)(void 0);
4523
+ const [autoResume, setAutoResume] = (0, import_react19.useState)(false);
4524
+ const [activeRunId, setActiveRunId] = (0, import_react19.useState)(null);
4525
+ const regenerateNext = (0, import_react19.useRef)(false);
4526
+ const createdThreadId = (0, import_react19.useRef)(void 0);
4527
+ const client = (0, import_react19.useMemo)(() => {
4166
4528
  if (options.backend) return options.backend;
4167
4529
  if (options.client) return options.client;
4168
4530
  return new AgentClient({
@@ -4178,11 +4540,11 @@ function useAgentChat(options) {
4178
4540
  getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders")
4179
4541
  });
4180
4542
  }, []);
4181
- const [connection, setConnection] = (0, import_react17.useState)({
4543
+ const [connection, setConnection] = (0, import_react19.useState)({
4182
4544
  status: "live"
4183
4545
  });
4184
- const resyncAfterSettle = (0, import_react17.useRef)(false);
4185
- (0, import_react17.useEffect)(() => {
4546
+ const resyncAfterSettle = (0, import_react19.useRef)(false);
4547
+ (0, import_react19.useEffect)(() => {
4186
4548
  if (!options.resume || options.threadId === void 0) return;
4187
4549
  let cancelled = false;
4188
4550
  const threadId = options.threadId;
@@ -4202,7 +4564,7 @@ function useAgentChat(options) {
4202
4564
  options.resume,
4203
4565
  options.threadId
4204
4566
  ]);
4205
- const transport = (0, import_react17.useMemo)(() => {
4567
+ const transport = (0, import_react19.useMemo)(() => {
4206
4568
  function onMeta(meta) {
4207
4569
  settlingRunIdRef.current = meta.runId;
4208
4570
  setRunId(meta.runId);
@@ -4257,8 +4619,8 @@ function useAgentChat(options) {
4257
4619
  }, "onAttemptStart")
4258
4620
  });
4259
4621
  }, []);
4260
- const backgroundRef = (0, import_react17.useRef)(() => Promise.resolve());
4261
- const chat = (0, import_react16.useChat)({
4622
+ const backgroundRef = (0, import_react19.useRef)(() => Promise.resolve());
4623
+ const chat = (0, import_react18.useChat)({
4262
4624
  transport,
4263
4625
  resume: options.resumeRunId !== void 0 || autoResume,
4264
4626
  ...options.threadId !== void 0 ? {
@@ -4306,7 +4668,7 @@ function useAgentChat(options) {
4306
4668
  }
4307
4669
  }, "onFinish")
4308
4670
  });
4309
- const chatRef = (0, import_react17.useRef)(chat);
4671
+ const chatRef = (0, import_react19.useRef)(chat);
4310
4672
  chatRef.current = chat;
4311
4673
  async function resyncFromThread() {
4312
4674
  const threadId = latest.current.threadId ?? createdThreadId.current;
@@ -4318,11 +4680,11 @@ function useAgentChat(options) {
4318
4680
  }
4319
4681
  }
4320
4682
  __name(resyncFromThread, "resyncFromThread");
4321
- const turnInFlight = (0, import_react17.useRef)(false);
4322
- const isTurnInFlight = (0, import_react17.useCallback)(() => turnInFlight.current || transport.isAttemptLive, [
4683
+ const turnInFlight = (0, import_react19.useRef)(false);
4684
+ const isTurnInFlight = (0, import_react19.useCallback)(() => turnInFlight.current || transport.isAttemptLive, [
4323
4685
  transport
4324
4686
  ]);
4325
- const sendMessage = (0, import_react17.useCallback)(async (...args) => {
4687
+ const sendMessage = (0, import_react19.useCallback)(async (...args) => {
4326
4688
  const blocked = latest.current.blocked;
4327
4689
  if (blocked != null) throw new QuotaBlockedError(blocked);
4328
4690
  if (isTurnInFlight()) return;
@@ -4335,15 +4697,15 @@ function useAgentChat(options) {
4335
4697
  }, [
4336
4698
  isTurnInFlight
4337
4699
  ]);
4338
- const addToolResult = (0, import_react17.useCallback)(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
4700
+ const addToolResult = (0, import_react19.useCallback)(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
4339
4701
  tool,
4340
4702
  toolCallId,
4341
4703
  output
4342
4704
  }), []);
4343
- const [threads, setThreads] = (0, import_react17.useState)([]);
4344
- const [quota, setQuota] = (0, import_react17.useState)(null);
4345
- const [backgroundRuns, setBackgroundRuns] = (0, import_react17.useState)([]);
4346
- const refreshBackground = (0, import_react17.useCallback)(async () => {
4705
+ const [threads, setThreads] = (0, import_react19.useState)([]);
4706
+ const [quota, setQuota] = (0, import_react19.useState)(null);
4707
+ const [backgroundRuns, setBackgroundRuns] = (0, import_react19.useState)([]);
4708
+ const refreshBackground = (0, import_react19.useCallback)(async () => {
4347
4709
  const threadId = latest.current.threadId ?? createdThreadId.current;
4348
4710
  if (threadId === void 0) {
4349
4711
  return;
@@ -4368,7 +4730,7 @@ function useAgentChat(options) {
4368
4730
  ]);
4369
4731
  backgroundRef.current = refreshBackground;
4370
4732
  const isWorking = backgroundRuns.some((run) => run.status === "running");
4371
- (0, import_react17.useEffect)(() => {
4733
+ (0, import_react19.useEffect)(() => {
4372
4734
  if (options.background !== true || options.threadId === void 0) {
4373
4735
  return;
4374
4736
  }
@@ -4378,7 +4740,7 @@ function useAgentChat(options) {
4378
4740
  options.threadId,
4379
4741
  refreshBackground
4380
4742
  ]);
4381
- (0, import_react17.useEffect)(() => {
4743
+ (0, import_react19.useEffect)(() => {
4382
4744
  const every = latest.current.backgroundPollMs ?? 5e3;
4383
4745
  if (latest.current.background !== true || !isWorking || every <= 0) {
4384
4746
  return;
@@ -4389,26 +4751,26 @@ function useAgentChat(options) {
4389
4751
  isWorking,
4390
4752
  refreshBackground
4391
4753
  ]);
4392
- const loadThreads = (0, import_react17.useCallback)(async () => {
4754
+ const loadThreads = (0, import_react19.useCallback)(async () => {
4393
4755
  const list = await client.listThreads();
4394
4756
  setThreads(list);
4395
4757
  return list;
4396
4758
  }, [
4397
4759
  client
4398
4760
  ]);
4399
- const loadThread = (0, import_react17.useCallback)((id) => client.getThread(id), [
4761
+ const loadThread = (0, import_react19.useCallback)((id) => client.getThread(id), [
4400
4762
  client
4401
4763
  ]);
4402
- const deleteThread = (0, import_react17.useCallback)(async (id) => {
4764
+ const deleteThread = (0, import_react19.useCallback)(async (id) => {
4403
4765
  await client.deleteThread(id);
4404
4766
  setThreads((current) => current.filter((thread) => thread.id !== id));
4405
4767
  }, [
4406
4768
  client
4407
4769
  ]);
4408
- const forkThread = (0, import_react17.useCallback)((threadId, messageId) => requireBackendMethod(client, "forkFromMessage")(threadId, messageId), [
4770
+ const forkThread = (0, import_react19.useCallback)((threadId, messageId) => requireBackendMethod(client, "forkFromMessage")(threadId, messageId), [
4409
4771
  client
4410
4772
  ]);
4411
- const renameThread = (0, import_react17.useCallback)(async (id, title) => {
4773
+ const renameThread = (0, import_react19.useCallback)(async (id, title) => {
4412
4774
  await client.updateThread(id, {
4413
4775
  title
4414
4776
  });
@@ -4419,24 +4781,24 @@ function useAgentChat(options) {
4419
4781
  }, [
4420
4782
  client
4421
4783
  ]);
4422
- const promoteThread = (0, import_react17.useCallback)(async (id) => {
4784
+ const promoteThread = (0, import_react19.useCallback)(async (id) => {
4423
4785
  await requireBackendMethod(client, "promoteThread")(id);
4424
4786
  await loadThreads();
4425
4787
  }, [
4426
4788
  client,
4427
4789
  loadThreads
4428
4790
  ]);
4429
- const truncateFromMessage = (0, import_react17.useCallback)((threadId, messageId) => requireBackendMethod(client, "truncateFromMessage")(threadId, messageId).then(() => void 0), [
4791
+ const truncateFromMessage = (0, import_react19.useCallback)((threadId, messageId) => requireBackendMethod(client, "truncateFromMessage")(threadId, messageId).then(() => void 0), [
4430
4792
  client
4431
4793
  ]);
4432
- const loadQuota = (0, import_react17.useCallback)(async () => {
4794
+ const loadQuota = (0, import_react19.useCallback)(async () => {
4433
4795
  const today = await requireBackendMethod(client, "getQuotaToday")();
4434
4796
  setQuota(today);
4435
4797
  return today;
4436
4798
  }, [
4437
4799
  client
4438
4800
  ]);
4439
- const cancel = (0, import_react17.useCallback)(async () => {
4801
+ const cancel = (0, import_react19.useCallback)(async () => {
4440
4802
  chatRef.current.stop();
4441
4803
  const activeRunId2 = runIdRef.current;
4442
4804
  if (activeRunId2) {
@@ -4448,7 +4810,7 @@ function useAgentChat(options) {
4448
4810
  }, [
4449
4811
  client
4450
4812
  ]);
4451
- const approve = (0, import_react17.useCallback)(async ({ toolCallId, remember, via }) => {
4813
+ const approve = (0, import_react19.useCallback)(async ({ toolCallId, remember, via }) => {
4452
4814
  await requireBackendMethod(client, "approveToolCall")({
4453
4815
  toolCallId,
4454
4816
  ...remember === true ? {
@@ -4461,7 +4823,7 @@ function useAgentChat(options) {
4461
4823
  }, [
4462
4824
  client
4463
4825
  ]);
4464
- const reject = (0, import_react17.useCallback)(async ({ toolCallId, reason, via }) => {
4826
+ const reject = (0, import_react19.useCallback)(async ({ toolCallId, reason, via }) => {
4465
4827
  await requireBackendMethod(client, "rejectToolCall")({
4466
4828
  toolCallId,
4467
4829
  ...reason !== void 0 ? {
@@ -4474,7 +4836,7 @@ function useAgentChat(options) {
4474
4836
  }, [
4475
4837
  client
4476
4838
  ]);
4477
- const answer = (0, import_react17.useCallback)(async ({ toolCallId, answers }) => {
4839
+ const answer = (0, import_react19.useCallback)(async ({ toolCallId, answers }) => {
4478
4840
  await requireBackendMethod(client, "answerToolCall")({
4479
4841
  toolCallId,
4480
4842
  ...answers !== void 0 ? {
@@ -4484,14 +4846,14 @@ function useAgentChat(options) {
4484
4846
  }, [
4485
4847
  client
4486
4848
  ]);
4487
- const skip = (0, import_react17.useCallback)(async ({ toolCallId }) => {
4849
+ const skip = (0, import_react19.useCallback)(async ({ toolCallId }) => {
4488
4850
  await requireBackendMethod(client, "skipToolCall")({
4489
4851
  toolCallId
4490
4852
  });
4491
4853
  }, [
4492
4854
  client
4493
4855
  ]);
4494
- const regenerate = (0, import_react17.useCallback)(() => {
4856
+ const regenerate = (0, import_react19.useCallback)(() => {
4495
4857
  const blocked = latest.current.blocked;
4496
4858
  if (blocked != null) throw new QuotaBlockedError(blocked);
4497
4859
  if (isTurnInFlight()) return;
@@ -4508,7 +4870,7 @@ function useAgentChat(options) {
4508
4870
  isWorking,
4509
4871
  refresh: refreshBackground
4510
4872
  };
4511
- const setThreadModel = (0, import_react17.useCallback)(async (model) => {
4873
+ const setThreadModel = (0, import_react19.useCallback)(async (model) => {
4512
4874
  const threadId = latest.current.threadId ?? createdThreadId.current;
4513
4875
  if (threadId === void 0) {
4514
4876
  throw new Error("setThreadModel: this chat has no thread yet");
@@ -4522,7 +4884,7 @@ function useAgentChat(options) {
4522
4884
  }, [
4523
4885
  client
4524
4886
  ]);
4525
- const getThreadId = (0, import_react17.useCallback)(() => latest.current.threadId ?? createdThreadId.current, []);
4887
+ const getThreadId = (0, import_react19.useCallback)(() => latest.current.threadId ?? createdThreadId.current, []);
4526
4888
  const status = connection.status === "reconnecting" ? "reconnecting" : chat.status;
4527
4889
  return {
4528
4890
  ...chat,
@@ -4589,6 +4951,7 @@ __name(mergeHeaders, "mergeHeaders");
4589
4951
  AgentChatTransport,
4590
4952
  AgentClient,
4591
4953
  AgentHttpError,
4954
+ AmbientRenderUiContext,
4592
4955
  ChatInput,
4593
4956
  DEFAULT_SPEECH_LANG,
4594
4957
  MessageItem,
@@ -4596,6 +4959,7 @@ __name(mergeHeaders, "mergeHeaders");
4596
4959
  MessageList,
4597
4960
  QuotaBlockedError,
4598
4961
  SPEECH_LANGUAGES,
4962
+ acceptsFile,
4599
4963
  applyCompletion,
4600
4964
  approvalCountdown,
4601
4965
  backgroundRunsFromThread,
@@ -4606,7 +4970,10 @@ __name(mergeHeaders, "mergeHeaders");
4606
4970
  describeTimestamp,
4607
4971
  describeToolCall,
4608
4972
  describeUsage,
4973
+ dragHasFiles,
4609
4974
  extractMessageText,
4975
+ fileKind,
4976
+ filesFromClipboard,
4610
4977
  fillTemplate,
4611
4978
  filterAutocompleteItems,
4612
4979
  findActiveTrigger,
@@ -4616,6 +4983,7 @@ __name(mergeHeaders, "mergeHeaders");
4616
4983
  inferResultView,
4617
4984
  isActionCall,
4618
4985
  loadStoredSpeechLang,
4986
+ messageFiles,
4619
4987
  notifyThreads,
4620
4988
  onThreadsEvent,
4621
4989
  persistSpeechLang,
@@ -4630,7 +4998,9 @@ __name(mergeHeaders, "mergeHeaders");
4630
4998
  toolCatalogFrom,
4631
4999
  useAgentChat,
4632
5000
  useAgents,
5001
+ useAmbientRenderUi,
4633
5002
  useApprovalCountdown,
5003
+ useAttachments,
4634
5004
  useChatTranscript,
4635
5005
  useComposerAutocomplete,
4636
5006
  useElapsed,