@dudousxd/nestjs-agent-react 0.20.0 → 0.22.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
+ AgentProvider: () => AgentProvider,
38
39
  AmbientRenderUiContext: () => AmbientRenderUiContext,
39
40
  ChatInput: () => ChatInput,
40
41
  DEFAULT_SPEECH_LANG: () => DEFAULT_SPEECH_LANG,
@@ -80,6 +81,7 @@ __export(src_exports, {
80
81
  storedThreadToUiMessages: () => storedThreadToUiMessages,
81
82
  toolCallState: () => toolCallState,
82
83
  toolCatalogFrom: () => toolCatalogFrom,
84
+ useAgentBackend: () => useAgentBackend,
83
85
  useAgentChat: () => useAgentChat,
84
86
  useAgents: () => useAgents,
85
87
  useAmbientRenderUi: () => useAmbientRenderUi,
@@ -114,6 +116,11 @@ var AgentHttpError = class extends Error {
114
116
  this.name = "AgentHttpError";
115
117
  }
116
118
  };
119
+ function normalizeAgentPath(path) {
120
+ const trimmed = (path ?? "agent").replace(/^\/+|\/+$/g, "");
121
+ return trimmed === "" ? "" : `/${trimmed}`;
122
+ }
123
+ __name(normalizeAgentPath, "normalizeAgentPath");
117
124
  var HEADER_RUN_ID = "x-agent-run-id";
118
125
  var HEADER_THREAD_ID = "x-agent-thread-id";
119
126
  var AgentClient = class {
@@ -124,9 +131,9 @@ var AgentClient = class {
124
131
  constructor(options = {}) {
125
132
  this.options = options;
126
133
  }
127
- /** `POST /agent/chat` → the turn's SSE stream. Throws {@link AgentHttpError} on a non-2xx. */
134
+ /** `POST <path>/chat` → the turn's SSE stream. Throws {@link AgentHttpError} on a non-2xx. */
128
135
  async openChatStream(request) {
129
- const response = await this.fetchImpl()(`${this.baseUrl()}/agent/chat`, {
136
+ const response = await this.fetchImpl()(`${this.root()}/chat`, {
130
137
  method: "POST",
131
138
  headers: {
132
139
  "content-type": "application/json",
@@ -141,18 +148,18 @@ var AgentClient = class {
141
148
  } : {}
142
149
  });
143
150
  if (!response.ok || !response.body) {
144
- throw new AgentHttpError(response.status, "POST", "/agent/chat", response.statusText);
151
+ throw new AgentHttpError(response.status, "POST", `${this.agentPath()}/chat`, response.statusText);
145
152
  }
146
153
  return streamResponse(response);
147
154
  }
148
155
  /**
149
- * `GET /agent/chat/:runId/stream[?after=<seq>]` → the run's SSE stream, or `null` when nothing is
156
+ * `GET <path>/chat/:runId/stream[?after=<seq>]` → the run's SSE stream, or `null` when nothing is
150
157
  * streaming under that id (404).
151
158
  */
152
159
  async resumeChatStream(request) {
153
- const path = `/agent/chat/${encodeURIComponent(request.runId)}/stream`;
160
+ const path = `/chat/${encodeURIComponent(request.runId)}/stream`;
154
161
  const query = request.after !== void 0 && request.after > 0 ? `?after=${request.after}` : "";
155
- const response = await this.fetchImpl()(`${this.baseUrl()}${path}${query}`, {
162
+ const response = await this.fetchImpl()(`${this.root()}${path}${query}`, {
156
163
  method: "GET",
157
164
  headers: {
158
165
  accept: "text/event-stream",
@@ -168,7 +175,7 @@ var AgentClient = class {
168
175
  return null;
169
176
  }
170
177
  if (!response.ok || !response.body) {
171
- throw new AgentHttpError(response.status, "GET", path, response.statusText);
178
+ throw new AgentHttpError(response.status, "GET", `${this.agentPath()}${path}`, response.statusText);
172
179
  }
173
180
  return streamResponse(response);
174
181
  }
@@ -177,10 +184,10 @@ var AgentClient = class {
177
184
  * the stored rating.
178
185
  */
179
186
  setMessageFeedback(messageId, input) {
180
- return this.request("POST", `/agent/messages/${encodeURIComponent(messageId)}/feedback`, input);
187
+ return this.request("POST", `/messages/${encodeURIComponent(messageId)}/feedback`, input);
181
188
  }
182
189
  listThreads() {
183
- return this.request("GET", "/agent/threads");
190
+ return this.request("GET", "/threads");
184
191
  }
185
192
  /**
186
193
  * The skills this caller can invoke right now, scope-resolved — the same list, built by the same
@@ -190,7 +197,7 @@ var AgentClient = class {
190
197
  */
191
198
  listSkills(threadId) {
192
199
  const query = threadId === void 0 ? "" : `?threadId=${encodeURIComponent(threadId)}`;
193
- return this.request("GET", `/agent/skills${query}`);
200
+ return this.request("GET", `/skills${query}`);
194
201
  }
195
202
  /**
196
203
  * The tools this caller can reach through `agent` (the default agent when omitted), each with the
@@ -199,41 +206,37 @@ var AgentClient = class {
199
206
  */
200
207
  listTools(agent) {
201
208
  const query = agent === void 0 ? "" : `?agent=${encodeURIComponent(agent)}`;
202
- return this.request("GET", `/agent/tools${query}`);
209
+ return this.request("GET", `/tools${query}`);
203
210
  }
204
211
  getThread(id) {
205
- return this.request("GET", `/agent/threads/${encodeURIComponent(id)}`);
212
+ return this.request("GET", `/threads/${encodeURIComponent(id)}`);
206
213
  }
207
214
  deleteThread(id) {
208
- return this.request("DELETE", `/agent/threads/${encodeURIComponent(id)}`);
215
+ return this.request("DELETE", `/threads/${encodeURIComponent(id)}`);
209
216
  }
210
217
  forkFromMessage(threadId, messageId) {
211
- return this.request("POST", `/agent/threads/${encodeURIComponent(threadId)}/fork-from/${encodeURIComponent(messageId)}`);
212
- }
213
- renameThread(id, title) {
214
- return this.updateThread(id, {
215
- title
216
- });
218
+ return this.request("POST", `/threads/${encodeURIComponent(threadId)}/fork-from/${encodeURIComponent(messageId)}`);
217
219
  }
218
- /** General `PATCH /agent/threads/:threadId` — title and/or the thread's pinned default agent. */
220
+ /** General `PATCH <path>/threads/:threadId` — title and/or the thread's pinned default agent. */
219
221
  updateThread(id, patch) {
220
- return this.request("PATCH", `/agent/threads/${encodeURIComponent(id)}`, patch);
222
+ return this.request("PATCH", `/threads/${encodeURIComponent(id)}`, patch);
221
223
  }
222
224
  /**
223
225
  * Uploads a file (image/PDF) for a vision-capable model turn. Multipart, field name `file` —
224
- * mirrors the backend's `POST /agent/attachments`. The returned {@link MessageAttachment} is
226
+ * mirrors the backend's `POST <path>/attachments`. The returned {@link MessageAttachment} is
225
227
  * what a caller then rides on `sendMessage({ text }, { body: { attachments: [...] } })`.
226
228
  */
227
229
  async uploadAttachment(file, options = {}) {
228
- if (this.options.attachments !== void 0) {
229
- return this.options.attachments(file, options, this.connection());
230
+ const upload = this.options.attachments?.upload;
231
+ if (upload !== void 0) {
232
+ return upload(file, options, this.connection());
230
233
  }
231
234
  if (options.onProgress !== void 0 && this.options.fetch === void 0 && typeof XMLHttpRequest !== "undefined") {
232
235
  return this.uploadWithProgress(file, options);
233
236
  }
234
237
  const formData = new FormData();
235
238
  formData.append("file", file);
236
- const response = await this.fetchImpl()(`${this.baseUrl()}/agent/attachments`, {
239
+ const response = await this.fetchImpl()(`${this.root()}/attachments`, {
237
240
  method: "POST",
238
241
  headers: {
239
242
  accept: "application/json",
@@ -245,7 +248,7 @@ var AgentClient = class {
245
248
  signal: options.signal
246
249
  } : {}
247
250
  });
248
- const attachment = await this.handleResponse(response, "POST", "/agent/attachments");
251
+ const attachment = await this.handleResponse(response, "POST", `${this.agentPath()}/attachments`);
249
252
  options.onProgress?.(1);
250
253
  return attachment;
251
254
  }
@@ -254,7 +257,7 @@ var AgentClient = class {
254
257
  accept: "application/json",
255
258
  ...await this.resolveHeaders()
256
259
  };
257
- const url = `${this.baseUrl()}/agent/attachments`;
260
+ const url = `${this.root()}/attachments`;
258
261
  return new Promise((resolve, reject) => {
259
262
  const xhr = new XMLHttpRequest();
260
263
  xhr.open("POST", url);
@@ -265,7 +268,7 @@ var AgentClient = class {
265
268
  };
266
269
  xhr.onload = () => {
267
270
  if (xhr.status < 200 || xhr.status >= 300) {
268
- reject(new AgentHttpError(xhr.status, "POST", "/agent/attachments", xhr.statusText));
271
+ reject(new AgentHttpError(xhr.status, "POST", `${this.agentPath()}/attachments`, xhr.statusText));
269
272
  return;
270
273
  }
271
274
  onProgress?.(1);
@@ -288,39 +291,36 @@ var AgentClient = class {
288
291
  });
289
292
  }
290
293
  promoteThread(id) {
291
- return this.request("POST", `/agent/threads/${encodeURIComponent(id)}/promote`);
294
+ return this.request("POST", `/threads/${encodeURIComponent(id)}/promote`);
292
295
  }
293
296
  truncateFromMessage(threadId, messageId) {
294
- return this.request("DELETE", `/agent/threads/${encodeURIComponent(threadId)}/from/${encodeURIComponent(messageId)}`);
297
+ return this.request("DELETE", `/threads/${encodeURIComponent(threadId)}/from/${encodeURIComponent(messageId)}`);
295
298
  }
296
- /** `GET /agent/models?agent=` — the models this caller may pick, grouped by provider. */
299
+ /** `GET <path>/models?agent=` — the models this caller may pick, grouped by provider. */
297
300
  listModels(agent) {
298
301
  const query = agent === void 0 ? "" : `?agent=${encodeURIComponent(agent)}`;
299
- return this.request("GET", `/agent/models${query}`);
302
+ return this.request("GET", `/models${query}`);
300
303
  }
301
- /** `GET /agent/agents` — the registered agents, the default one flagged. */
304
+ /** `GET <path>/agents` — the registered agents, the default one flagged. */
302
305
  listAgents() {
303
- return this.request("GET", "/agent/agents");
306
+ return this.request("GET", "/agents");
304
307
  }
305
- /** `GET /agent/quota` — the caller's budget windows and the one blocking sends, if any. */
308
+ /** `GET <path>/quota` — the caller's budget windows and the one blocking sends, if any. */
306
309
  getQuota() {
307
- return this.request("GET", "/agent/quota");
308
- }
309
- getQuotaToday() {
310
- return this.request("GET", "/agent/quota/today");
310
+ return this.request("GET", "/quota");
311
311
  }
312
312
  cancelStream(runId) {
313
- return this.request("POST", `/agent/chat/${encodeURIComponent(runId)}/cancel`);
313
+ return this.request("POST", `/chat/${encodeURIComponent(runId)}/cancel`);
314
314
  }
315
315
  /**
316
316
  * `remember` approves later calls of the same tool in the same thread; `via` names the surface
317
317
  * the decision came through (the server records `'web'` when omitted).
318
318
  */
319
319
  approveToolCall(input) {
320
- return this.request("POST", "/agent/tool-call/approve", input);
320
+ return this.request("POST", "/tool-call/approve", input);
321
321
  }
322
322
  rejectToolCall(input) {
323
- return this.request("POST", "/agent/tool-call/reject", input);
323
+ return this.request("POST", "/tool-call/reject", input);
324
324
  }
325
325
  /**
326
326
  * Settle a parked question set. `answers` is questionId → chosen option values; a question left
@@ -329,7 +329,7 @@ var AgentClient = class {
329
329
  * answer — which is the point of the surface, so it is a valid submission rather than a blank.
330
330
  */
331
331
  answerToolCall(input) {
332
- return this.request("POST", "/agent/tool-call/answer", input);
332
+ return this.request("POST", "/tool-call/answer", input);
333
333
  }
334
334
  /**
335
335
  * Decline to answer and let the agent proceed on its own pre-picked values. Lands on the same
@@ -337,7 +337,7 @@ var AgentClient = class {
337
337
  * evidence the user chose them.
338
338
  */
339
339
  skipToolCall(input) {
340
- return this.request("POST", "/agent/tool-call/skip", input);
340
+ return this.request("POST", "/tool-call/skip", input);
341
341
  }
342
342
  fetchImpl() {
343
343
  return this.options.fetch ?? globalThis.fetch;
@@ -346,13 +346,21 @@ var AgentClient = class {
346
346
  connection() {
347
347
  return {
348
348
  baseUrl: this.baseUrl(),
349
+ path: this.agentPath(),
349
350
  headers: /* @__PURE__ */ __name(() => this.resolveHeaders(), "headers"),
350
351
  fetch: this.fetchImpl(),
351
352
  ...this.credentials()
352
353
  };
353
354
  }
354
355
  baseUrl() {
355
- return (this.options.baseUrl ?? "").replace(/\/$/, "");
356
+ return (this.options.baseUrl ?? "").replace(/\/+$/, "");
357
+ }
358
+ agentPath() {
359
+ return normalizeAgentPath(this.options.path);
360
+ }
361
+ /** Origin + agent path: what every route hangs off. */
362
+ root() {
363
+ return `${this.baseUrl()}${this.agentPath()}`;
356
364
  }
357
365
  async resolveHeaders() {
358
366
  const dynamic = await this.options.getHeaders?.() ?? {};
@@ -366,7 +374,8 @@ var AgentClient = class {
366
374
  credentials: this.options.credentials
367
375
  } : {};
368
376
  }
369
- async request(method, path, body) {
377
+ async request(method, route, body) {
378
+ const path = `${this.agentPath()}${route}`;
370
379
  const response = await this.fetchImpl()(`${this.baseUrl()}${path}`, {
371
380
  method,
372
381
  headers: {
@@ -497,6 +506,9 @@ var AgentChatTransport = class {
497
506
  ...options.baseUrl !== void 0 ? {
498
507
  baseUrl: options.baseUrl
499
508
  } : {},
509
+ ...options.path !== void 0 ? {
510
+ path: options.path
511
+ } : {},
500
512
  ...options.headers !== void 0 ? {
501
513
  headers: options.headers
502
514
  } : {},
@@ -708,13 +720,14 @@ var AgentChatTransport = class {
708
720
  function ensureStarted() {
709
721
  if (started) return;
710
722
  started = true;
711
- controller.enqueue(streamRunId !== void 0 ? {
723
+ controller.enqueue({
712
724
  type: "start",
713
725
  messageMetadata: {
714
- runId: streamRunId
726
+ ...streamRunId !== void 0 ? {
727
+ runId: streamRunId
728
+ } : {},
729
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
715
730
  }
716
- } : {
717
- type: "start"
718
731
  });
719
732
  }
720
733
  __name(ensureStarted, "ensureStarted");
@@ -1202,7 +1215,7 @@ function dragHasFiles(data) {
1202
1215
  __name(dragHasFiles, "dragHasFiles");
1203
1216
 
1204
1217
  // src/attachments/use-attachments.ts
1205
- var import_react2 = require("react");
1218
+ var import_react6 = require("react");
1206
1219
 
1207
1220
  // src/backend.ts
1208
1221
  var AgentBackendUnsupportedError = class extends Error {
@@ -1224,6 +1237,570 @@ function requireBackendMethod(backend, name) {
1224
1237
  }
1225
1238
  __name(requireBackendMethod, "requireBackendMethod");
1226
1239
 
1240
+ // src/provider.tsx
1241
+ var import_react5 = require("react");
1242
+
1243
+ // src/components/ambient-ui.ts
1244
+ var import_react2 = require("react");
1245
+ function sharedContext(key) {
1246
+ const symbol = Symbol.for(key);
1247
+ const holder = globalThis;
1248
+ const existing = holder[symbol];
1249
+ if (existing !== void 0) return existing;
1250
+ const created = (0, import_react2.createContext)(null);
1251
+ holder[symbol] = created;
1252
+ return created;
1253
+ }
1254
+ __name(sharedContext, "sharedContext");
1255
+ var AmbientRenderUiContext = sharedContext("@dudousxd/nestjs-agent-react:ambient-render-ui");
1256
+ function useAmbientRenderUi() {
1257
+ return (0, import_react2.useContext)(AmbientRenderUiContext);
1258
+ }
1259
+ __name(useAmbientRenderUi, "useAmbientRenderUi");
1260
+
1261
+ // src/genui/generative-ui.tsx
1262
+ var import_react4 = require("react");
1263
+
1264
+ // src/genui/use-generative-ui.ts
1265
+ var import_react3 = require("react");
1266
+
1267
+ // src/genui/types.ts
1268
+ var GENUI_TREE_COMPONENT = "genui:tree";
1269
+
1270
+ // src/genui/use-generative-ui.ts
1271
+ function isRecord(value) {
1272
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1273
+ }
1274
+ __name(isRecord, "isRecord");
1275
+ function toGenerativeUIItem(part) {
1276
+ if (!isRecord(part)) return null;
1277
+ const source = part.type === "data-ui" && isRecord(part.data) ? part.data : part;
1278
+ if (typeof source.component !== "string") return null;
1279
+ const id = typeof source.id === "string" ? source.id : typeof part.id === "string" ? part.id : null;
1280
+ if (id === null) return null;
1281
+ return {
1282
+ id,
1283
+ component: source.component,
1284
+ props: isRecord(source.props) ? source.props : {},
1285
+ version: typeof source.version === "number" ? source.version : null,
1286
+ toolCallId: typeof source.toolCallId === "string" ? source.toolCallId : null
1287
+ };
1288
+ }
1289
+ __name(toGenerativeUIItem, "toGenerativeUIItem");
1290
+ var resolverCaches = /* @__PURE__ */ new WeakMap();
1291
+ function resolveCached(resolve, name, version) {
1292
+ let cache = resolverCaches.get(resolve);
1293
+ if (cache === void 0) {
1294
+ cache = /* @__PURE__ */ new Map();
1295
+ resolverCaches.set(resolve, cache);
1296
+ }
1297
+ const key = `${name}@${version ?? ""}`;
1298
+ const hit = cache.get(key);
1299
+ if (hit !== void 0) return hit;
1300
+ let entry;
1301
+ try {
1302
+ const result = resolve(name, version);
1303
+ if (result instanceof Promise) {
1304
+ entry = {
1305
+ resolution: {
1306
+ status: "loading"
1307
+ },
1308
+ settled: null
1309
+ };
1310
+ entry.settled = result.then((Component2) => {
1311
+ entry.resolution = Component2 ? {
1312
+ status: "ready",
1313
+ Component: Component2
1314
+ } : {
1315
+ status: "unknown"
1316
+ };
1317
+ }, (error) => {
1318
+ entry.resolution = {
1319
+ status: "error",
1320
+ error
1321
+ };
1322
+ });
1323
+ } else {
1324
+ entry = {
1325
+ resolution: result ? {
1326
+ status: "ready",
1327
+ Component: result
1328
+ } : {
1329
+ status: "unknown"
1330
+ },
1331
+ settled: null
1332
+ };
1333
+ }
1334
+ } catch (error) {
1335
+ entry = {
1336
+ resolution: {
1337
+ status: "error",
1338
+ error
1339
+ },
1340
+ settled: null
1341
+ };
1342
+ }
1343
+ cache.set(key, entry);
1344
+ return entry;
1345
+ }
1346
+ __name(resolveCached, "resolveCached");
1347
+ function useResolvedComponent(name, version, registry, resolve, fallbackTree) {
1348
+ const [, setTick] = (0, import_react3.useState)(0);
1349
+ const own = registry[name];
1350
+ const entry = own !== void 0 || resolve === void 0 ? null : resolveCached(resolve, name, version);
1351
+ const pending = entry?.resolution.status === "loading" ? entry.settled : null;
1352
+ (0, import_react3.useEffect)(() => {
1353
+ if (pending === null) return;
1354
+ let live = true;
1355
+ void pending.then(() => {
1356
+ if (live) setTick((tick) => tick + 1);
1357
+ });
1358
+ return () => {
1359
+ live = false;
1360
+ };
1361
+ }, [
1362
+ pending
1363
+ ]);
1364
+ if (own !== void 0) return {
1365
+ status: "ready",
1366
+ Component: own
1367
+ };
1368
+ if (entry !== null && entry.resolution.status !== "unknown") return entry.resolution;
1369
+ if (name === GENUI_TREE_COMPONENT && fallbackTree !== void 0) {
1370
+ return {
1371
+ status: "ready",
1372
+ Component: fallbackTree
1373
+ };
1374
+ }
1375
+ return {
1376
+ status: "unknown"
1377
+ };
1378
+ }
1379
+ __name(useResolvedComponent, "useResolvedComponent");
1380
+ function useValidatedProps(catalog, name, props) {
1381
+ const checked = catalog?.has(name) === true;
1382
+ const immediate = (0, import_react3.useMemo)(() => {
1383
+ if (!checked || catalog === void 0) return {
1384
+ status: "ok",
1385
+ props
1386
+ };
1387
+ const result = catalog.validateSync?.(name, props);
1388
+ if (result === void 0) return null;
1389
+ return result.ok ? {
1390
+ status: "ok",
1391
+ props: result.value
1392
+ } : {
1393
+ status: "invalid",
1394
+ issues: result.issues
1395
+ };
1396
+ }, [
1397
+ checked,
1398
+ catalog,
1399
+ name,
1400
+ props
1401
+ ]);
1402
+ const [late, setLate] = (0, import_react3.useState)(null);
1403
+ (0, import_react3.useEffect)(() => {
1404
+ if (immediate !== null || catalog === void 0) return;
1405
+ let live = true;
1406
+ catalog.validate(name, props).then((result) => {
1407
+ if (!live) return;
1408
+ setLate({
1409
+ props,
1410
+ result: result.ok ? {
1411
+ status: "ok",
1412
+ props: result.value
1413
+ } : {
1414
+ status: "invalid",
1415
+ issues: result.issues
1416
+ }
1417
+ });
1418
+ }, (error) => {
1419
+ if (!live) return;
1420
+ setLate({
1421
+ props,
1422
+ result: {
1423
+ status: "invalid",
1424
+ issues: [
1425
+ {
1426
+ path: [],
1427
+ message: error instanceof Error ? error.message : String(error)
1428
+ }
1429
+ ]
1430
+ }
1431
+ });
1432
+ });
1433
+ return () => {
1434
+ live = false;
1435
+ };
1436
+ }, [
1437
+ immediate,
1438
+ catalog,
1439
+ name,
1440
+ props
1441
+ ]);
1442
+ if (immediate !== null) return immediate;
1443
+ return late !== null && late.props === props ? late.result : {
1444
+ status: "pending"
1445
+ };
1446
+ }
1447
+ __name(useValidatedProps, "useValidatedProps");
1448
+ function useGenerativeUIState(part, options, fallbackTree) {
1449
+ const item = (0, import_react3.useMemo)(() => toGenerativeUIItem(part), [
1450
+ part
1451
+ ]);
1452
+ const name = item?.component ?? "";
1453
+ const resolution = useResolvedComponent(name, item?.version ?? null, options.registry, item === null ? void 0 : options.resolveComponent, fallbackTree);
1454
+ const empty = (0, import_react3.useMemo)(() => ({}), []);
1455
+ const validation = useValidatedProps(item === null || name === GENUI_TREE_COMPONENT ? void 0 : options.catalog, name, item?.props ?? empty);
1456
+ if (item === null) return null;
1457
+ if (resolution.status === "loading") return {
1458
+ status: "loading",
1459
+ item
1460
+ };
1461
+ if (resolution.status === "unknown") return {
1462
+ status: "problem",
1463
+ reason: "unknown",
1464
+ item
1465
+ };
1466
+ if (resolution.status === "error") {
1467
+ return {
1468
+ status: "problem",
1469
+ reason: "error",
1470
+ item,
1471
+ error: resolution.error
1472
+ };
1473
+ }
1474
+ if (validation.status === "pending") return {
1475
+ status: "loading",
1476
+ item
1477
+ };
1478
+ if (validation.status === "invalid") {
1479
+ return {
1480
+ status: "problem",
1481
+ reason: "invalid",
1482
+ item,
1483
+ issues: validation.issues
1484
+ };
1485
+ }
1486
+ return {
1487
+ status: "ready",
1488
+ item,
1489
+ Component: resolution.Component,
1490
+ props: validation.props
1491
+ };
1492
+ }
1493
+ __name(useGenerativeUIState, "useGenerativeUIState");
1494
+
1495
+ // src/genui/generative-ui.tsx
1496
+ var TreeScopeContext = /* @__PURE__ */ (0, import_react4.createContext)(null);
1497
+ function renderFallback(fallback2, problem) {
1498
+ if (typeof fallback2 === "function") return fallback2(problem);
1499
+ return fallback2 ?? null;
1500
+ }
1501
+ __name(renderFallback, "renderFallback");
1502
+ var ItemBoundary = class ItemBoundary2 extends import_react4.Component {
1503
+ static {
1504
+ __name(this, "ItemBoundary");
1505
+ }
1506
+ state = {
1507
+ error: void 0,
1508
+ failed: false
1509
+ };
1510
+ static getDerivedStateFromError(error) {
1511
+ return {
1512
+ error,
1513
+ failed: true
1514
+ };
1515
+ }
1516
+ componentDidCatch(error, _info) {
1517
+ this.props.onError?.(error, this.props.item);
1518
+ }
1519
+ componentDidUpdate(previous) {
1520
+ if (this.state.failed && previous.item !== this.props.item) {
1521
+ this.setState({
1522
+ error: void 0,
1523
+ failed: false
1524
+ });
1525
+ }
1526
+ }
1527
+ render() {
1528
+ if (this.state.failed) {
1529
+ return renderFallback(this.props.fallback, {
1530
+ reason: "error",
1531
+ item: this.props.item,
1532
+ error: this.state.error
1533
+ });
1534
+ }
1535
+ return this.props.children;
1536
+ }
1537
+ };
1538
+ function nodeItem(node, id) {
1539
+ return {
1540
+ id,
1541
+ component: node.type,
1542
+ props: node.props ?? {},
1543
+ version: null,
1544
+ toolCallId: null
1545
+ };
1546
+ }
1547
+ __name(nodeItem, "nodeItem");
1548
+ function TreeNode({ node, id }) {
1549
+ const scope = (0, import_react4.useContext)(TreeScopeContext);
1550
+ const item = (0, import_react4.useMemo)(() => nodeItem(node, id), [
1551
+ node,
1552
+ id
1553
+ ]);
1554
+ const resolution = useResolvedComponent(node.type, null, scope?.options.registry ?? {}, scope?.options.resolveComponent);
1555
+ const validation = useValidatedProps(scope?.options.catalog, node.type, item.props);
1556
+ if (scope === null) return null;
1557
+ if (resolution.status === "loading" || validation.status === "pending") return scope.loading;
1558
+ if (resolution.status === "unknown") {
1559
+ return renderFallback(scope.fallback, {
1560
+ reason: "unknown",
1561
+ item
1562
+ });
1563
+ }
1564
+ if (resolution.status === "error") {
1565
+ return renderFallback(scope.fallback, {
1566
+ reason: "error",
1567
+ item,
1568
+ error: resolution.error
1569
+ });
1570
+ }
1571
+ if (validation.status === "invalid") {
1572
+ return renderFallback(scope.fallback, {
1573
+ reason: "invalid",
1574
+ item,
1575
+ issues: validation.issues
1576
+ });
1577
+ }
1578
+ const Renderer = resolution.Component;
1579
+ const children = Array.isArray(node.children) ? node.children : [];
1580
+ return /* @__PURE__ */ React.createElement(Renderer, validation.props, children.length > 0 ? children.map((child, index) => {
1581
+ const childId = `${id}.${index}`;
1582
+ return /* @__PURE__ */ React.createElement(ItemBoundary, {
1583
+ key: childId,
1584
+ item: nodeItem(child, childId),
1585
+ fallback: scope.fallback,
1586
+ onError: scope.onError
1587
+ }, /* @__PURE__ */ React.createElement(TreeNode, {
1588
+ node: child,
1589
+ id: childId
1590
+ }));
1591
+ }) : void 0);
1592
+ }
1593
+ __name(TreeNode, "TreeNode");
1594
+ function GenuiTree({ root }) {
1595
+ if (root === void 0 || root === null || typeof root.type !== "string") return null;
1596
+ return /* @__PURE__ */ React.createElement(TreeNode, {
1597
+ node: root,
1598
+ id: "root"
1599
+ });
1600
+ }
1601
+ __name(GenuiTree, "GenuiTree");
1602
+ function GenerativeUIScope({ registry, catalog, resolveComponent, fallback: fallback2, loading = null, onError, children }) {
1603
+ const scope = (0, import_react4.useMemo)(() => ({
1604
+ options: {
1605
+ registry,
1606
+ ...catalog !== void 0 ? {
1607
+ catalog
1608
+ } : {},
1609
+ ...resolveComponent !== void 0 ? {
1610
+ resolveComponent
1611
+ } : {}
1612
+ },
1613
+ fallback: fallback2,
1614
+ loading,
1615
+ onError
1616
+ }), [
1617
+ registry,
1618
+ catalog,
1619
+ resolveComponent,
1620
+ fallback2,
1621
+ loading,
1622
+ onError
1623
+ ]);
1624
+ return /* @__PURE__ */ React.createElement(TreeScopeContext.Provider, {
1625
+ value: scope
1626
+ }, children);
1627
+ }
1628
+ __name(GenerativeUIScope, "GenerativeUIScope");
1629
+ var GenuiContext = sharedContext("@dudousxd/nestjs-agent-react:genui-provider");
1630
+ function GenuiProvider({ registry, catalog, resolveComponent, treeRenderer, fallback: fallback2, loading, onError, children }) {
1631
+ const value = (0, import_react4.useMemo)(() => ({
1632
+ ...registry !== void 0 ? {
1633
+ registry
1634
+ } : {},
1635
+ ...catalog !== void 0 ? {
1636
+ catalog
1637
+ } : {},
1638
+ ...resolveComponent !== void 0 ? {
1639
+ resolveComponent
1640
+ } : {},
1641
+ ...treeRenderer !== void 0 ? {
1642
+ treeRenderer
1643
+ } : {},
1644
+ ...fallback2 !== void 0 ? {
1645
+ fallback: fallback2
1646
+ } : {},
1647
+ ...loading !== void 0 ? {
1648
+ loading
1649
+ } : {},
1650
+ ...onError !== void 0 ? {
1651
+ onError
1652
+ } : {}
1653
+ }), [
1654
+ registry,
1655
+ catalog,
1656
+ resolveComponent,
1657
+ treeRenderer,
1658
+ fallback2,
1659
+ loading,
1660
+ onError
1661
+ ]);
1662
+ const renderUi = (0, import_react4.useCallback)((block) => /* @__PURE__ */ React.createElement(GenerativeUI, {
1663
+ part: block
1664
+ }), []);
1665
+ return /* @__PURE__ */ React.createElement(GenuiContext.Provider, {
1666
+ value
1667
+ }, /* @__PURE__ */ React.createElement(AmbientRenderUiContext.Provider, {
1668
+ value: renderUi
1669
+ }, children));
1670
+ }
1671
+ __name(GenuiProvider, "GenuiProvider");
1672
+ var NO_REGISTRY = {};
1673
+ function useMergedOptions(own) {
1674
+ const provided = (0, import_react4.useContext)(GenuiContext);
1675
+ const registry = own.registry ?? provided?.registry ?? NO_REGISTRY;
1676
+ const catalog = own.catalog ?? provided?.catalog;
1677
+ const resolveComponent = own.resolveComponent ?? provided?.resolveComponent;
1678
+ const treeRenderer = own.treeRenderer ?? provided?.treeRenderer;
1679
+ return (0, import_react4.useMemo)(() => ({
1680
+ registry,
1681
+ ...catalog !== void 0 ? {
1682
+ catalog
1683
+ } : {},
1684
+ ...resolveComponent !== void 0 ? {
1685
+ resolveComponent
1686
+ } : {},
1687
+ ...treeRenderer !== void 0 ? {
1688
+ treeRenderer
1689
+ } : {}
1690
+ }), [
1691
+ registry,
1692
+ catalog,
1693
+ resolveComponent,
1694
+ treeRenderer
1695
+ ]);
1696
+ }
1697
+ __name(useMergedOptions, "useMergedOptions");
1698
+ function GenerativeUI({ part, registry, catalog, resolveComponent, treeRenderer, fallback: ownFallback, loading: ownLoading, onError: ownOnError }) {
1699
+ const provided = (0, import_react4.useContext)(GenuiContext);
1700
+ const fallback2 = ownFallback ?? provided?.fallback;
1701
+ const loading = ownLoading ?? provided?.loading ?? null;
1702
+ const onError = ownOnError ?? provided?.onError;
1703
+ const options = useMergedOptions({
1704
+ ...registry !== void 0 ? {
1705
+ registry
1706
+ } : {},
1707
+ ...catalog !== void 0 ? {
1708
+ catalog
1709
+ } : {},
1710
+ ...resolveComponent !== void 0 ? {
1711
+ resolveComponent
1712
+ } : {},
1713
+ ...treeRenderer !== void 0 ? {
1714
+ treeRenderer
1715
+ } : {}
1716
+ });
1717
+ const state = useGenerativeUIState(part, options, options.treeRenderer ?? GenuiTree);
1718
+ if (state === null) return null;
1719
+ if (state.status === "loading") return loading;
1720
+ if (state.status === "problem") {
1721
+ const { status: _status, ...problem } = state;
1722
+ return renderFallback(fallback2, problem);
1723
+ }
1724
+ const Renderer = state.Component;
1725
+ return /* @__PURE__ */ React.createElement(GenerativeUIScope, {
1726
+ ...options,
1727
+ fallback: fallback2,
1728
+ loading,
1729
+ ...onError !== void 0 ? {
1730
+ onError
1731
+ } : {}
1732
+ }, /* @__PURE__ */ React.createElement(ItemBoundary, {
1733
+ item: state.item,
1734
+ fallback: fallback2,
1735
+ onError
1736
+ }, /* @__PURE__ */ React.createElement(Renderer, state.props)));
1737
+ }
1738
+ __name(GenerativeUI, "GenerativeUI");
1739
+
1740
+ // src/provider.tsx
1741
+ var AgentBackendContext = sharedContext("@dudousxd/nestjs-agent-react:agent-backend");
1742
+ function AgentProvider({ backend, baseUrl, path, headers, getHeaders, credentials, fetch, attachments, genui, children }) {
1743
+ const latestHeaders = (0, import_react5.useRef)({
1744
+ headers,
1745
+ getHeaders
1746
+ });
1747
+ latestHeaders.current = {
1748
+ headers,
1749
+ getHeaders
1750
+ };
1751
+ const upload = attachments?.upload;
1752
+ const value = (0, import_react5.useMemo)(() => backend ?? new AgentClient({
1753
+ ...baseUrl !== void 0 ? {
1754
+ baseUrl
1755
+ } : {},
1756
+ ...path !== void 0 ? {
1757
+ path
1758
+ } : {},
1759
+ ...credentials !== void 0 ? {
1760
+ credentials
1761
+ } : {},
1762
+ ...fetch !== void 0 ? {
1763
+ fetch
1764
+ } : {},
1765
+ ...upload !== void 0 ? {
1766
+ attachments: {
1767
+ upload
1768
+ }
1769
+ } : {},
1770
+ getHeaders: /* @__PURE__ */ __name(async () => {
1771
+ const current = latestHeaders.current;
1772
+ const dynamic = await current.getHeaders?.() ?? {};
1773
+ return {
1774
+ ...current.headers,
1775
+ ...dynamic
1776
+ };
1777
+ }, "getHeaders")
1778
+ }), [
1779
+ backend,
1780
+ baseUrl,
1781
+ path,
1782
+ credentials,
1783
+ fetch,
1784
+ upload
1785
+ ]);
1786
+ const content = genui !== void 0 ? /* @__PURE__ */ React.createElement(GenuiProvider, genui, children) : children;
1787
+ return /* @__PURE__ */ React.createElement(AgentBackendContext.Provider, {
1788
+ value
1789
+ }, content);
1790
+ }
1791
+ __name(AgentProvider, "AgentProvider");
1792
+ var fallback;
1793
+ function defaultBackend() {
1794
+ fallback ??= new AgentClient();
1795
+ return fallback;
1796
+ }
1797
+ __name(defaultBackend, "defaultBackend");
1798
+ function useAgentBackend(own) {
1799
+ const provided = (0, import_react5.useContext)(AgentBackendContext);
1800
+ return own ?? provided ?? defaultBackend();
1801
+ }
1802
+ __name(useAgentBackend, "useAgentBackend");
1803
+
1227
1804
  // src/attachments/use-attachments.ts
1228
1805
  var DEFAULT_REJECTION = {
1229
1806
  type: /* @__PURE__ */ __name((file) => `${file.name} is not a supported file type`, "type"),
@@ -1231,23 +1808,30 @@ var DEFAULT_REJECTION = {
1231
1808
  count: /* @__PURE__ */ __name(() => "Too many files", "count")
1232
1809
  };
1233
1810
  var sequence = 0;
1234
- function useAttachments(options) {
1235
- const latest = (0, import_react2.useRef)(options);
1236
- latest.current = options;
1237
- const [items, setItems] = (0, import_react2.useState)([]);
1238
- const itemsRef = (0, import_react2.useRef)(items);
1811
+ function useAttachments(options = {}) {
1812
+ const backend = useAgentBackend(options.backend);
1813
+ const latest = (0, import_react6.useRef)({
1814
+ ...options,
1815
+ backend
1816
+ });
1817
+ latest.current = {
1818
+ ...options,
1819
+ backend
1820
+ };
1821
+ const [items, setItems] = (0, import_react6.useState)([]);
1822
+ const itemsRef = (0, import_react6.useRef)(items);
1239
1823
  itemsRef.current = items;
1240
- const controllers = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
1241
- const [dragDepth, setDragDepth] = (0, import_react2.useState)(0);
1242
- const patch = (0, import_react2.useCallback)((id, change) => {
1824
+ const controllers = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
1825
+ const [dragDepth, setDragDepth] = (0, import_react6.useState)(0);
1826
+ const patch = (0, import_react6.useCallback)((id, change) => {
1243
1827
  setItems((current) => current.map((item) => item.id === id ? {
1244
1828
  ...item,
1245
1829
  ...change
1246
1830
  } : item));
1247
1831
  }, []);
1248
- const start = (0, import_react2.useCallback)((item) => {
1249
- const { upload, backend } = latest.current;
1250
- const run = upload ?? (backend?.uploadAttachment !== void 0 ? backend.uploadAttachment.bind(backend) : void 0);
1832
+ const start = (0, import_react6.useCallback)((item) => {
1833
+ const { upload, backend: backend2 } = latest.current;
1834
+ const run = upload ?? (backend2.uploadAttachment !== void 0 ? backend2.uploadAttachment.bind(backend2) : void 0);
1251
1835
  if (run === void 0) {
1252
1836
  patch(item.id, {
1253
1837
  status: "error",
@@ -1281,7 +1865,7 @@ function useAttachments(options) {
1281
1865
  }, [
1282
1866
  patch
1283
1867
  ]);
1284
- const add = (0, import_react2.useCallback)((files) => {
1868
+ const add = (0, import_react6.useCallback)((files) => {
1285
1869
  if (files == null) return;
1286
1870
  const { accept: accept2, maxBytes, maxFiles, describeRejection } = latest.current;
1287
1871
  const describe = /* @__PURE__ */ __name((reason, file) => describeRejection?.(reason, file) ?? DEFAULT_REJECTION[reason](file), "describe");
@@ -1327,7 +1911,7 @@ function useAttachments(options) {
1327
1911
  }, [
1328
1912
  start
1329
1913
  ]);
1330
- const release = (0, import_react2.useCallback)((item) => {
1914
+ const release = (0, import_react6.useCallback)((item) => {
1331
1915
  if (item === void 0) return;
1332
1916
  controllers.current.get(item.id)?.abort();
1333
1917
  controllers.current.delete(item.id);
@@ -1335,19 +1919,19 @@ function useAttachments(options) {
1335
1919
  URL.revokeObjectURL(item.previewUrl);
1336
1920
  }
1337
1921
  }, []);
1338
- const remove = (0, import_react2.useCallback)((id) => {
1922
+ const remove = (0, import_react6.useCallback)((id) => {
1339
1923
  release(itemsRef.current.find((item) => item.id === id));
1340
1924
  setItems((current) => current.filter((item) => item.id !== id));
1341
1925
  }, [
1342
1926
  release
1343
1927
  ]);
1344
- const clear = (0, import_react2.useCallback)(() => {
1928
+ const clear = (0, import_react6.useCallback)(() => {
1345
1929
  for (const item of itemsRef.current) release(item);
1346
1930
  setItems([]);
1347
1931
  }, [
1348
1932
  release
1349
1933
  ]);
1350
- const retry = (0, import_react2.useCallback)((id) => {
1934
+ const retry = (0, import_react6.useCallback)((id) => {
1351
1935
  const item = itemsRef.current.find((candidate) => candidate.id === id);
1352
1936
  if (item === void 0 || item.status !== "error") return;
1353
1937
  const { error: _previous, ...rest } = item;
@@ -1361,12 +1945,12 @@ function useAttachments(options) {
1361
1945
  }, [
1362
1946
  start
1363
1947
  ]);
1364
- (0, import_react2.useEffect)(() => () => {
1948
+ (0, import_react6.useEffect)(() => () => {
1365
1949
  for (const item of itemsRef.current) release(item);
1366
1950
  }, [
1367
1951
  release
1368
1952
  ]);
1369
- const onPaste = (0, import_react2.useCallback)((event) => {
1953
+ const onPaste = (0, import_react6.useCallback)((event) => {
1370
1954
  const files = filesFromClipboard(event.clipboardData);
1371
1955
  if (files.length === 0) return;
1372
1956
  event.preventDefault();
@@ -1374,7 +1958,7 @@ function useAttachments(options) {
1374
1958
  }, [
1375
1959
  add
1376
1960
  ]);
1377
- const dropZoneProps = (0, import_react2.useMemo)(() => ({
1961
+ const dropZoneProps = (0, import_react6.useMemo)(() => ({
1378
1962
  onDragEnter: /* @__PURE__ */ __name((event) => {
1379
1963
  if (!dragHasFiles(event.dataTransfer)) return;
1380
1964
  event.preventDefault();
@@ -1397,7 +1981,7 @@ function useAttachments(options) {
1397
1981
  add
1398
1982
  ]);
1399
1983
  const accept = options.accept;
1400
- const inputProps = (0, import_react2.useMemo)(() => ({
1984
+ const inputProps = (0, import_react6.useMemo)(() => ({
1401
1985
  type: "file",
1402
1986
  accept: typeof accept === "string" ? accept : accept?.join(","),
1403
1987
  multiple: options.maxFiles === void 0 || options.maxFiles > 1,
@@ -1411,7 +1995,7 @@ function useAttachments(options) {
1411
1995
  options.maxFiles,
1412
1996
  add
1413
1997
  ]);
1414
- return (0, import_react2.useMemo)(() => {
1998
+ return (0, import_react6.useMemo)(() => {
1415
1999
  const ready = items.filter((item) => item.status === "ready" && item.attachment !== void 0);
1416
2000
  const attachments = ready.map((item) => item.attachment);
1417
2001
  return {
@@ -1445,18 +2029,18 @@ function useAttachments(options) {
1445
2029
  __name(useAttachments, "useAttachments");
1446
2030
 
1447
2031
  // src/catalog/use-agents.ts
1448
- var import_react4 = require("react");
2032
+ var import_react8 = require("react");
1449
2033
 
1450
2034
  // src/catalog/use-resource.ts
1451
- var import_react3 = require("react");
2035
+ var import_react7 = require("react");
1452
2036
  function useResource(load2, key, enabled) {
1453
- const [data, setData] = (0, import_react3.useState)(void 0);
1454
- const [isLoading, setIsLoading] = (0, import_react3.useState)(enabled);
1455
- const [error, setError] = (0, import_react3.useState)(null);
1456
- const loadRef = (0, import_react3.useRef)(load2);
2037
+ const [data, setData] = (0, import_react7.useState)(void 0);
2038
+ const [isLoading, setIsLoading] = (0, import_react7.useState)(enabled);
2039
+ const [error, setError] = (0, import_react7.useState)(null);
2040
+ const loadRef = (0, import_react7.useRef)(load2);
1457
2041
  loadRef.current = load2;
1458
- const generation = (0, import_react3.useRef)(0);
1459
- const refresh = (0, import_react3.useCallback)(async () => {
2042
+ const generation = (0, import_react7.useRef)(0);
2043
+ const refresh = (0, import_react7.useCallback)(async () => {
1460
2044
  const mine = ++generation.current;
1461
2045
  setIsLoading(true);
1462
2046
  try {
@@ -1471,7 +2055,7 @@ function useResource(load2, key, enabled) {
1471
2055
  if (mine === generation.current) setIsLoading(false);
1472
2056
  }
1473
2057
  }, []);
1474
- (0, import_react3.useEffect)(() => {
2058
+ (0, import_react7.useEffect)(() => {
1475
2059
  if (!enabled) return;
1476
2060
  void refresh();
1477
2061
  }, [
@@ -1489,10 +2073,11 @@ function useResource(load2, key, enabled) {
1489
2073
  __name(useResource, "useResource");
1490
2074
 
1491
2075
  // src/catalog/use-agents.ts
1492
- function useAgents(options) {
1493
- const { backend, enabled = true } = options;
2076
+ function useAgents(options = {}) {
2077
+ const { enabled = true } = options;
2078
+ const backend = useAgentBackend(options.backend);
1494
2079
  const { data, isLoading, error, refresh } = useResource(() => requireBackendMethod(backend, "listAgents")(), "", enabled);
1495
- return (0, import_react4.useMemo)(() => {
2080
+ return (0, import_react8.useMemo)(() => {
1496
2081
  const agents = data ?? [];
1497
2082
  return {
1498
2083
  agents,
@@ -1511,11 +2096,12 @@ function useAgents(options) {
1511
2096
  __name(useAgents, "useAgents");
1512
2097
 
1513
2098
  // src/catalog/use-models.ts
1514
- var import_react5 = require("react");
1515
- function useModels(options) {
1516
- const { backend, agent, enabled = true } = options;
2099
+ var import_react9 = require("react");
2100
+ function useModels(options = {}) {
2101
+ const { agent, enabled = true } = options;
2102
+ const backend = useAgentBackend(options.backend);
1517
2103
  const { data, isLoading, error, refresh } = useResource(() => requireBackendMethod(backend, "listModels")(agent), `${agent ?? ""}`, enabled);
1518
- return (0, import_react5.useMemo)(() => {
2104
+ return (0, import_react9.useMemo)(() => {
1519
2105
  const providers = data?.providers ?? [];
1520
2106
  const models = providers.flatMap((provider) => provider.models.map((model) => ({
1521
2107
  ...model,
@@ -1545,7 +2131,7 @@ function useModels(options) {
1545
2131
  __name(useModels, "useModels");
1546
2132
 
1547
2133
  // src/quota/use-quota.ts
1548
- var import_react6 = require("react");
2134
+ var import_react10 = require("react");
1549
2135
 
1550
2136
  // src/threads/threads-events.ts
1551
2137
  var listeners = /* @__PURE__ */ new WeakMap();
@@ -1569,10 +2155,11 @@ function onThreadsEvent(backend, listener) {
1569
2155
  __name(onThreadsEvent, "onThreadsEvent");
1570
2156
 
1571
2157
  // src/quota/use-quota.ts
1572
- function useQuota(options) {
1573
- const { backend, enabled = true, pollMs } = options;
2158
+ function useQuota(options = {}) {
2159
+ const { enabled = true, pollMs } = options;
2160
+ const backend = useAgentBackend(options.backend);
1574
2161
  const { data, isLoading, error, refresh } = useResource(() => requireBackendMethod(backend, "getQuota")(), "", enabled);
1575
- (0, import_react6.useEffect)(() => {
2162
+ (0, import_react10.useEffect)(() => {
1576
2163
  if (!enabled) return;
1577
2164
  return onThreadsEvent(backend, (event) => {
1578
2165
  if (event.type === "changed") void refresh();
@@ -1582,7 +2169,7 @@ function useQuota(options) {
1582
2169
  enabled,
1583
2170
  refresh
1584
2171
  ]);
1585
- (0, import_react6.useEffect)(() => {
2172
+ (0, import_react10.useEffect)(() => {
1586
2173
  if (!enabled || pollMs === void 0 || pollMs <= 0) return;
1587
2174
  const timer = setInterval(() => void refresh(), pollMs);
1588
2175
  return () => clearInterval(timer);
@@ -1591,7 +2178,7 @@ function useQuota(options) {
1591
2178
  pollMs,
1592
2179
  refresh
1593
2180
  ]);
1594
- return (0, import_react6.useMemo)(() => {
2181
+ return (0, import_react10.useMemo)(() => {
1595
2182
  const windows = data?.windows ?? [];
1596
2183
  return {
1597
2184
  report: data,
@@ -1613,37 +2200,44 @@ function useQuota(options) {
1613
2200
  __name(useQuota, "useQuota");
1614
2201
 
1615
2202
  // src/feedback/use-message-feedback.ts
1616
- var import_react7 = require("react");
1617
- function useMessageFeedback(options) {
1618
- const latest = (0, import_react7.useRef)(options);
1619
- latest.current = options;
1620
- const [local, setLocal] = (0, import_react7.useState)(/* @__PURE__ */ new Map());
1621
- const [pending, setPending] = (0, import_react7.useState)(/* @__PURE__ */ new Set());
1622
- const [error, setError] = (0, import_react7.useState)(null);
1623
- const localRef = (0, import_react7.useRef)(local);
2203
+ var import_react11 = require("react");
2204
+ function useMessageFeedback(options = {}) {
2205
+ const backend = useAgentBackend(options.backend);
2206
+ const latest = (0, import_react11.useRef)({
2207
+ ...options,
2208
+ backend
2209
+ });
2210
+ latest.current = {
2211
+ ...options,
2212
+ backend
2213
+ };
2214
+ const [local, setLocal] = (0, import_react11.useState)(/* @__PURE__ */ new Map());
2215
+ const [pending, setPending] = (0, import_react11.useState)(/* @__PURE__ */ new Set());
2216
+ const [error, setError] = (0, import_react11.useState)(null);
2217
+ const localRef = (0, import_react11.useRef)(local);
1624
2218
  localRef.current = local;
1625
- const resolved = (0, import_react7.useRef)(/* @__PURE__ */ new Map());
1626
- const feedbackOf = (0, import_react7.useCallback)((message) => {
2219
+ const resolved = (0, import_react11.useRef)(/* @__PURE__ */ new Map());
2220
+ const feedbackOf = (0, import_react11.useCallback)((message) => {
1627
2221
  const mine = local.get(message.id);
1628
2222
  if (mine !== void 0) return mine.feedback;
1629
2223
  return metadataOf(message).feedback ?? null;
1630
2224
  }, [
1631
2225
  local
1632
2226
  ]);
1633
- const isPending = (0, import_react7.useCallback)((message) => pending.has(message.id), [
2227
+ const isPending = (0, import_react11.useCallback)((message) => pending.has(message.id), [
1634
2228
  pending
1635
2229
  ]);
1636
- const storedIdFor = (0, import_react7.useCallback)(async (message) => {
2230
+ const storedIdFor = (0, import_react11.useCallback)(async (message) => {
1637
2231
  const runId = metadataOf(message).runId;
1638
2232
  if (runId === void 0) return message.id;
1639
2233
  const known = resolved.current.get(message.id);
1640
2234
  if (known !== void 0) return known;
1641
- const { backend, threadId } = latest.current;
2235
+ const { backend: backend2, threadId } = latest.current;
1642
2236
  const thread = typeof threadId === "function" ? threadId() : threadId;
1643
2237
  if (thread === void 0) {
1644
2238
  throw new Error("useMessageFeedback: a streamed message needs `threadId` to be rated");
1645
2239
  }
1646
- const detail = await backend.getThread(thread);
2240
+ const detail = await backend2.getThread(thread);
1647
2241
  const row = [
1648
2242
  ...detail.messages
1649
2243
  ].reverse().find((candidate) => candidate.role === "assistant" && candidate.runId === runId);
@@ -1653,7 +2247,7 @@ function useMessageFeedback(options) {
1653
2247
  resolved.current.set(message.id, row.id);
1654
2248
  return row.id;
1655
2249
  }, []);
1656
- const rate = (0, import_react7.useCallback)(async (message, value, comment) => {
2250
+ const rate = (0, import_react11.useCallback)(async (message, value, comment) => {
1657
2251
  const setFeedback = requireBackendMethod(latest.current.backend, "setMessageFeedback");
1658
2252
  const had = localRef.current.get(message.id);
1659
2253
  const optimistic = value === null ? null : {
@@ -1699,7 +2293,7 @@ function useMessageFeedback(options) {
1699
2293
  }, [
1700
2294
  storedIdFor
1701
2295
  ]);
1702
- const toggle = (0, import_react7.useCallback)((message, value) => {
2296
+ const toggle = (0, import_react11.useCallback)((message, value) => {
1703
2297
  const mine = localRef.current.get(message.id);
1704
2298
  const current = mine !== void 0 ? mine.feedback : metadataOf(message).feedback ?? null;
1705
2299
  return rate(message, current?.value === value ? null : value);
@@ -1722,16 +2316,17 @@ function metadataOf(message) {
1722
2316
  __name(metadataOf, "metadataOf");
1723
2317
 
1724
2318
  // src/threads/use-threads.ts
1725
- var import_react8 = require("react");
1726
- function useThreads(options) {
1727
- const { backend, enabled = true } = options;
1728
- const [threads, setThreads] = (0, import_react8.useState)([]);
1729
- const [isLoading, setIsLoading] = (0, import_react8.useState)(enabled);
1730
- const [error, setError] = (0, import_react8.useState)(null);
1731
- const threadsRef = (0, import_react8.useRef)(threads);
2319
+ var import_react12 = require("react");
2320
+ function useThreads(options = {}) {
2321
+ const { enabled = true } = options;
2322
+ const backend = useAgentBackend(options.backend);
2323
+ const [threads, setThreads] = (0, import_react12.useState)([]);
2324
+ const [isLoading, setIsLoading] = (0, import_react12.useState)(enabled);
2325
+ const [error, setError] = (0, import_react12.useState)(null);
2326
+ const threadsRef = (0, import_react12.useRef)(threads);
1732
2327
  threadsRef.current = threads;
1733
- const generation = (0, import_react8.useRef)(0);
1734
- const refresh = (0, import_react8.useCallback)(async () => {
2328
+ const generation = (0, import_react12.useRef)(0);
2329
+ const refresh = (0, import_react12.useCallback)(async () => {
1735
2330
  const mine = ++generation.current;
1736
2331
  setIsLoading(true);
1737
2332
  try {
@@ -1748,7 +2343,7 @@ function useThreads(options) {
1748
2343
  }, [
1749
2344
  backend
1750
2345
  ]);
1751
- (0, import_react8.useEffect)(() => {
2346
+ (0, import_react12.useEffect)(() => {
1752
2347
  if (!enabled) return;
1753
2348
  void refresh();
1754
2349
  return onThreadsEvent(backend, (event) => {
@@ -1768,7 +2363,7 @@ function useThreads(options) {
1768
2363
  enabled,
1769
2364
  refresh
1770
2365
  ]);
1771
- const rename = (0, import_react8.useCallback)(async (id, title) => {
2366
+ const rename = (0, import_react12.useCallback)(async (id, title) => {
1772
2367
  const before = threadsRef.current;
1773
2368
  setThreads((current) => current.map((thread) => thread.id === id ? {
1774
2369
  ...thread,
@@ -1790,7 +2385,7 @@ function useThreads(options) {
1790
2385
  }, [
1791
2386
  backend
1792
2387
  ]);
1793
- const remove = (0, import_react8.useCallback)(async (id) => {
2388
+ const remove = (0, import_react12.useCallback)(async (id) => {
1794
2389
  const before = threadsRef.current;
1795
2390
  setThreads((current) => current.filter((thread) => thread.id !== id));
1796
2391
  try {
@@ -1864,29 +2459,11 @@ function backgroundRunsFromThread(messages) {
1864
2459
  }
1865
2460
  __name(backgroundRunsFromThread, "backgroundRunsFromThread");
1866
2461
 
1867
- // src/components/ambient-ui.ts
1868
- var import_react9 = require("react");
1869
- function sharedContext(key) {
1870
- const symbol = Symbol.for(key);
1871
- const holder = globalThis;
1872
- const existing = holder[symbol];
1873
- if (existing !== void 0) return existing;
1874
- const created = (0, import_react9.createContext)(null);
1875
- holder[symbol] = created;
1876
- return created;
1877
- }
1878
- __name(sharedContext, "sharedContext");
1879
- var AmbientRenderUiContext = sharedContext("@dudousxd/nestjs-agent-react:ambient-render-ui");
1880
- function useAmbientRenderUi() {
1881
- return (0, import_react9.useContext)(AmbientRenderUiContext);
1882
- }
1883
- __name(useAmbientRenderUi, "useAmbientRenderUi");
1884
-
1885
2462
  // src/components/chat-input.tsx
1886
- var import_react11 = __toESM(require("react"), 1);
2463
+ var import_react14 = __toESM(require("react"), 1);
1887
2464
 
1888
2465
  // src/components/use-speech-recognition.ts
1889
- var import_react10 = require("react");
2466
+ var import_react13 = require("react");
1890
2467
  function getRecognitionCtor() {
1891
2468
  if (typeof window === "undefined") return null;
1892
2469
  const scope = window;
@@ -1894,15 +2471,15 @@ function getRecognitionCtor() {
1894
2471
  }
1895
2472
  __name(getRecognitionCtor, "getRecognitionCtor");
1896
2473
  function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTranscript }) {
1897
- const recognitionRef = (0, import_react10.useRef)(null);
1898
- const [isListening, setIsListening] = (0, import_react10.useState)(false);
1899
- const ctor = (0, import_react10.useMemo)(() => getRecognitionCtor(), []);
2474
+ const recognitionRef = (0, import_react13.useRef)(null);
2475
+ const [isListening, setIsListening] = (0, import_react13.useState)(false);
2476
+ const ctor = (0, import_react13.useMemo)(() => getRecognitionCtor(), []);
1900
2477
  const isSupported = ctor !== null;
1901
- const finalRef = (0, import_react10.useRef)(onFinalTranscript);
1902
- const interimRef = (0, import_react10.useRef)(onInterimTranscript);
2478
+ const finalRef = (0, import_react13.useRef)(onFinalTranscript);
2479
+ const interimRef = (0, import_react13.useRef)(onInterimTranscript);
1903
2480
  finalRef.current = onFinalTranscript;
1904
2481
  interimRef.current = onInterimTranscript;
1905
- (0, import_react10.useEffect)(() => {
2482
+ (0, import_react13.useEffect)(() => {
1906
2483
  if (!ctor) return;
1907
2484
  const recognition = new ctor();
1908
2485
  recognition.lang = lang;
@@ -1941,7 +2518,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
1941
2518
  ctor,
1942
2519
  lang
1943
2520
  ]);
1944
- const start = (0, import_react10.useCallback)(() => {
2521
+ const start = (0, import_react13.useCallback)(() => {
1945
2522
  const recognition = recognitionRef.current;
1946
2523
  if (!recognition) return;
1947
2524
  try {
@@ -1951,7 +2528,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
1951
2528
  console.warn("SpeechRecognition start failed", error);
1952
2529
  }
1953
2530
  }, []);
1954
- const stop = (0, import_react10.useCallback)(() => {
2531
+ const stop = (0, import_react13.useCallback)(() => {
1955
2532
  const recognition = recognitionRef.current;
1956
2533
  if (!recognition) return;
1957
2534
  try {
@@ -1970,8 +2547,8 @@ __name(useSpeechRecognition, "useSpeechRecognition");
1970
2547
 
1971
2548
  // src/components/chat-input.tsx
1972
2549
  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 }) {
1973
- const [value, setValue] = (0, import_react11.useState)("");
1974
- const [interim, setInterim] = (0, import_react11.useState)("");
2550
+ const [value, setValue] = (0, import_react14.useState)("");
2551
+ const [interim, setInterim] = (0, import_react14.useState)("");
1975
2552
  const speech = useSpeechRecognition({
1976
2553
  lang: speechLang,
1977
2554
  onFinalTranscript: /* @__PURE__ */ __name((text) => {
@@ -1982,7 +2559,7 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
1982
2559
  }, "onFinalTranscript"),
1983
2560
  onInterimTranscript: /* @__PURE__ */ __name((text) => setInterim(text), "onInterimTranscript")
1984
2561
  });
1985
- (0, import_react11.useEffect)(() => {
2562
+ (0, import_react14.useEffect)(() => {
1986
2563
  if (!speech.isListening) setInterim("");
1987
2564
  }, [
1988
2565
  speech.isListening
@@ -2010,9 +2587,9 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
2010
2587
  const displayValue = speech.isListening && interim ? `${value}${value && !value.endsWith(" ") ? " " : ""}${interim}` : value;
2011
2588
  const showMic = enableSpeech && speech.isSupported;
2012
2589
  const showStop = isStreaming && onStop !== void 0;
2013
- return /* @__PURE__ */ import_react11.default.createElement("div", {
2590
+ return /* @__PURE__ */ import_react14.default.createElement("div", {
2014
2591
  className
2015
- }, /* @__PURE__ */ import_react11.default.createElement("textarea", {
2592
+ }, /* @__PURE__ */ import_react14.default.createElement("textarea", {
2016
2593
  className: textareaClassName,
2017
2594
  rows,
2018
2595
  value: displayValue,
@@ -2023,17 +2600,17 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
2023
2600
  }, "onChange"),
2024
2601
  onKeyDown: handleKeyDown,
2025
2602
  placeholder: placeholder ?? "Ask something (Enter to send, Shift+Enter for newline)"
2026
- }), /* @__PURE__ */ import_react11.default.createElement("button", {
2603
+ }), /* @__PURE__ */ import_react14.default.createElement("button", {
2027
2604
  type: "button",
2028
2605
  className: sendButtonClassName,
2029
2606
  onClick: submit,
2030
2607
  disabled: disabled || !value.trim()
2031
- }, sendLabel), showStop ? /* @__PURE__ */ import_react11.default.createElement("button", {
2608
+ }, sendLabel), showStop ? /* @__PURE__ */ import_react14.default.createElement("button", {
2032
2609
  type: "button",
2033
2610
  className: stopButtonClassName,
2034
2611
  onClick: /* @__PURE__ */ __name(() => void onStop(), "onClick"),
2035
2612
  "aria-label": "Stop generating"
2036
- }, stopLabel) : null, showMic ? /* @__PURE__ */ import_react11.default.createElement("button", {
2613
+ }, stopLabel) : null, showMic ? /* @__PURE__ */ import_react14.default.createElement("button", {
2037
2614
  type: "button",
2038
2615
  className: micButtonClassName,
2039
2616
  onClick: toggleMic,
@@ -2046,10 +2623,10 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
2046
2623
  __name(ChatInput, "ChatInput");
2047
2624
 
2048
2625
  // src/components/message-item.tsx
2049
- var import_react14 = __toESM(require("react"), 1);
2626
+ var import_react17 = __toESM(require("react"), 1);
2050
2627
 
2051
2628
  // src/transcript/use-chat-transcript.ts
2052
- var import_react13 = require("react");
2629
+ var import_react16 = require("react");
2053
2630
 
2054
2631
  // src/transcript/model.ts
2055
2632
  var import_nestjs_agent_core2 = require("@dudousxd/nestjs-agent-core");
@@ -2146,21 +2723,21 @@ function fillTemplate(template, context) {
2146
2723
  }).replace(/\s{2,}/g, " ").trim();
2147
2724
  }
2148
2725
  __name(fillTemplate, "fillTemplate");
2149
- function phraseFor(presentation, input, isSettled2, fallback = {
2726
+ function phraseFor(presentation, input, isSettled2, fallback2 = {
2150
2727
  running: "Working",
2151
2728
  done: "Done"
2152
2729
  }) {
2153
- if (presentation === void 0) return isSettled2 ? fallback.done : fallback.running;
2730
+ if (presentation === void 0) return isSettled2 ? fallback2.done : fallback2.running;
2154
2731
  const filled = fillTemplate(isSettled2 ? presentation.done : presentation.running, input);
2155
2732
  return filled === "" ? presentation.label : filled;
2156
2733
  }
2157
2734
  __name(phraseFor, "phraseFor");
2158
2735
 
2159
2736
  // src/presentation/result-view.ts
2160
- function isRecord(value) {
2737
+ function isRecord2(value) {
2161
2738
  return typeof value === "object" && value !== null && !Array.isArray(value);
2162
2739
  }
2163
- __name(isRecord, "isRecord");
2740
+ __name(isRecord2, "isRecord");
2164
2741
  function toText(value) {
2165
2742
  if (value === null || value === void 0) return "\u2014";
2166
2743
  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
@@ -2171,7 +2748,7 @@ function toText(value) {
2171
2748
  }
2172
2749
  __name(toText, "toText");
2173
2750
  function inferResultView(output) {
2174
- if (!isRecord(output)) return void 0;
2751
+ if (!isRecord2(output)) return void 0;
2175
2752
  for (const [key, value] of Object.entries(output)) {
2176
2753
  if (!Array.isArray(value) || value.length === 0) continue;
2177
2754
  if (value.every((entry) => typeof entry === "string")) {
@@ -2180,12 +2757,12 @@ function inferResultView(output) {
2180
2757
  lines: key
2181
2758
  };
2182
2759
  }
2183
- if (value.every(isRecord)) {
2760
+ if (value.every(isRecord2)) {
2184
2761
  const first = value[0];
2185
2762
  return {
2186
2763
  kind: "table",
2187
2764
  rows: key,
2188
- columns: Object.keys(first).filter((column) => !isRecord(first[column])).map((column) => ({
2765
+ columns: Object.keys(first).filter((column) => !isRecord2(first[column])).map((column) => ({
2189
2766
  path: column,
2190
2767
  label: column
2191
2768
  }))
@@ -2561,7 +3138,7 @@ function isSettled(state) {
2561
3138
  __name(isSettled, "isSettled");
2562
3139
  function toolKind(part) {
2563
3140
  const metadata = part.toolMetadata;
2564
- return isRecord2(metadata) && typeof metadata.toolKind === "string" ? metadata.toolKind : void 0;
3141
+ return isRecord3(metadata) && typeof metadata.toolKind === "string" ? metadata.toolKind : void 0;
2565
3142
  }
2566
3143
  __name(toolKind, "toolKind");
2567
3144
  function isAwaitingApproval(part) {
@@ -2576,7 +3153,7 @@ function isAwaitingApproval(part) {
2576
3153
  __name(isAwaitingApproval, "isAwaitingApproval");
2577
3154
  function toolParentId(part) {
2578
3155
  const metadata = part.toolMetadata;
2579
- return isRecord2(metadata) && typeof metadata.parentId === "string" ? metadata.parentId : null;
3156
+ return isRecord3(metadata) && typeof metadata.parentId === "string" ? metadata.parentId : null;
2580
3157
  }
2581
3158
  __name(toolParentId, "toolParentId");
2582
3159
  function nestToolCalls(calls) {
@@ -2643,12 +3220,12 @@ function buildToolCall(part, options, approval, catalog) {
2643
3220
  __name(buildToolCall, "buildToolCall");
2644
3221
  function readElicitationRequest(part) {
2645
3222
  const input = part.input;
2646
- if (!isRecord2(input) || !Array.isArray(input.questions) || input.questions.length === 0) {
3223
+ if (!isRecord3(input) || !Array.isArray(input.questions) || input.questions.length === 0) {
2647
3224
  return null;
2648
3225
  }
2649
3226
  const questions = [];
2650
3227
  for (const candidate of input.questions) {
2651
- if (!isRecord2(candidate) || typeof candidate.id !== "string" || typeof candidate.prompt !== "string") {
3228
+ if (!isRecord3(candidate) || typeof candidate.id !== "string" || typeof candidate.prompt !== "string") {
2652
3229
  return null;
2653
3230
  }
2654
3231
  const input2 = (0, import_nestjs_agent_core2.readElicitationInput)(candidate.input) ?? null;
@@ -2658,7 +3235,7 @@ function readElicitationRequest(part) {
2658
3235
  }
2659
3236
  const options = [];
2660
3237
  for (const option of rawOptions) {
2661
- if (!isRecord2(option) || typeof option.value !== "string" || typeof option.label !== "string") {
3238
+ if (!isRecord3(option) || typeof option.value !== "string" || typeof option.label !== "string") {
2662
3239
  return null;
2663
3240
  }
2664
3241
  options.push({
@@ -2686,7 +3263,7 @@ function readElicitationRequest(part) {
2686
3263
  __name(readElicitationRequest, "readElicitationRequest");
2687
3264
  function readElicitationOutcome(part) {
2688
3265
  const output = part.output;
2689
- if (!isRecord2(output) || !isRecord2(output.answers) || typeof output.skipped !== "boolean") {
3266
+ if (!isRecord3(output) || !isRecord3(output.answers) || typeof output.skipped !== "boolean") {
2690
3267
  return null;
2691
3268
  }
2692
3269
  const answers = {};
@@ -2823,7 +3400,7 @@ function readApprovals(parts) {
2823
3400
  continue;
2824
3401
  }
2825
3402
  const data = part.data;
2826
- if (!isRecord2(data) || typeof data.id !== "string") {
3403
+ if (!isRecord3(data) || typeof data.id !== "string") {
2827
3404
  continue;
2828
3405
  }
2829
3406
  const approval = entry(data.id);
@@ -2879,7 +3456,7 @@ function statusFromPart(call) {
2879
3456
  __name(statusFromPart, "statusFromPart");
2880
3457
  function readUiComponent(part) {
2881
3458
  const data = part.data;
2882
- if (!isRecord2(data) || typeof data.component !== "string") {
3459
+ if (!isRecord3(data) || typeof data.component !== "string") {
2883
3460
  return null;
2884
3461
  }
2885
3462
  const id = typeof data.id === "string" ? data.id : part.id;
@@ -2889,19 +3466,19 @@ function readUiComponent(part) {
2889
3466
  return {
2890
3467
  id,
2891
3468
  component: data.component,
2892
- props: isRecord2(data.props) ? data.props : {},
3469
+ props: isRecord3(data.props) ? data.props : {},
2893
3470
  version: typeof data.version === "number" ? data.version : null,
2894
3471
  toolCallId: typeof data.toolCallId === "string" ? data.toolCallId : null
2895
3472
  };
2896
3473
  }
2897
3474
  __name(readUiComponent, "readUiComponent");
2898
- function isRecord2(value) {
3475
+ function isRecord3(value) {
2899
3476
  return typeof value === "object" && value !== null;
2900
3477
  }
2901
- __name(isRecord2, "isRecord");
3478
+ __name(isRecord3, "isRecord");
2902
3479
  function readQuery(part) {
2903
3480
  const input = part.input;
2904
- if (!isRecord2(input) || typeof input.query !== "string") {
3481
+ if (!isRecord3(input) || typeof input.query !== "string") {
2905
3482
  return null;
2906
3483
  }
2907
3484
  return input.query;
@@ -2909,12 +3486,12 @@ function readQuery(part) {
2909
3486
  __name(readQuery, "readQuery");
2910
3487
  function readPassages(part) {
2911
3488
  const output = part.output;
2912
- if (!isRecord2(output) || !Array.isArray(output.passages) || output.passages.length === 0) {
3489
+ if (!isRecord3(output) || !Array.isArray(output.passages) || output.passages.length === 0) {
2913
3490
  return null;
2914
3491
  }
2915
3492
  const passages = [];
2916
3493
  for (const candidate of output.passages) {
2917
- if (!isRecord2(candidate) || typeof candidate.id !== "string" || typeof candidate.text !== "string") {
3494
+ if (!isRecord3(candidate) || typeof candidate.id !== "string" || typeof candidate.text !== "string") {
2918
3495
  return null;
2919
3496
  }
2920
3497
  passages.push({
@@ -2924,7 +3501,7 @@ function readPassages(part) {
2924
3501
  ...typeof candidate.source === "string" ? {
2925
3502
  source: candidate.source
2926
3503
  } : {},
2927
- ...isRecord2(candidate.metadata) ? {
3504
+ ...isRecord3(candidate.metadata) ? {
2928
3505
  metadata: candidate.metadata
2929
3506
  } : {}
2930
3507
  });
@@ -3026,11 +3603,11 @@ function formatCostUsd(cost) {
3026
3603
  __name(formatCostUsd, "formatCostUsd");
3027
3604
 
3028
3605
  // src/transcript/use-stick-to-bottom.ts
3029
- var import_react12 = require("react");
3606
+ var import_react15 = require("react");
3030
3607
  function useStickToBottom({ contentKey, threshold = 32 }) {
3031
- const containerRef = (0, import_react12.useRef)(null);
3032
- const [isAtBottom, setIsAtBottom] = (0, import_react12.useState)(true);
3033
- const measure = (0, import_react12.useCallback)(() => {
3608
+ const containerRef = (0, import_react15.useRef)(null);
3609
+ const [isAtBottom, setIsAtBottom] = (0, import_react15.useState)(true);
3610
+ const measure = (0, import_react15.useCallback)(() => {
3034
3611
  const element = containerRef.current;
3035
3612
  if (!element) {
3036
3613
  return;
@@ -3040,7 +3617,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
3040
3617
  }, [
3041
3618
  threshold
3042
3619
  ]);
3043
- const scrollToBottom = (0, import_react12.useCallback)(() => {
3620
+ const scrollToBottom = (0, import_react15.useCallback)(() => {
3044
3621
  const element = containerRef.current;
3045
3622
  if (!element) {
3046
3623
  return;
@@ -3048,7 +3625,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
3048
3625
  element.scrollTop = element.scrollHeight;
3049
3626
  setIsAtBottom(true);
3050
3627
  }, []);
3051
- const setContainer = (0, import_react12.useCallback)((element) => {
3628
+ const setContainer = (0, import_react15.useCallback)((element) => {
3052
3629
  containerRef.current = element;
3053
3630
  if (element) {
3054
3631
  measure();
@@ -3056,7 +3633,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
3056
3633
  }, [
3057
3634
  measure
3058
3635
  ]);
3059
- (0, import_react12.useEffect)(() => {
3636
+ (0, import_react15.useEffect)(() => {
3060
3637
  if (isAtBottom) {
3061
3638
  scrollToBottom();
3062
3639
  }
@@ -3065,7 +3642,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
3065
3642
  isAtBottom,
3066
3643
  scrollToBottom
3067
3644
  ]);
3068
- const getContainerProps = (0, import_react12.useCallback)(() => ({
3645
+ const getContainerProps = (0, import_react15.useCallback)(() => ({
3069
3646
  ref: setContainer,
3070
3647
  onScroll: measure
3071
3648
  }), [
@@ -3087,14 +3664,14 @@ var DEFAULT_LOAD_EARLIER_STEP = 50;
3087
3664
  var DEFAULT_COPY_RESET_MS = 1500;
3088
3665
  function useChatTranscript(options) {
3089
3666
  const { messages, status, initialVisibleCount = DEFAULT_VISIBLE_COUNT, loadEarlierStep = DEFAULT_LOAD_EARLIER_STEP, followUps, stickToBottomThreshold } = options;
3090
- const latest = (0, import_react13.useRef)(options);
3667
+ const latest = (0, import_react16.useRef)(options);
3091
3668
  latest.current = options;
3092
- const [visibleCount, setVisibleCount] = (0, import_react13.useState)(initialVisibleCount);
3093
- const [stopRequested, setStopRequested] = (0, import_react13.useState)(false);
3669
+ const [visibleCount, setVisibleCount] = (0, import_react16.useState)(initialVisibleCount);
3670
+ const [stopRequested, setStopRequested] = (0, import_react16.useState)(false);
3094
3671
  const isStreaming = status === "streaming" || status === "reconnecting";
3095
3672
  const isBusy = status === "submitted" || isStreaming;
3096
3673
  const isEmpty = messages.length === 0;
3097
- (0, import_react13.useEffect)(() => {
3674
+ (0, import_react16.useEffect)(() => {
3098
3675
  if (!isBusy) {
3099
3676
  setStopRequested(false);
3100
3677
  }
@@ -3111,7 +3688,7 @@ function useChatTranscript(options) {
3111
3688
  streamingMessageId,
3112
3689
  options
3113
3690
  });
3114
- const stop = (0, import_react13.useCallback)(() => {
3691
+ const stop = (0, import_react16.useCallback)(() => {
3115
3692
  const current = latest.current;
3116
3693
  if (current.status !== "submitted" && current.status !== "streaming" && current.status !== "reconnecting") {
3117
3694
  return;
@@ -3119,8 +3696,8 @@ function useChatTranscript(options) {
3119
3696
  setStopRequested(true);
3120
3697
  void current.onStop?.();
3121
3698
  }, []);
3122
- const loadEarlier = (0, import_react13.useCallback)(() => setVisibleCount((count) => count + (latest.current.loadEarlierStep ?? DEFAULT_LOAD_EARLIER_STEP)), []);
3123
- const showAll = (0, import_react13.useCallback)(() => setVisibleCount(latest.current.messages.length), []);
3699
+ const loadEarlier = (0, import_react16.useCallback)(() => setVisibleCount((count) => count + (latest.current.loadEarlierStep ?? DEFAULT_LOAD_EARLIER_STEP)), []);
3700
+ const showAll = (0, import_react16.useCallback)(() => setVisibleCount(latest.current.messages.length), []);
3124
3701
  const scroll = useStickToBottom({
3125
3702
  contentKey: contentSignature(messages),
3126
3703
  ...stickToBottomThreshold !== void 0 ? {
@@ -3156,7 +3733,7 @@ function useChatTranscript(options) {
3156
3733
  __name(useChatTranscript, "useChatTranscript");
3157
3734
  function useTranscriptItem(options) {
3158
3735
  const { message, isStreaming = false } = options;
3159
- const messagesRef = (0, import_react13.useRef)([
3736
+ const messagesRef = (0, import_react16.useRef)([
3160
3737
  message
3161
3738
  ]);
3162
3739
  if (messagesRef.current[0] !== message) {
@@ -3174,41 +3751,45 @@ function useTranscriptItem(options) {
3174
3751
  }
3175
3752
  __name(useTranscriptItem, "useTranscriptItem");
3176
3753
  function useTranscriptItems({ messages, visibleStart, streamingMessageId, options }) {
3177
- const [drafts, setDrafts] = (0, import_react13.useState)(() => /* @__PURE__ */ new Map());
3178
- const [copied, setCopied] = (0, import_react13.useState)(() => /* @__PURE__ */ new Set());
3179
- const [openReasoning, setOpenReasoning] = (0, import_react13.useState)(() => /* @__PURE__ */ new Map());
3180
- const [picks, setPicks] = (0, import_react13.useState)(() => /* @__PURE__ */ new Map());
3181
- const [settling, setSettling] = (0, import_react13.useState)(() => /* @__PURE__ */ new Map());
3182
- const [settleErrors, setSettleErrors] = (0, import_react13.useState)(() => /* @__PURE__ */ new Map());
3183
- const latest = (0, import_react13.useRef)({
3754
+ const [drafts, setDrafts] = (0, import_react16.useState)(() => /* @__PURE__ */ new Map());
3755
+ const [copied, setCopied] = (0, import_react16.useState)(() => /* @__PURE__ */ new Set());
3756
+ const [openReasoning, setOpenReasoning] = (0, import_react16.useState)(() => /* @__PURE__ */ new Map());
3757
+ const [picks, setPicks] = (0, import_react16.useState)(() => /* @__PURE__ */ new Map());
3758
+ const [settling, setSettling] = (0, import_react16.useState)(() => /* @__PURE__ */ new Map());
3759
+ const [settleErrors, setSettleErrors] = (0, import_react16.useState)(() => /* @__PURE__ */ new Map());
3760
+ const backend = useAgentBackend();
3761
+ const handlers = settleHandlers(options, backend);
3762
+ const latest = (0, import_react16.useRef)({
3184
3763
  options,
3764
+ handlers,
3185
3765
  drafts,
3186
3766
  picks,
3187
3767
  messages
3188
3768
  });
3189
3769
  latest.current = {
3190
3770
  options,
3771
+ handlers,
3191
3772
  drafts,
3192
3773
  picks,
3193
3774
  messages
3194
3775
  };
3195
- const copyTimers = (0, import_react13.useRef)(/* @__PURE__ */ new Map());
3196
- (0, import_react13.useEffect)(() => () => {
3776
+ const copyTimers = (0, import_react16.useRef)(/* @__PURE__ */ new Map());
3777
+ (0, import_react16.useEffect)(() => () => {
3197
3778
  for (const timer of copyTimers.current.values()) {
3198
3779
  clearTimeout(timer);
3199
3780
  }
3200
3781
  copyTimers.current.clear();
3201
3782
  }, []);
3202
- const callbackCache = (0, import_react13.useRef)(/* @__PURE__ */ new Map());
3203
- const reasoningToggleCache = (0, import_react13.useRef)(/* @__PURE__ */ new Map());
3204
- const toggleReasoning = (0, import_react13.useCallback)((key, open) => {
3783
+ const callbackCache = (0, import_react16.useRef)(/* @__PURE__ */ new Map());
3784
+ const reasoningToggleCache = (0, import_react16.useRef)(/* @__PURE__ */ new Map());
3785
+ const toggleReasoning = (0, import_react16.useCallback)((key, open) => {
3205
3786
  setOpenReasoning((current) => {
3206
3787
  const next = new Map(current);
3207
3788
  next.set(key, open ?? !(current.get(key) ?? false));
3208
3789
  return next;
3209
3790
  });
3210
3791
  }, []);
3211
- const stableToggle = (0, import_react13.useCallback)((key) => {
3792
+ const stableToggle = (0, import_react16.useCallback)((key) => {
3212
3793
  let cached = reasoningToggleCache.current.get(key);
3213
3794
  if (!cached) {
3214
3795
  cached = /* @__PURE__ */ __name((open) => toggleReasoning(key, open), "cached");
@@ -3218,10 +3799,10 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
3218
3799
  }, [
3219
3800
  toggleReasoning
3220
3801
  ]);
3221
- const pick = (0, import_react13.useCallback)((toolCallId, questionId, values) => {
3802
+ const pick = (0, import_react16.useCallback)((toolCallId, questionId, values) => {
3222
3803
  setPicks((current) => new Map(current).set(pickKey(toolCallId, questionId), values));
3223
3804
  }, []);
3224
- const settle = (0, import_react13.useCallback)((toolCallId, action, send) => {
3805
+ const settle = (0, import_react16.useCallback)((toolCallId, action, send) => {
3225
3806
  setSettleErrors((current) => {
3226
3807
  if (!current.has(toolCallId)) {
3227
3808
  return current;
@@ -3245,41 +3826,53 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
3245
3826
  failed(error);
3246
3827
  }
3247
3828
  }, []);
3248
- const answer = (0, import_react13.useCallback)((toolCallId) => {
3829
+ const answer = (0, import_react16.useCallback)((toolCallId) => {
3249
3830
  const current = latest.current;
3250
- const onAnswer = current.options.onAnswer;
3831
+ const onAnswer = current.handlers.onAnswer;
3251
3832
  if (!onAnswer) {
3252
3833
  return;
3253
3834
  }
3254
- settle(toolCallId, "answer", () => onAnswer(toolCallId, answersFor(current.picks, toolCallId)));
3835
+ settle(toolCallId, "answer", () => onAnswer({
3836
+ toolCallId,
3837
+ answers: answersFor(current.picks, toolCallId)
3838
+ }));
3255
3839
  }, [
3256
3840
  settle
3257
3841
  ]);
3258
- const skip = (0, import_react13.useCallback)((toolCallId) => {
3259
- const onSkip = latest.current.options.onSkip;
3842
+ const skip = (0, import_react16.useCallback)((toolCallId) => {
3843
+ const onSkip = latest.current.handlers.onSkip;
3260
3844
  if (onSkip) {
3261
- settle(toolCallId, "skip", () => onSkip(toolCallId));
3845
+ settle(toolCallId, "skip", () => onSkip({
3846
+ toolCallId
3847
+ }));
3262
3848
  }
3263
3849
  }, [
3264
3850
  settle
3265
3851
  ]);
3266
- const approve = (0, import_react13.useCallback)((toolCallId, options2) => {
3267
- const onApprove = latest.current.options.onApprove;
3852
+ const approve = (0, import_react16.useCallback)((toolCallId, options2) => {
3853
+ const onApprove = latest.current.handlers.onApprove;
3268
3854
  if (onApprove) {
3269
- settle(toolCallId, "approve", () => options2 === void 0 ? onApprove(toolCallId) : onApprove(toolCallId, options2));
3855
+ settle(toolCallId, "approve", () => onApprove({
3856
+ toolCallId,
3857
+ ...options2?.remember === true ? {
3858
+ remember: true
3859
+ } : {}
3860
+ }));
3270
3861
  }
3271
3862
  }, [
3272
3863
  settle
3273
3864
  ]);
3274
- const reject = (0, import_react13.useCallback)((toolCallId) => {
3275
- const onReject = latest.current.options.onReject;
3865
+ const reject = (0, import_react16.useCallback)((toolCallId) => {
3866
+ const onReject = latest.current.handlers.onReject;
3276
3867
  if (onReject) {
3277
- settle(toolCallId, "reject", () => onReject(toolCallId));
3868
+ settle(toolCallId, "reject", () => onReject({
3869
+ toolCallId
3870
+ }));
3278
3871
  }
3279
3872
  }, [
3280
3873
  settle
3281
3874
  ]);
3282
- const callbacksFor = (0, import_react13.useCallback)((id) => {
3875
+ const callbacksFor = (0, import_react16.useCallback)((id) => {
3283
3876
  const cached = callbackCache.current.get(id);
3284
3877
  if (cached) {
3285
3878
  return cached;
@@ -3329,13 +3922,20 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
3329
3922
  return;
3330
3923
  }
3331
3924
  created.cancelEdit();
3332
- void latest.current.options.onEditSubmit?.(id, next);
3925
+ void latest.current.options.onEditSubmit?.({
3926
+ messageId: id,
3927
+ text: next
3928
+ });
3333
3929
  }, "save"),
3334
3930
  fork: /* @__PURE__ */ __name(() => {
3335
- void latest.current.options.onFork?.(id);
3931
+ void latest.current.options.onFork?.({
3932
+ messageId: id
3933
+ });
3336
3934
  }, "fork"),
3337
3935
  regenerate: /* @__PURE__ */ __name(() => {
3338
- void latest.current.options.onRegenerate?.(id);
3936
+ void latest.current.options.onRegenerate?.({
3937
+ messageId: id
3938
+ });
3339
3939
  }, "regenerate"),
3340
3940
  textareaRef: /* @__PURE__ */ __name((element) => {
3341
3941
  if (!element) {
@@ -3372,7 +3972,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
3372
3972
  const draft = drafts.get(message.id);
3373
3973
  const isEditing = draft !== void 0;
3374
3974
  const text = extractMessageText(message.parts);
3375
- const usage = options.getUsage?.(message) ?? null;
3975
+ const usage = (options.getUsage ?? usageFromMetadata)(message);
3376
3976
  const isUser = message.role === "user";
3377
3977
  const isAssistant = message.role === "assistant";
3378
3978
  const isLastAssistant = isAssistant && message.id === lastAssistantId;
@@ -3394,22 +3994,22 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
3394
3994
  ...options.toolCatalog !== void 0 ? {
3395
3995
  toolCatalog: options.toolCatalog
3396
3996
  } : {},
3397
- ...options.onAnswer !== void 0 ? {
3997
+ ...handlers.onAnswer !== void 0 ? {
3398
3998
  elicitation: {
3399
3999
  picked: /* @__PURE__ */ __name((toolCallId, questionId) => picks.get(pickKey(toolCallId, questionId)), "picked"),
3400
4000
  pick,
3401
4001
  canAnswer: true,
3402
- canSkip: options.onSkip !== void 0,
4002
+ canSkip: handlers.onSkip !== void 0,
3403
4003
  answer,
3404
4004
  skip,
3405
4005
  submitting: /* @__PURE__ */ __name((toolCallId) => settling.get(toolCallId) ?? null, "submitting"),
3406
4006
  errorOf: /* @__PURE__ */ __name((toolCallId) => settleErrors.get(toolCallId) ?? null, "errorOf")
3407
4007
  }
3408
4008
  } : {},
3409
- ...options.onApprove !== void 0 || options.onReject !== void 0 ? {
4009
+ ...handlers.onApprove !== void 0 || handlers.onReject !== void 0 ? {
3410
4010
  approval: {
3411
- canApprove: options.onApprove !== void 0,
3412
- canReject: options.onReject !== void 0,
4011
+ canApprove: handlers.onApprove !== void 0,
4012
+ canReject: handlers.onReject !== void 0,
3413
4013
  approve,
3414
4014
  reject,
3415
4015
  submitting: /* @__PURE__ */ __name((toolCallId) => settling.get(toolCallId) ?? null, "submitting"),
@@ -3419,7 +4019,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
3419
4019
  }),
3420
4020
  text,
3421
4021
  usage: usage ? describeUsage(usage) : null,
3422
- timestamp: describeTimestamp(options.getCreatedAt?.(message)),
4022
+ timestamp: describeTimestamp((options.getCreatedAt ?? createdAtFromMetadata)(message)),
3423
4023
  copy: {
3424
4024
  available: text.length > 0,
3425
4025
  copied: copied.has(message.id),
@@ -3454,6 +4054,43 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
3454
4054
  return items;
3455
4055
  }
3456
4056
  __name(useTranscriptItems, "useTranscriptItems");
4057
+ function settleHandlers(options, backend) {
4058
+ const pick = /* @__PURE__ */ __name((own, fallback2) => own === null ? void 0 : own ?? fallback2, "pick");
4059
+ return {
4060
+ onAnswer: pick(options.onAnswer, backend.answerToolCall ? async (input) => {
4061
+ await backend.answerToolCall?.(input);
4062
+ } : void 0),
4063
+ onSkip: pick(options.onSkip, backend.skipToolCall ? async (input) => {
4064
+ await backend.skipToolCall?.(input);
4065
+ } : void 0),
4066
+ onApprove: pick(options.onApprove, backend.approveToolCall ? async (input) => {
4067
+ await backend.approveToolCall?.(input);
4068
+ } : void 0),
4069
+ onReject: pick(options.onReject, backend.rejectToolCall ? async (input) => {
4070
+ await backend.rejectToolCall?.(input);
4071
+ } : void 0)
4072
+ };
4073
+ }
4074
+ __name(settleHandlers, "settleHandlers");
4075
+ function metadataOf2(message) {
4076
+ const metadata = message.metadata;
4077
+ return metadata !== null && typeof metadata === "object" ? metadata : {};
4078
+ }
4079
+ __name(metadataOf2, "metadataOf");
4080
+ function usageFromMetadata(message) {
4081
+ const usage = metadataOf2(message).usage;
4082
+ if (usage === void 0) return null;
4083
+ return {
4084
+ inputTokens: usage.inputTokens,
4085
+ outputTokens: usage.outputTokens,
4086
+ costUsd: usage.costUsd ?? null
4087
+ };
4088
+ }
4089
+ __name(usageFromMetadata, "usageFromMetadata");
4090
+ function createdAtFromMetadata(message) {
4091
+ return metadataOf2(message).createdAt ?? null;
4092
+ }
4093
+ __name(createdAtFromMetadata, "createdAtFromMetadata");
3457
4094
  function pickKey(toolCallId, questionId) {
3458
4095
  return `${toolCallId}|${questionId}`;
3459
4096
  }
@@ -3503,7 +4140,7 @@ function MessageItem({ message, isStreaming = false, editable, onEditSubmit, reg
3503
4140
  editable
3504
4141
  } : {},
3505
4142
  ...onEditSubmit ? {
3506
- onEditSubmit: /* @__PURE__ */ __name((_id, next) => onEditSubmit(next), "onEditSubmit")
4143
+ onEditSubmit
3507
4144
  } : {},
3508
4145
  ...onFork ? {
3509
4146
  onFork
@@ -3512,7 +4149,7 @@ function MessageItem({ message, isStreaming = false, editable, onEditSubmit, reg
3512
4149
  regeneratable
3513
4150
  } : {},
3514
4151
  ...onRegenerate ? {
3515
- onRegenerate: /* @__PURE__ */ __name(() => onRegenerate(), "onRegenerate")
4152
+ onRegenerate
3516
4153
  } : {},
3517
4154
  ...usage != null ? {
3518
4155
  getUsage: /* @__PURE__ */ __name(() => usage, "getUsage")
@@ -3521,7 +4158,7 @@ function MessageItem({ message, isStreaming = false, editable, onEditSubmit, reg
3521
4158
  getCreatedAt: /* @__PURE__ */ __name(() => createdAt, "getCreatedAt")
3522
4159
  } : {}
3523
4160
  });
3524
- return /* @__PURE__ */ import_react14.default.createElement(MessageItemView, {
4161
+ return /* @__PURE__ */ import_react17.default.createElement(MessageItemView, {
3525
4162
  item,
3526
4163
  ...slots
3527
4164
  });
@@ -3534,51 +4171,51 @@ function MessageItemView({ item, ...given }) {
3534
4171
  renderUi: ambientRenderUi
3535
4172
  } : given;
3536
4173
  const { classNames } = slots;
3537
- const actionRow = /* @__PURE__ */ import_react14.default.createElement("div", {
4174
+ const actionRow = /* @__PURE__ */ import_react17.default.createElement("div", {
3538
4175
  className: classNames?.actions,
3539
4176
  "data-actions-for": item.role
3540
- }, item.copy.available ? /* @__PURE__ */ import_react14.default.createElement("button", {
4177
+ }, item.copy.available ? /* @__PURE__ */ import_react17.default.createElement("button", {
3541
4178
  type: "button",
3542
4179
  onClick: item.copy.copy,
3543
4180
  className: classNames?.actionButton,
3544
4181
  title: item.copy.copied ? "Copied!" : "Copy message text"
3545
- }, item.copy.copied ? "Copied" : "Copy") : null, item.fork.available ? /* @__PURE__ */ import_react14.default.createElement("button", {
4182
+ }, item.copy.copied ? "Copied" : "Copy") : null, item.fork.available ? /* @__PURE__ */ import_react17.default.createElement("button", {
3546
4183
  type: "button",
3547
4184
  onClick: item.fork.run,
3548
4185
  className: classNames?.actionButton,
3549
4186
  title: "Fork thread from this message"
3550
- }, "Fork") : null, item.regenerate.available ? /* @__PURE__ */ import_react14.default.createElement("button", {
4187
+ }, "Fork") : null, item.regenerate.available ? /* @__PURE__ */ import_react17.default.createElement("button", {
3551
4188
  type: "button",
3552
4189
  onClick: item.regenerate.run,
3553
4190
  className: classNames?.actionButton,
3554
4191
  title: "Regenerate response"
3555
- }, "Regenerate") : null, slots.meta, !item.isUser && item.usage ? /* @__PURE__ */ import_react14.default.createElement("span", {
4192
+ }, "Regenerate") : null, slots.meta, !item.isUser && item.usage ? /* @__PURE__ */ import_react17.default.createElement("span", {
3556
4193
  className: classNames?.usage,
3557
4194
  title: `Input ${item.usage.inputTokens.toLocaleString()} \xB7 Output ${item.usage.outputTokens.toLocaleString()} tokens`
3558
- }, item.usage.costLabel, " \xB7 ", item.usage.tokensLabel) : null, item.timestamp ? /* @__PURE__ */ import_react14.default.createElement("span", {
4195
+ }, item.usage.costLabel, " \xB7 ", item.usage.tokensLabel) : null, item.timestamp ? /* @__PURE__ */ import_react17.default.createElement("span", {
3559
4196
  className: classNames?.timestamp,
3560
4197
  title: item.timestamp.absolute
3561
4198
  }, item.timestamp.relative) : null);
3562
4199
  if (item.edit.available) {
3563
- return /* @__PURE__ */ import_react14.default.createElement(EditableUserBubble, {
4200
+ return /* @__PURE__ */ import_react17.default.createElement(EditableUserBubble, {
3564
4201
  item,
3565
4202
  slots,
3566
4203
  actions: actionRow
3567
4204
  });
3568
4205
  }
3569
4206
  const className = joinClasses(classNames?.root, classNames?.byRole?.[item.role]);
3570
- return /* @__PURE__ */ import_react14.default.createElement("div", {
4207
+ return /* @__PURE__ */ import_react17.default.createElement("div", {
3571
4208
  className,
3572
4209
  "data-role": item.role,
3573
4210
  "data-streaming": item.isStreaming ? "true" : void 0
3574
- }, /* @__PURE__ */ import_react14.default.createElement("div", null, renderBlocks(item, slots)), actionRow);
4211
+ }, /* @__PURE__ */ import_react17.default.createElement("div", null, renderBlocks(item, slots)), actionRow);
3575
4212
  }
3576
4213
  __name(MessageItemView, "MessageItemView");
3577
4214
  function renderBlocks(item, slots) {
3578
4215
  const nodes = [];
3579
4216
  for (const block of item.blocks) {
3580
4217
  if (block.kind === "text") {
3581
- nodes.push(/* @__PURE__ */ import_react14.default.createElement("div", {
4218
+ nodes.push(/* @__PURE__ */ import_react17.default.createElement("div", {
3582
4219
  key: block.key,
3583
4220
  className: slots.classNames?.text
3584
4221
  }, slots.renderText ? slots.renderText(block.text, {
@@ -3599,7 +4236,7 @@ function renderBlocks(item, slots) {
3599
4236
  continue;
3600
4237
  }
3601
4238
  if (block.kind === "ui" && slots.renderUi) {
3602
- nodes.push(/* @__PURE__ */ import_react14.default.createElement("div", {
4239
+ nodes.push(/* @__PURE__ */ import_react17.default.createElement("div", {
3603
4240
  key: block.key,
3604
4241
  "data-slot": "ui",
3605
4242
  "data-component": block.component
@@ -3610,18 +4247,18 @@ function renderBlocks(item, slots) {
3610
4247
  }
3611
4248
  __name(renderBlocks, "renderBlocks");
3612
4249
  function renderFilesBlock(block, slots) {
3613
- return /* @__PURE__ */ import_react14.default.createElement("div", {
4250
+ return /* @__PURE__ */ import_react17.default.createElement("div", {
3614
4251
  key: block.key,
3615
4252
  className: slots.classNames?.files,
3616
4253
  "data-files": "true"
3617
- }, slots.renderFiles ? slots.renderFiles(block.files) : block.files.map((file) => /* @__PURE__ */ import_react14.default.createElement("a", {
4254
+ }, slots.renderFiles ? slots.renderFiles(block.files) : block.files.map((file) => /* @__PURE__ */ import_react17.default.createElement("a", {
3618
4255
  key: file.url,
3619
4256
  href: file.url,
3620
4257
  target: "_blank",
3621
4258
  rel: "noreferrer",
3622
4259
  className: slots.classNames?.file,
3623
4260
  title: file.filename ?? file.mediaType
3624
- }, file.isImage ? /* @__PURE__ */ import_react14.default.createElement("img", {
4261
+ }, file.isImage ? /* @__PURE__ */ import_react17.default.createElement("img", {
3625
4262
  src: file.url,
3626
4263
  alt: file.filename ?? "image",
3627
4264
  className: slots.classNames?.fileImage
@@ -3629,17 +4266,17 @@ function renderFilesBlock(block, slots) {
3629
4266
  }
3630
4267
  __name(renderFilesBlock, "renderFilesBlock");
3631
4268
  function renderReasoningBlock(block, slots) {
3632
- return /* @__PURE__ */ import_react14.default.createElement("div", {
4269
+ return /* @__PURE__ */ import_react17.default.createElement("div", {
3633
4270
  key: block.key,
3634
4271
  className: slots.classNames?.reasoning,
3635
4272
  "data-reasoning": "true",
3636
4273
  "data-streaming": block.isStreaming ? "true" : void 0
3637
- }, /* @__PURE__ */ import_react14.default.createElement("button", {
4274
+ }, /* @__PURE__ */ import_react17.default.createElement("button", {
3638
4275
  type: "button",
3639
4276
  className: slots.classNames?.reasoningToggle,
3640
4277
  onClick: /* @__PURE__ */ __name(() => block.toggle(), "onClick"),
3641
4278
  "aria-expanded": block.isOpen
3642
- }, slots.reasoningLabel ?? "Reasoning"), block.isOpen ? /* @__PURE__ */ import_react14.default.createElement("div", {
4279
+ }, slots.reasoningLabel ?? "Reasoning"), block.isOpen ? /* @__PURE__ */ import_react17.default.createElement("div", {
3643
4280
  className: slots.classNames?.reasoningText
3644
4281
  }, slots.renderReasoning ? slots.renderReasoning(block.text, {
3645
4282
  isStreaming: block.isStreaming,
@@ -3650,7 +4287,7 @@ __name(renderReasoningBlock, "renderReasoningBlock");
3650
4287
  function renderToolBlock(item, block, slots) {
3651
4288
  if (slots.renderToolGroup) {
3652
4289
  return [
3653
- /* @__PURE__ */ import_react14.default.createElement(import_react14.default.Fragment, {
4290
+ /* @__PURE__ */ import_react17.default.createElement(import_react17.default.Fragment, {
3654
4291
  key: block.key
3655
4292
  }, slots.renderToolGroup(block.parts, block.key))
3656
4293
  ];
@@ -3661,7 +4298,7 @@ function renderToolBlock(item, block, slots) {
3661
4298
  const renderToolPart = slots.renderToolPart;
3662
4299
  return block.parts.map((part) => {
3663
4300
  const partKey = `${item.id}-${part.toolCallId}`;
3664
- return /* @__PURE__ */ import_react14.default.createElement(import_react14.default.Fragment, {
4301
+ return /* @__PURE__ */ import_react17.default.createElement(import_react17.default.Fragment, {
3665
4302
  key: partKey
3666
4303
  }, renderToolPart(part, partKey));
3667
4304
  });
@@ -3671,14 +4308,14 @@ function EditableUserBubble({ item, slots, actions }) {
3671
4308
  const { classNames } = slots;
3672
4309
  const { edit } = item;
3673
4310
  if (!edit.isEditing) {
3674
- return /* @__PURE__ */ import_react14.default.createElement("div", {
4311
+ return /* @__PURE__ */ import_react17.default.createElement("div", {
3675
4312
  className: joinClasses(classNames?.root, classNames?.byRole?.user),
3676
4313
  "data-role": "user"
3677
- }, /* @__PURE__ */ import_react14.default.createElement("div", {
4314
+ }, /* @__PURE__ */ import_react17.default.createElement("div", {
3678
4315
  className: classNames?.text
3679
4316
  }, slots.renderText ? slots.renderText(item.text, {
3680
4317
  isStreaming: false
3681
- }) : item.text), /* @__PURE__ */ import_react14.default.createElement("button", {
4318
+ }) : item.text), /* @__PURE__ */ import_react17.default.createElement("button", {
3682
4319
  type: "button",
3683
4320
  onClick: edit.start,
3684
4321
  className: classNames?.actionButton,
@@ -3686,18 +4323,18 @@ function EditableUserBubble({ item, slots, actions }) {
3686
4323
  }, "Edit"), actions);
3687
4324
  }
3688
4325
  const textareaProps = edit.getTextareaProps();
3689
- return /* @__PURE__ */ import_react14.default.createElement("div", {
4326
+ return /* @__PURE__ */ import_react17.default.createElement("div", {
3690
4327
  "data-editing": "true"
3691
- }, /* @__PURE__ */ import_react14.default.createElement("textarea", {
4328
+ }, /* @__PURE__ */ import_react17.default.createElement("textarea", {
3692
4329
  ...textareaProps,
3693
4330
  rows: Math.min(8, edit.draft.split("\n").length + 1),
3694
4331
  className: classNames?.editTextarea
3695
- }), /* @__PURE__ */ import_react14.default.createElement("div", {
4332
+ }), /* @__PURE__ */ import_react17.default.createElement("div", {
3696
4333
  className: classNames?.editActions
3697
- }, /* @__PURE__ */ import_react14.default.createElement("button", {
4334
+ }, /* @__PURE__ */ import_react17.default.createElement("button", {
3698
4335
  type: "button",
3699
4336
  onClick: edit.cancel
3700
- }, "Cancel"), /* @__PURE__ */ import_react14.default.createElement("button", {
4337
+ }, "Cancel"), /* @__PURE__ */ import_react17.default.createElement("button", {
3701
4338
  type: "button",
3702
4339
  onClick: edit.save,
3703
4340
  disabled: !edit.canSave
@@ -3907,11 +4544,11 @@ function createSkillsSource(options) {
3907
4544
  if (cached) {
3908
4545
  return cached;
3909
4546
  }
3910
- const listSkills = options.client.listSkills;
4547
+ const listSkills = options.backend.listSkills;
3911
4548
  if (listSkills === void 0) {
3912
4549
  return Promise.reject(new AgentBackendUnsupportedError("listSkills"));
3913
4550
  }
3914
- const pending = listSkills.call(options.client, threadId).then((entries) => entries.map(toItem)).catch((error) => {
4551
+ const pending = listSkills.call(options.backend, threadId).then((entries) => entries.map(toItem)).catch((error) => {
3915
4552
  byThread.delete(key);
3916
4553
  throw error;
3917
4554
  });
@@ -3941,35 +4578,35 @@ function toItem(entry) {
3941
4578
  __name(toItem, "toItem");
3942
4579
 
3943
4580
  // src/composer/use-composer-autocomplete.ts
3944
- var import_react15 = require("react");
4581
+ var import_react18 = require("react");
3945
4582
  function useComposerAutocomplete(options) {
3946
4583
  const { value, sources, acceptOnTab = true } = options;
3947
- const latest = (0, import_react15.useRef)(options);
4584
+ const latest = (0, import_react18.useRef)(options);
3948
4585
  latest.current = options;
3949
- const inputRef = (0, import_react15.useRef)(null);
3950
- const [caret, setCaret] = (0, import_react15.useState)(value.length);
3951
- const [items, setItems] = (0, import_react15.useState)([]);
3952
- const [isLoading, setLoading] = (0, import_react15.useState)(false);
3953
- const [error, setError] = (0, import_react15.useState)(null);
3954
- const [activeIndex, setActiveIndex] = (0, import_react15.useState)(0);
3955
- const [dismissedIndex, setDismissedIndex] = (0, import_react15.useState)(null);
4586
+ const inputRef = (0, import_react18.useRef)(null);
4587
+ const [caret, setCaret] = (0, import_react18.useState)(value.length);
4588
+ const [items, setItems] = (0, import_react18.useState)([]);
4589
+ const [isLoading, setLoading] = (0, import_react18.useState)(false);
4590
+ const [error, setError] = (0, import_react18.useState)(null);
4591
+ const [activeIndex, setActiveIndex] = (0, import_react18.useState)(0);
4592
+ const [dismissedIndex, setDismissedIndex] = (0, import_react18.useState)(null);
3956
4593
  const safeCaret = Math.min(caret, value.length);
3957
- const match = (0, import_react15.useMemo)(() => findActiveTrigger(value, safeCaret, sources), [
4594
+ const match = (0, import_react18.useMemo)(() => findActiveTrigger(value, safeCaret, sources), [
3958
4595
  value,
3959
4596
  safeCaret,
3960
4597
  sources
3961
4598
  ]);
3962
4599
  const isActive = match !== null && dismissedIndex !== match.index;
3963
- const matchRef = (0, import_react15.useRef)(match);
4600
+ const matchRef = (0, import_react18.useRef)(match);
3964
4601
  matchRef.current = match;
3965
- const caretRef = (0, import_react15.useRef)(safeCaret);
4602
+ const caretRef = (0, import_react18.useRef)(safeCaret);
3966
4603
  caretRef.current = safeCaret;
3967
- const itemsRef = (0, import_react15.useRef)(items);
4604
+ const itemsRef = (0, import_react18.useRef)(items);
3968
4605
  itemsRef.current = items;
3969
- const activeIndexRef = (0, import_react15.useRef)(activeIndex);
4606
+ const activeIndexRef = (0, import_react18.useRef)(activeIndex);
3970
4607
  activeIndexRef.current = activeIndex;
3971
4608
  const requestKey = isActive && match ? `${match.source.id}\0${match.query}` : null;
3972
- (0, import_react15.useEffect)(() => {
4609
+ (0, import_react18.useEffect)(() => {
3973
4610
  const current = matchRef.current;
3974
4611
  if (requestKey === null || current === null) {
3975
4612
  setItems([]);
@@ -4017,25 +4654,25 @@ function useComposerAutocomplete(options) {
4017
4654
  ]);
4018
4655
  const isOpen = isActive;
4019
4656
  const activeItem = isOpen ? items[activeIndex] ?? null : null;
4020
- const reactId = (0, import_react15.useId)();
4657
+ const reactId = (0, import_react18.useId)();
4021
4658
  const listboxId = options.id ?? `agent-autocomplete-${reactId}`;
4022
- const optionId = (0, import_react15.useCallback)((index) => `${listboxId}-option-${index}`, [
4659
+ const optionId = (0, import_react18.useCallback)((index) => `${listboxId}-option-${index}`, [
4023
4660
  listboxId
4024
4661
  ]);
4025
- const highlight = (0, import_react15.useCallback)((index) => {
4662
+ const highlight = (0, import_react18.useCallback)((index) => {
4026
4663
  const total = itemsRef.current.length;
4027
4664
  if (total === 0) return;
4028
4665
  setActiveIndex((index % total + total) % total);
4029
4666
  }, []);
4030
- const moveHighlight = (0, import_react15.useCallback)((delta) => highlight(activeIndexRef.current + delta), [
4667
+ const moveHighlight = (0, import_react18.useCallback)((delta) => highlight(activeIndexRef.current + delta), [
4031
4668
  highlight
4032
4669
  ]);
4033
- const dismiss = (0, import_react15.useCallback)(() => {
4670
+ const dismiss = (0, import_react18.useCallback)(() => {
4034
4671
  const current = matchRef.current;
4035
4672
  if (current) setDismissedIndex(current.index);
4036
4673
  }, []);
4037
- const pendingCaret = (0, import_react15.useRef)(null);
4038
- const accept = (0, import_react15.useCallback)((item) => {
4674
+ const pendingCaret = (0, import_react18.useRef)(null);
4675
+ const accept = (0, import_react18.useCallback)((item) => {
4039
4676
  const current = matchRef.current;
4040
4677
  if (!current) return;
4041
4678
  const chosen = item ?? itemsRef.current[activeIndexRef.current];
@@ -4046,16 +4683,16 @@ function useComposerAutocomplete(options) {
4046
4683
  setDismissedIndex(null);
4047
4684
  pendingCaret.current = edit.caret;
4048
4685
  }, []);
4049
- (0, import_react15.useLayoutEffect)(() => {
4686
+ (0, import_react18.useLayoutEffect)(() => {
4050
4687
  const position = pendingCaret.current;
4051
4688
  if (position === null) return;
4052
4689
  pendingCaret.current = null;
4053
4690
  inputRef.current?.setSelectionRange(position, position);
4054
4691
  });
4055
- const readCaret = (0, import_react15.useCallback)((element) => {
4692
+ const readCaret = (0, import_react18.useCallback)((element) => {
4056
4693
  setCaret(element.selectionStart ?? element.value.length);
4057
4694
  }, []);
4058
- const onKeyDown = (0, import_react15.useCallback)((event) => {
4695
+ const onKeyDown = (0, import_react18.useCallback)((event) => {
4059
4696
  if (!isOpen) return;
4060
4697
  const hasItems = itemsRef.current.length > 0;
4061
4698
  switch (event.key) {
@@ -4093,23 +4730,23 @@ function useComposerAutocomplete(options) {
4093
4730
  dismiss,
4094
4731
  moveHighlight
4095
4732
  ]);
4096
- const onChange = (0, import_react15.useCallback)((event) => {
4733
+ const onChange = (0, import_react18.useCallback)((event) => {
4097
4734
  readCaret(event.target);
4098
4735
  const next = findActiveTrigger(event.target.value, event.target.selectionStart ?? event.target.value.length, latest.current.sources);
4099
4736
  setDismissedIndex((current) => current !== null && next?.index === current ? current : null);
4100
4737
  }, [
4101
4738
  readCaret
4102
4739
  ]);
4103
- const onSelect = (0, import_react15.useCallback)((event) => readCaret(event.currentTarget), [
4740
+ const onSelect = (0, import_react18.useCallback)((event) => readCaret(event.currentTarget), [
4104
4741
  readCaret
4105
4742
  ]);
4106
- const onClick = (0, import_react15.useCallback)((event) => readCaret(event.currentTarget), [
4743
+ const onClick = (0, import_react18.useCallback)((event) => readCaret(event.currentTarget), [
4107
4744
  readCaret
4108
4745
  ]);
4109
- const setInput = (0, import_react15.useCallback)((element) => {
4746
+ const setInput = (0, import_react18.useCallback)((element) => {
4110
4747
  inputRef.current = element;
4111
4748
  }, []);
4112
- const getInputProps = (0, import_react15.useCallback)(() => ({
4749
+ const getInputProps = (0, import_react18.useCallback)(() => ({
4113
4750
  ref: setInput,
4114
4751
  onChange,
4115
4752
  onKeyDown,
@@ -4137,7 +4774,7 @@ function useComposerAutocomplete(options) {
4137
4774
  activeIndex,
4138
4775
  optionId
4139
4776
  ]);
4140
- const getListboxProps = (0, import_react15.useCallback)(() => ({
4777
+ const getListboxProps = (0, import_react18.useCallback)(() => ({
4141
4778
  id: listboxId,
4142
4779
  role: "listbox",
4143
4780
  "aria-label": match?.source.label ?? match?.source.id ?? "Suggestions"
@@ -4145,7 +4782,7 @@ function useComposerAutocomplete(options) {
4145
4782
  listboxId,
4146
4783
  match
4147
4784
  ]);
4148
- const getOptionProps = (0, import_react15.useCallback)((index) => ({
4785
+ const getOptionProps = (0, import_react18.useCallback)((index) => ({
4149
4786
  id: optionId(index),
4150
4787
  role: "option",
4151
4788
  "aria-selected": index === activeIndex
@@ -4179,7 +4816,7 @@ function isPromise(value) {
4179
4816
  __name(isPromise, "isPromise");
4180
4817
 
4181
4818
  // src/presentation/use-tool-catalog.ts
4182
- var import_react16 = require("react");
4819
+ var import_react19 = require("react");
4183
4820
  var shared = /* @__PURE__ */ new WeakMap();
4184
4821
  function load(client, agent) {
4185
4822
  let byAgent = shared.get(client);
@@ -4201,13 +4838,14 @@ function load(client, agent) {
4201
4838
  return request;
4202
4839
  }
4203
4840
  __name(load, "load");
4204
- function useToolCatalog(options) {
4205
- const { client, agent, enabled = true } = options;
4206
- const [entries, setEntries] = (0, import_react16.useState)([]);
4207
- const [isLoading, setLoading] = (0, import_react16.useState)(enabled);
4208
- const [error, setError] = (0, import_react16.useState)(null);
4209
- const [generation, setGeneration] = (0, import_react16.useState)(0);
4210
- (0, import_react16.useEffect)(() => {
4841
+ function useToolCatalog(options = {}) {
4842
+ const { agent, enabled = true } = options;
4843
+ const client = useAgentBackend(options.backend);
4844
+ const [entries, setEntries] = (0, import_react19.useState)([]);
4845
+ const [isLoading, setLoading] = (0, import_react19.useState)(enabled);
4846
+ const [error, setError] = (0, import_react19.useState)(null);
4847
+ const [generation, setGeneration] = (0, import_react19.useState)(0);
4848
+ (0, import_react19.useEffect)(() => {
4211
4849
  if (!enabled) {
4212
4850
  setLoading(false);
4213
4851
  return;
@@ -4216,7 +4854,7 @@ function useToolCatalog(options) {
4216
4854
  setLoading(true);
4217
4855
  load(client, agent).then((loaded) => {
4218
4856
  if (cancelled) return;
4219
- setEntries(loaded);
4857
+ setEntries(Array.isArray(loaded) ? loaded : []);
4220
4858
  setError(null);
4221
4859
  setLoading(false);
4222
4860
  }, (failure) => {
@@ -4233,14 +4871,14 @@ function useToolCatalog(options) {
4233
4871
  enabled,
4234
4872
  generation
4235
4873
  ]);
4236
- const refresh = (0, import_react16.useCallback)(() => {
4874
+ const refresh = (0, import_react19.useCallback)(() => {
4237
4875
  shared.get(client)?.delete(agent ?? "");
4238
4876
  setGeneration((current) => current + 1);
4239
4877
  }, [
4240
4878
  client,
4241
4879
  agent
4242
4880
  ]);
4243
- const catalog = (0, import_react16.useMemo)(() => toolCatalogFrom(entries), [
4881
+ const catalog = (0, import_react19.useMemo)(() => toolCatalogFrom(entries), [
4244
4882
  entries
4245
4883
  ]);
4246
4884
  return {
@@ -4254,7 +4892,7 @@ function useToolCatalog(options) {
4254
4892
  __name(useToolCatalog, "useToolCatalog");
4255
4893
 
4256
4894
  // src/approvals/countdown.ts
4257
- var import_react17 = require("react");
4895
+ var import_react20 = require("react");
4258
4896
  function approvalCountdown(expiresAt, now = Date.now()) {
4259
4897
  const deadline = expiresAt == null ? Number.NaN : Date.parse(expiresAt);
4260
4898
  if (!Number.isFinite(deadline)) {
@@ -4272,10 +4910,10 @@ function approvalCountdown(expiresAt, now = Date.now()) {
4272
4910
  __name(approvalCountdown, "approvalCountdown");
4273
4911
  function useApprovalCountdown(expiresAt, options = {}) {
4274
4912
  const { intervalMs = 1e3 } = options;
4275
- const nowRef = (0, import_react17.useRef)(options.now ?? Date.now);
4913
+ const nowRef = (0, import_react20.useRef)(options.now ?? Date.now);
4276
4914
  nowRef.current = options.now ?? Date.now;
4277
- const [state, setState] = (0, import_react17.useState)(() => approvalCountdown(expiresAt, nowRef.current()));
4278
- (0, import_react17.useEffect)(() => {
4915
+ const [state, setState] = (0, import_react20.useState)(() => approvalCountdown(expiresAt, nowRef.current()));
4916
+ (0, import_react20.useEffect)(() => {
4279
4917
  const read = /* @__PURE__ */ __name(() => approvalCountdown(expiresAt, nowRef.current()), "read");
4280
4918
  const first = read();
4281
4919
  setState(first);
@@ -4444,14 +5082,27 @@ function storedMessageToUiMessage(message) {
4444
5082
  });
4445
5083
  parts.push(...pushedByCall.get(call.id) ?? []);
4446
5084
  }
5085
+ const metadata = {
5086
+ ...message.feedback !== void 0 ? {
5087
+ feedback: message.feedback
5088
+ } : {},
5089
+ ...message.createdAt ? {
5090
+ createdAt: message.createdAt
5091
+ } : {},
5092
+ ...message.usage !== void 0 ? {
5093
+ usage: {
5094
+ inputTokens: message.usage.inputTokens,
5095
+ outputTokens: message.usage.outputTokens,
5096
+ costUsd: message.usage.costUsd ?? null
5097
+ }
5098
+ } : {}
5099
+ };
4447
5100
  return {
4448
5101
  id: message.id,
4449
5102
  role: message.role,
4450
5103
  parts,
4451
- ...message.feedback !== void 0 ? {
4452
- metadata: {
4453
- feedback: message.feedback
4454
- }
5104
+ ...Object.keys(metadata).length > 0 ? {
5105
+ metadata
4455
5106
  } : {}
4456
5107
  };
4457
5108
  }
@@ -4489,12 +5140,16 @@ function mergeTurn(rows) {
4489
5140
  const parts = rows.flatMap((row) => storedMessageToUiMessage(row).parts);
4490
5141
  const usage = sumUsage(rows);
4491
5142
  const feedback = lastRow.feedback;
5143
+ const createdAt = rows[0]?.createdAt;
4492
5144
  const metadata = {
4493
5145
  ...usage !== void 0 ? {
4494
5146
  usage
4495
5147
  } : {},
4496
5148
  ...feedback !== void 0 ? {
4497
5149
  feedback
5150
+ } : {},
5151
+ ...createdAt ? {
5152
+ createdAt
4498
5153
  } : {}
4499
5154
  };
4500
5155
  return {
@@ -4522,69 +5177,63 @@ function sumUsage(rows) {
4522
5177
  __name(sumUsage, "sumUsage");
4523
5178
 
4524
5179
  // src/use-agent-chat.ts
4525
- var import_react18 = require("@ai-sdk/react");
4526
- var import_react19 = require("react");
4527
- function useAgentChat(options) {
4528
- const latest = (0, import_react19.useRef)(options);
5180
+ var import_react21 = require("@ai-sdk/react");
5181
+ var import_react22 = require("react");
5182
+ var localChatSequence = 0;
5183
+ function localChatId() {
5184
+ localChatSequence += 1;
5185
+ return `local-chat-${localChatSequence}`;
5186
+ }
5187
+ __name(localChatId, "localChatId");
5188
+ function useAgentChat(options = {}) {
5189
+ const latest = (0, import_react22.useRef)(options);
4529
5190
  latest.current = options;
4530
- const [runId, setRunId] = (0, import_react19.useState)(options.resumeRunId);
4531
- const runIdRef = (0, import_react19.useRef)(runId);
5191
+ const resolved = useAgentBackend(options.backend);
5192
+ const client = (0, import_react22.useRef)(resolved).current;
5193
+ const createdThreadId = (0, import_react22.useRef)(void 0);
5194
+ const chatIdRef = (0, import_react22.useRef)(options.threadId ?? localChatId());
5195
+ const boundThreadRef = (0, import_react22.useRef)(options.threadId);
5196
+ if (options.threadId !== boundThreadRef.current) {
5197
+ boundThreadRef.current = options.threadId;
5198
+ const adopting = options.threadId !== void 0 && options.threadId === createdThreadId.current;
5199
+ if (!adopting) {
5200
+ chatIdRef.current = options.threadId ?? localChatId();
5201
+ createdThreadId.current = void 0;
5202
+ }
5203
+ }
5204
+ const chatId = chatIdRef.current;
5205
+ const [runId, setRunId] = (0, import_react22.useState)(options.resumeRunId);
5206
+ const runIdRef = (0, import_react22.useRef)(runId);
4532
5207
  runIdRef.current = runId;
4533
- const settlingRunIdRef = (0, import_react19.useRef)(void 0);
4534
- const autoResumeRunIdRef = (0, import_react19.useRef)(void 0);
4535
- const [autoResume, setAutoResume] = (0, import_react19.useState)(false);
4536
- const [activeRunId, setActiveRunId] = (0, import_react19.useState)(null);
4537
- const regenerateNext = (0, import_react19.useRef)(false);
4538
- const createdThreadId = (0, import_react19.useRef)(void 0);
4539
- const client = (0, import_react19.useMemo)(() => {
4540
- if (options.backend) return options.backend;
4541
- if (options.client) return options.client;
4542
- return new AgentClient({
4543
- ...options.baseUrl !== void 0 ? {
4544
- baseUrl: options.baseUrl
4545
- } : {},
4546
- ...options.credentials !== void 0 ? {
4547
- credentials: options.credentials
4548
- } : {},
4549
- ...options.fetch !== void 0 ? {
4550
- fetch: options.fetch
4551
- } : {},
4552
- ...options.attachments !== void 0 ? {
4553
- attachments: options.attachments
4554
- } : {},
4555
- getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders")
4556
- });
4557
- }, []);
4558
- const [connection, setConnection] = (0, import_react19.useState)({
5208
+ const settlingRunIdRef = (0, import_react22.useRef)(void 0);
5209
+ const autoResumeRunIdRef = (0, import_react22.useRef)(void 0);
5210
+ const [activeRunId, setActiveRunId] = (0, import_react22.useState)(null);
5211
+ const [isLoadingHistory, setIsLoadingHistory] = (0, import_react22.useState)(false);
5212
+ const [historyError, setHistoryError] = (0, import_react22.useState)(null);
5213
+ const [threadModel, setThreadModel] = (0, import_react22.useState)(null);
5214
+ const [pickedModel, setPickedModel] = (0, import_react22.useState)(void 0);
5215
+ const pickedModelRef = (0, import_react22.useRef)(pickedModel);
5216
+ pickedModelRef.current = pickedModel;
5217
+ const pendingPin = (0, import_react22.useRef)(void 0);
5218
+ const regenerateNext = (0, import_react22.useRef)(false);
5219
+ const currentThreadId = (0, import_react22.useCallback)(() => latest.current.threadId ?? createdThreadId.current, []);
5220
+ const [connection, setConnection] = (0, import_react22.useState)({
4559
5221
  status: "live"
4560
5222
  });
4561
- const resyncAfterSettle = (0, import_react19.useRef)(false);
4562
- (0, import_react19.useEffect)(() => {
4563
- if (!options.resume || options.threadId === void 0) return;
4564
- let cancelled = false;
4565
- const threadId = options.threadId;
4566
- void client.getThread(threadId).then((thread) => {
4567
- if (cancelled) return;
4568
- setActiveRunId(thread.activeRunId ?? null);
4569
- if (thread.activeRunId != null) {
4570
- autoResumeRunIdRef.current = thread.activeRunId;
4571
- setAutoResume(true);
4572
- }
4573
- });
4574
- return () => {
4575
- cancelled = true;
4576
- };
4577
- }, [
4578
- client,
4579
- options.resume,
4580
- options.threadId
4581
- ]);
4582
- const transport = (0, import_react19.useMemo)(() => {
5223
+ const resyncAfterSettle = (0, import_react22.useRef)(false);
5224
+ const transport = (0, import_react22.useMemo)(() => {
4583
5225
  function onMeta(meta) {
4584
5226
  settlingRunIdRef.current = meta.runId;
4585
5227
  setRunId(meta.runId);
4586
5228
  if (latest.current.threadId === void 0 && meta.threadId && createdThreadId.current !== meta.threadId) {
4587
5229
  createdThreadId.current = meta.threadId;
5230
+ const pin = pendingPin.current;
5231
+ if (pin !== void 0) {
5232
+ pendingPin.current = void 0;
5233
+ void client.updateThread(meta.threadId, {
5234
+ model: pin.model
5235
+ }).catch(() => void 0);
5236
+ }
4588
5237
  latest.current.onThreadCreated?.(meta.threadId);
4589
5238
  notifyThreads(client, {
4590
5239
  type: "changed"
@@ -4611,13 +5260,14 @@ function useAgentChat(options) {
4611
5260
  const pageContext = current.getPageContext?.() ?? null;
4612
5261
  const regenerate2 = regenerateNext.current;
4613
5262
  regenerateNext.current = false;
4614
- const threadId = current.threadId ?? createdThreadId.current;
5263
+ const threadId = currentThreadId();
5264
+ const model = current.model ?? pickedModelRef.current;
4615
5265
  return {
4616
5266
  ...threadId !== void 0 ? {
4617
5267
  threadId
4618
5268
  } : {},
4619
- ...current.model !== void 0 ? {
4620
- model: current.model
5269
+ ...model !== void 0 ? {
5270
+ model
4621
5271
  } : {},
4622
5272
  ...pageContext ? {
4623
5273
  pageContext
@@ -4634,13 +5284,11 @@ function useAgentChat(options) {
4634
5284
  }, "onAttemptStart")
4635
5285
  });
4636
5286
  }, []);
4637
- const backgroundRef = (0, import_react19.useRef)(() => Promise.resolve());
4638
- const chat = (0, import_react18.useChat)({
5287
+ const backgroundRef = (0, import_react22.useRef)(() => Promise.resolve());
5288
+ const chat = (0, import_react21.useChat)({
4639
5289
  transport,
4640
- resume: options.resumeRunId !== void 0 || autoResume,
4641
- ...options.threadId !== void 0 ? {
4642
- id: options.threadId
4643
- } : {},
5290
+ id: chatId,
5291
+ resume: options.resumeRunId !== void 0,
4644
5292
  ...options.initialMessages !== void 0 ? {
4645
5293
  messages: options.initialMessages
4646
5294
  } : {},
@@ -4650,7 +5298,7 @@ function useAgentChat(options) {
4650
5298
  const title = part.data?.title;
4651
5299
  if (typeof title === "string") {
4652
5300
  latest.current.onTitle?.(title);
4653
- const threadId = latest.current.threadId ?? createdThreadId.current;
5301
+ const threadId = currentThreadId();
4654
5302
  if (threadId !== void 0) notifyThreads(client, {
4655
5303
  type: "title",
4656
5304
  threadId,
@@ -4683,10 +5331,10 @@ function useAgentChat(options) {
4683
5331
  }
4684
5332
  }, "onFinish")
4685
5333
  });
4686
- const chatRef = (0, import_react19.useRef)(chat);
5334
+ const chatRef = (0, import_react22.useRef)(chat);
4687
5335
  chatRef.current = chat;
4688
5336
  async function resyncFromThread() {
4689
- const threadId = latest.current.threadId ?? createdThreadId.current;
5337
+ const threadId = currentThreadId();
4690
5338
  if (threadId === void 0) return;
4691
5339
  try {
4692
5340
  const thread = await client.getThread(threadId);
@@ -4695,13 +5343,75 @@ function useAgentChat(options) {
4695
5343
  }
4696
5344
  }
4697
5345
  __name(resyncFromThread, "resyncFromThread");
4698
- const turnInFlight = (0, import_react19.useRef)(false);
4699
- const isTurnInFlight = (0, import_react19.useCallback)(() => turnInFlight.current || transport.isAttemptLive, [
5346
+ const firstChatId = (0, import_react22.useRef)(chatId);
5347
+ (0, import_react22.useEffect)(() => {
5348
+ if (chatId === firstChatId.current) return;
5349
+ firstChatId.current = chatId;
5350
+ setRunId(void 0);
5351
+ setActiveRunId(null);
5352
+ setThreadModel(null);
5353
+ setBackgroundRuns([]);
5354
+ setHistoryError(null);
5355
+ autoResumeRunIdRef.current = void 0;
5356
+ pendingPin.current = void 0;
5357
+ }, [
5358
+ chatId
5359
+ ]);
5360
+ (0, import_react22.useEffect)(() => {
5361
+ const threadId = options.threadId;
5362
+ if (threadId === void 0) return;
5363
+ if (threadId === createdThreadId.current) return;
5364
+ const current = latest.current;
5365
+ const wantHistory = current.history !== false && current.initialMessages === void 0;
5366
+ const wantResume = current.resume !== false && current.resumeRunId === void 0;
5367
+ if (!wantHistory && !wantResume) return;
5368
+ let cancelled = false;
5369
+ if (wantHistory) setIsLoadingHistory(true);
5370
+ client.getThread(threadId).then((thread) => {
5371
+ if (cancelled || thread == null) return;
5372
+ const active = thread.activeRunId ?? null;
5373
+ const stored = Array.isArray(thread.messages) ? thread.messages : [];
5374
+ setActiveRunId(active);
5375
+ setThreadModel(thread.model ?? null);
5376
+ setHistoryError(null);
5377
+ const resuming = wantResume && active !== null;
5378
+ if (wantHistory && chatRef.current.messages.length === 0) {
5379
+ const rows = resuming ? stored.filter((message) => !(message.role === "assistant" && message.runId === active)) : stored;
5380
+ chatRef.current.setMessages(storedThreadToUiMessages(rows));
5381
+ }
5382
+ if (resuming) {
5383
+ autoResumeRunIdRef.current = active;
5384
+ void chatRef.current.resumeStream();
5385
+ }
5386
+ }, (error) => {
5387
+ if (cancelled) return;
5388
+ setHistoryError(error instanceof Error ? error : new Error(String(error)));
5389
+ }).finally(() => {
5390
+ if (!cancelled) setIsLoadingHistory(false);
5391
+ });
5392
+ return () => {
5393
+ cancelled = true;
5394
+ setIsLoadingHistory(false);
5395
+ };
5396
+ }, [
5397
+ client,
5398
+ chatId,
5399
+ options.threadId
5400
+ ]);
5401
+ const turnInFlight = (0, import_react22.useRef)(false);
5402
+ const isTurnInFlight = (0, import_react22.useCallback)(() => turnInFlight.current || transport.isAttemptLive, [
4700
5403
  transport
4701
5404
  ]);
4702
- const sendMessage = (0, import_react19.useCallback)(async (...args) => {
4703
- const blocked = latest.current.blocked;
4704
- if (blocked != null) throw new QuotaBlockedError(blocked);
5405
+ const quota = useQuota({
5406
+ backend: client,
5407
+ enabled: options.quota !== false && typeof client.getQuota === "function"
5408
+ });
5409
+ const blocked = options.blocked !== void 0 ? options.blocked : quota.blocked;
5410
+ const blockedRef = (0, import_react22.useRef)(blocked);
5411
+ blockedRef.current = blocked;
5412
+ const sendMessage = (0, import_react22.useCallback)(async (...args) => {
5413
+ const block = blockedRef.current;
5414
+ if (block != null) throw new QuotaBlockedError(block);
4705
5415
  if (isTurnInFlight()) return;
4706
5416
  turnInFlight.current = true;
4707
5417
  try {
@@ -4712,16 +5422,14 @@ function useAgentChat(options) {
4712
5422
  }, [
4713
5423
  isTurnInFlight
4714
5424
  ]);
4715
- const addToolResult = (0, import_react19.useCallback)(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
5425
+ const addToolResult = (0, import_react22.useCallback)(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
4716
5426
  tool,
4717
5427
  toolCallId,
4718
5428
  output
4719
5429
  }), []);
4720
- const [threads, setThreads] = (0, import_react19.useState)([]);
4721
- const [quota, setQuota] = (0, import_react19.useState)(null);
4722
- const [backgroundRuns, setBackgroundRuns] = (0, import_react19.useState)([]);
4723
- const refreshBackground = (0, import_react19.useCallback)(async () => {
4724
- const threadId = latest.current.threadId ?? createdThreadId.current;
5430
+ const [backgroundRuns, setBackgroundRuns] = (0, import_react22.useState)([]);
5431
+ const refreshBackground = (0, import_react22.useCallback)(async () => {
5432
+ const threadId = currentThreadId();
4725
5433
  if (threadId === void 0) {
4726
5434
  return;
4727
5435
  }
@@ -4741,11 +5449,12 @@ function useAgentChat(options) {
4741
5449
  ];
4742
5450
  });
4743
5451
  }, [
4744
- client
5452
+ client,
5453
+ currentThreadId
4745
5454
  ]);
4746
5455
  backgroundRef.current = refreshBackground;
4747
5456
  const isWorking = backgroundRuns.some((run) => run.status === "running");
4748
- (0, import_react19.useEffect)(() => {
5457
+ (0, import_react22.useEffect)(() => {
4749
5458
  if (options.background !== true || options.threadId === void 0) {
4750
5459
  return;
4751
5460
  }
@@ -4753,9 +5462,10 @@ function useAgentChat(options) {
4753
5462
  }, [
4754
5463
  options.background,
4755
5464
  options.threadId,
5465
+ chatId,
4756
5466
  refreshBackground
4757
5467
  ]);
4758
- (0, import_react19.useEffect)(() => {
5468
+ (0, import_react22.useEffect)(() => {
4759
5469
  const every = latest.current.backgroundPollMs ?? 5e3;
4760
5470
  if (latest.current.background !== true || !isWorking || every <= 0) {
4761
5471
  return;
@@ -4766,66 +5476,19 @@ function useAgentChat(options) {
4766
5476
  isWorking,
4767
5477
  refreshBackground
4768
5478
  ]);
4769
- const loadThreads = (0, import_react19.useCallback)(async () => {
4770
- const list = await client.listThreads();
4771
- setThreads(list);
4772
- return list;
4773
- }, [
4774
- client
4775
- ]);
4776
- const loadThread = (0, import_react19.useCallback)((id) => client.getThread(id), [
4777
- client
4778
- ]);
4779
- const deleteThread = (0, import_react19.useCallback)(async (id) => {
4780
- await client.deleteThread(id);
4781
- setThreads((current) => current.filter((thread) => thread.id !== id));
4782
- }, [
4783
- client
4784
- ]);
4785
- const forkThread = (0, import_react19.useCallback)((threadId, messageId) => requireBackendMethod(client, "forkFromMessage")(threadId, messageId), [
4786
- client
4787
- ]);
4788
- const renameThread = (0, import_react19.useCallback)(async (id, title) => {
4789
- await client.updateThread(id, {
4790
- title
4791
- });
4792
- setThreads((current) => current.map((thread) => thread.id === id ? {
4793
- ...thread,
4794
- title
4795
- } : thread));
4796
- }, [
4797
- client
4798
- ]);
4799
- const promoteThread = (0, import_react19.useCallback)(async (id) => {
4800
- await requireBackendMethod(client, "promoteThread")(id);
4801
- await loadThreads();
4802
- }, [
4803
- client,
4804
- loadThreads
4805
- ]);
4806
- const truncateFromMessage = (0, import_react19.useCallback)((threadId, messageId) => requireBackendMethod(client, "truncateFromMessage")(threadId, messageId).then(() => void 0), [
4807
- client
4808
- ]);
4809
- const loadQuota = (0, import_react19.useCallback)(async () => {
4810
- const today = await requireBackendMethod(client, "getQuotaToday")();
4811
- setQuota(today);
4812
- return today;
4813
- }, [
4814
- client
4815
- ]);
4816
- const cancel = (0, import_react19.useCallback)(async () => {
5479
+ const cancel = (0, import_react22.useCallback)(async () => {
4817
5480
  chatRef.current.stop();
4818
- const activeRunId2 = runIdRef.current;
4819
- if (activeRunId2) {
5481
+ const active = runIdRef.current;
5482
+ if (active) {
4820
5483
  try {
4821
- await client.cancelStream(activeRunId2);
5484
+ await client.cancelStream(active);
4822
5485
  } catch {
4823
5486
  }
4824
5487
  }
4825
5488
  }, [
4826
5489
  client
4827
5490
  ]);
4828
- const approve = (0, import_react19.useCallback)(async ({ toolCallId, remember, via }) => {
5491
+ const approve = (0, import_react22.useCallback)(async ({ toolCallId, remember, via }) => {
4829
5492
  await requireBackendMethod(client, "approveToolCall")({
4830
5493
  toolCallId,
4831
5494
  ...remember === true ? {
@@ -4838,7 +5501,7 @@ function useAgentChat(options) {
4838
5501
  }, [
4839
5502
  client
4840
5503
  ]);
4841
- const reject = (0, import_react19.useCallback)(async ({ toolCallId, reason, via }) => {
5504
+ const reject = (0, import_react22.useCallback)(async ({ toolCallId, reason, via }) => {
4842
5505
  await requireBackendMethod(client, "rejectToolCall")({
4843
5506
  toolCallId,
4844
5507
  ...reason !== void 0 ? {
@@ -4851,7 +5514,7 @@ function useAgentChat(options) {
4851
5514
  }, [
4852
5515
  client
4853
5516
  ]);
4854
- const answer = (0, import_react19.useCallback)(async ({ toolCallId, answers }) => {
5517
+ const answer = (0, import_react22.useCallback)(async ({ toolCallId, answers }) => {
4855
5518
  await requireBackendMethod(client, "answerToolCall")({
4856
5519
  toolCallId,
4857
5520
  ...answers !== void 0 ? {
@@ -4861,16 +5524,16 @@ function useAgentChat(options) {
4861
5524
  }, [
4862
5525
  client
4863
5526
  ]);
4864
- const skip = (0, import_react19.useCallback)(async ({ toolCallId }) => {
5527
+ const skip = (0, import_react22.useCallback)(async ({ toolCallId }) => {
4865
5528
  await requireBackendMethod(client, "skipToolCall")({
4866
5529
  toolCallId
4867
5530
  });
4868
5531
  }, [
4869
5532
  client
4870
5533
  ]);
4871
- const regenerate = (0, import_react19.useCallback)(() => {
4872
- const blocked = latest.current.blocked;
4873
- if (blocked != null) throw new QuotaBlockedError(blocked);
5534
+ const regenerate = (0, import_react22.useCallback)((_input) => {
5535
+ const block = blockedRef.current;
5536
+ if (block != null) throw new QuotaBlockedError(block);
4874
5537
  if (isTurnInFlight()) return;
4875
5538
  regenerateNext.current = true;
4876
5539
  turnInFlight.current = true;
@@ -4880,15 +5543,78 @@ function useAgentChat(options) {
4880
5543
  }, [
4881
5544
  isTurnInFlight
4882
5545
  ]);
4883
- const background = {
4884
- runs: backgroundRuns,
4885
- isWorking,
4886
- refresh: refreshBackground
4887
- };
4888
- const setThreadModel = (0, import_react19.useCallback)(async (model) => {
4889
- const threadId = latest.current.threadId ?? createdThreadId.current;
5546
+ const storedIdOf = (0, import_react22.useCallback)(async (messageId, threadId) => {
5547
+ const message = chatRef.current.messages.find((candidate) => candidate.id === messageId);
5548
+ const runOf = message?.metadata?.runId;
5549
+ if (runOf === void 0) return messageId;
5550
+ const thread = await client.getThread(threadId);
5551
+ const row = [
5552
+ ...thread.messages
5553
+ ].reverse().find((candidate) => candidate.role === "assistant" && candidate.runId === runOf);
5554
+ return row?.id ?? messageId;
5555
+ }, [
5556
+ client
5557
+ ]);
5558
+ const fork = (0, import_react22.useCallback)(async ({ messageId, threadId }) => {
5559
+ const source = threadId ?? currentThreadId();
5560
+ if (source === void 0) throw new Error("fork: this chat has no thread yet");
5561
+ const stored = await storedIdOf(messageId, source);
5562
+ const forked = await requireBackendMethod(client, "forkFromMessage")(source, stored);
5563
+ notifyThreads(client, {
5564
+ type: "changed"
5565
+ });
5566
+ latest.current.onThreadCreated?.(forked.id);
5567
+ return forked;
5568
+ }, [
5569
+ client,
5570
+ currentThreadId,
5571
+ storedIdOf
5572
+ ]);
5573
+ const truncateFrom = (0, import_react22.useCallback)(async ({ messageId, threadId }) => {
5574
+ const target = threadId ?? currentThreadId();
5575
+ if (target === void 0) throw new Error("truncateFrom: this chat has no thread yet");
5576
+ const stored = await storedIdOf(messageId, target);
5577
+ await requireBackendMethod(client, "truncateFromMessage")(target, stored);
5578
+ }, [
5579
+ client,
5580
+ currentThreadId,
5581
+ storedIdOf
5582
+ ]);
5583
+ const promote = (0, import_react22.useCallback)(async ({ threadId } = {}) => {
5584
+ const target = threadId ?? currentThreadId();
5585
+ if (target === void 0) throw new Error("promote: this chat has no thread yet");
5586
+ await requireBackendMethod(client, "promoteThread")(target);
5587
+ notifyThreads(client, {
5588
+ type: "changed"
5589
+ });
5590
+ }, [
5591
+ client,
5592
+ currentThreadId
5593
+ ]);
5594
+ const [modelsWanted, setModelsWanted] = (0, import_react22.useState)(false);
5595
+ const modelsWantedRef = (0, import_react22.useRef)(modelsWanted);
5596
+ modelsWantedRef.current = modelsWanted;
5597
+ const catalog = useModels({
5598
+ backend: client,
5599
+ ...options.agent !== void 0 ? {
5600
+ agent: options.agent
5601
+ } : {},
5602
+ enabled: modelsWanted && typeof client.listModels === "function"
5603
+ });
5604
+ const wantModels = (0, import_react22.useCallback)(() => {
5605
+ if (modelsWantedRef.current) return;
5606
+ modelsWantedRef.current = true;
5607
+ queueMicrotask(() => setModelsWanted(true));
5608
+ }, []);
5609
+ const selectModel = (0, import_react22.useCallback)((id) => setPickedModel(id), []);
5610
+ const pinToThread = (0, import_react22.useCallback)(async (model) => {
5611
+ const threadId = currentThreadId();
5612
+ setThreadModel(model);
4890
5613
  if (threadId === void 0) {
4891
- throw new Error("setThreadModel: this chat has no thread yet");
5614
+ pendingPin.current = {
5615
+ model
5616
+ };
5617
+ return;
4892
5618
  }
4893
5619
  await client.updateThread(threadId, {
4894
5620
  model
@@ -4897,10 +5623,99 @@ function useAgentChat(options) {
4897
5623
  type: "changed"
4898
5624
  });
4899
5625
  }, [
4900
- client
5626
+ client,
5627
+ currentThreadId
4901
5628
  ]);
4902
- const getThreadId = (0, import_react19.useCallback)(() => latest.current.threadId ?? createdThreadId.current, []);
5629
+ const models = {
5630
+ get list() {
5631
+ wantModels();
5632
+ return catalog.models;
5633
+ },
5634
+ get providers() {
5635
+ wantModels();
5636
+ return catalog.providers;
5637
+ },
5638
+ selected: options.model ?? pickedModel ?? threadModel ?? catalog.defaultModel ?? null,
5639
+ select: selectModel,
5640
+ pinToThread,
5641
+ isLoading: modelsWanted && catalog.isLoading,
5642
+ error: catalog.error
5643
+ };
4903
5644
  const status = connection.status === "reconnecting" ? "reconnecting" : chat.status;
5645
+ const isBusy = status === "submitted" || status === "streaming" || status === "reconnecting";
5646
+ const files = useAttachments({
5647
+ ...options.composer,
5648
+ backend: client
5649
+ });
5650
+ const [text, setText] = (0, import_react22.useState)("");
5651
+ const blockedBy = blocked != null ? "quota" : files.isUploading ? "uploading" : isBusy ? "busy" : text.trim().length === 0 && files.attachments.length === 0 ? "empty" : null;
5652
+ const composerRef = (0, import_react22.useRef)({
5653
+ text,
5654
+ files,
5655
+ blockedBy
5656
+ });
5657
+ composerRef.current = {
5658
+ text,
5659
+ files,
5660
+ blockedBy
5661
+ };
5662
+ const submit = (0, import_react22.useCallback)(async () => {
5663
+ const current = composerRef.current;
5664
+ if (current.blockedBy !== null) return;
5665
+ const refs = current.files.refs;
5666
+ const draft = current.text.trim();
5667
+ setText("");
5668
+ current.files.clear();
5669
+ await sendMessage({
5670
+ text: draft
5671
+ }, refs.length > 0 ? {
5672
+ body: {
5673
+ attachments: refs
5674
+ }
5675
+ } : void 0);
5676
+ }, [
5677
+ sendMessage
5678
+ ]);
5679
+ const composer = {
5680
+ text,
5681
+ setText,
5682
+ files,
5683
+ canSend: blockedBy === null,
5684
+ blockedBy,
5685
+ submit
5686
+ };
5687
+ const ownCatalog = options.transcript?.toolCatalog;
5688
+ const tools = useToolCatalog({
5689
+ backend: client,
5690
+ ...options.agent !== void 0 ? {
5691
+ agent: options.agent
5692
+ } : {},
5693
+ enabled: ownCatalog === void 0 && typeof client.listTools === "function"
5694
+ });
5695
+ const canFork = typeof client.forkFromMessage === "function";
5696
+ const transcript = useChatTranscript({
5697
+ messages: chat.messages,
5698
+ status,
5699
+ onStop: cancel,
5700
+ onApprove: approve,
5701
+ onReject: reject,
5702
+ onAnswer: answer,
5703
+ onSkip: skip,
5704
+ regeneratable: true,
5705
+ onRegenerate: regenerate,
5706
+ ...canFork ? {
5707
+ onFork: /* @__PURE__ */ __name((input) => fork(input).then(() => void 0), "onFork")
5708
+ } : {},
5709
+ ...ownCatalog === void 0 ? {
5710
+ toolCatalog: tools.catalog
5711
+ } : {},
5712
+ ...options.transcript
5713
+ });
5714
+ const background = {
5715
+ runs: backgroundRuns,
5716
+ isWorking,
5717
+ refresh: refreshBackground
5718
+ };
4904
5719
  return {
4905
5720
  ...chat,
4906
5721
  status,
@@ -4911,34 +5726,38 @@ function useAgentChat(options) {
4911
5726
  /** Sub-agents this conversation started and did not wait for. */
4912
5727
  background,
4913
5728
  runId,
4914
- /** The `resume`-fetched thread's active run id, or `null` once resolved with none. */
5729
+ /** The loaded thread's running turn, or `null` once read with none. */
4915
5730
  activeRunId,
5731
+ /** The thread's history is being read. */
5732
+ isLoadingHistory,
5733
+ /** Reading the thread's history failed. */
5734
+ historyError,
4916
5735
  /**
4917
5736
  * The thread this chat is on right now: the `threadId` option, else the one the backend created
4918
5737
  * on the first send. A getter, because the created id is learned mid-stream.
4919
5738
  */
4920
- getThreadId,
4921
- setThreadModel,
4922
- /** The backend this chat talks to — pass it to `useThreads`, `useMessageFeedback`, … */
5739
+ getThreadId: currentThreadId,
5740
+ /** The backend this chat talks to — the same one every other hook under the provider uses. */
4923
5741
  backend: client,
4924
- /** Same object as {@link backend}; kept for callers that read it under its old name. */
4925
- client,
4926
- threads,
4927
- loadThreads,
4928
- loadThread,
4929
- deleteThread,
4930
- forkThread,
4931
- renameThread,
4932
- promoteThread,
4933
- truncateFromMessage,
5742
+ /** The transcript model, with every action already bound to this chat. */
5743
+ transcript,
5744
+ /** A headless composer: draft, files, send gate, `submit()`. */
5745
+ composer,
5746
+ /** The model picker: `list`, `selected`, `select(id)`, `pinToThread(id)`. */
5747
+ models,
5748
+ /** The caller's budget (`GET <base>/quota`); `quota.blocked` gates sends. */
4934
5749
  quota,
4935
- loadQuota,
5750
+ /** The window blocking sends right now (the `blocked` option, else the quota report's). */
5751
+ blocked,
4936
5752
  cancel,
4937
5753
  approve,
4938
5754
  reject,
4939
5755
  answer,
4940
5756
  skip,
4941
- regenerate
5757
+ regenerate,
5758
+ fork,
5759
+ truncateFrom,
5760
+ promote
4942
5761
  };
4943
5762
  }
4944
5763
  __name(useAgentChat, "useAgentChat");
@@ -4952,20 +5771,13 @@ var QuotaBlockedError = class extends Error {
4952
5771
  this.name = "QuotaBlockedError";
4953
5772
  }
4954
5773
  };
4955
- async function mergeHeaders(options) {
4956
- const dynamic = await options.getHeaders?.() ?? {};
4957
- return {
4958
- ...options.headers,
4959
- ...dynamic
4960
- };
4961
- }
4962
- __name(mergeHeaders, "mergeHeaders");
4963
5774
  // Annotate the CommonJS export names for ESM import in node:
4964
5775
  0 && (module.exports = {
4965
5776
  AgentBackendUnsupportedError,
4966
5777
  AgentChatTransport,
4967
5778
  AgentClient,
4968
5779
  AgentHttpError,
5780
+ AgentProvider,
4969
5781
  AmbientRenderUiContext,
4970
5782
  ChatInput,
4971
5783
  DEFAULT_SPEECH_LANG,
@@ -5011,6 +5823,7 @@ __name(mergeHeaders, "mergeHeaders");
5011
5823
  storedThreadToUiMessages,
5012
5824
  toolCallState,
5013
5825
  toolCatalogFrom,
5826
+ useAgentBackend,
5014
5827
  useAgentChat,
5015
5828
  useAgents,
5016
5829
  useAmbientRenderUi,