@dudousxd/nestjs-agent-react 0.32.0 → 0.33.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
@@ -78,8 +78,10 @@ __export(src_exports, {
78
78
  onThreadsEvent: () => onThreadsEvent,
79
79
  persistSpeechLang: () => persistSpeechLang,
80
80
  phraseFor: () => phraseFor,
81
+ proposalNeedsPolling: () => proposalNeedsPolling,
81
82
  readPath: () => readPath,
82
83
  readReasoningMs: () => readReasoningMs,
84
+ reconcileProposalMessages: () => reconcileProposalMessages,
83
85
  reframeAgUiStream: () => reframeAgUiStream,
84
86
  requireBackendMethod: () => requireBackendMethod,
85
87
  resolveResultView: () => resolveResultView,
@@ -87,6 +89,7 @@ __export(src_exports, {
87
89
  storedThreadToUiMessages: () => storedThreadToUiMessages,
88
90
  toolCallState: () => toolCallState,
89
91
  toolCatalogFrom: () => toolCatalogFrom,
92
+ useActionProposals: () => useActionProposals,
90
93
  useAgentBackend: () => useAgentBackend,
91
94
  useAgentChat: () => useAgentChat,
92
95
  useAgentConfig: () => useAgentConfig,
@@ -121,9 +124,9 @@ function readErrorAnswer(text) {
121
124
  }
122
125
  if (body === null || typeof body !== "object")
123
126
  return { body, message: void 0, code: void 0 };
124
- const record = body;
125
- const message = typeof record.message === "string" && record.message !== "" ? record.message : Array.isArray(record.message) && record.message.length > 0 && record.message.every((each) => typeof each === "string") ? record.message.join("; ") : void 0;
126
- const code = typeof record.code === "string" && record.code !== "" ? record.code : void 0;
127
+ const record2 = body;
128
+ const message = typeof record2.message === "string" && record2.message !== "" ? record2.message : Array.isArray(record2.message) && record2.message.length > 0 && record2.message.every((each) => typeof each === "string") ? record2.message.join("; ") : void 0;
129
+ const code = typeof record2.code === "string" && record2.code !== "" ? record2.code : void 0;
127
130
  return { body, message, code };
128
131
  }
129
132
  async function readErrorResponse(response) {
@@ -200,6 +203,12 @@ var AgentClient = class {
200
203
  if (response.status === 202) {
201
204
  return { body: emptyStream(), queued: await response.json() };
202
205
  }
206
+ if (response.ok && response.headers.get("content-type")?.includes("application/json")) {
207
+ const decision = await response.json();
208
+ if (decision.proposalDecision !== void 0 && typeof decision.threadId === "string")
209
+ return { body: emptyStream(), threadId: decision.threadId, proposalDecision: decision };
210
+ throw new Error("Unexpected JSON chat response");
211
+ }
203
212
  if (!response.ok || !response.body) {
204
213
  throw await this.failure(response, "POST", `${this.agentPath()}/chat`);
205
214
  }
@@ -432,6 +441,54 @@ var AgentClient = class {
432
441
  * `remember` approves later calls of the same tool in the same thread; `via` names the surface
433
442
  * the decision came through (the server records `'web'` when omitted).
434
443
  */
444
+ async listActionProposals({ threadId }) {
445
+ const proposals = /* @__PURE__ */ new Map();
446
+ let after;
447
+ for (; ; ) {
448
+ const route = `/threads/${encodeURIComponent(threadId)}/action-proposals${after === void 0 ? "" : `?after=${encodeURIComponent(JSON.stringify(after))}`}`;
449
+ const response = await this.requestResponse("GET", route);
450
+ const page = await this.handleResponse(
451
+ response,
452
+ "GET",
453
+ `${this.agentPath()}${route}`
454
+ );
455
+ if (!Array.isArray(page)) throw new TypeError("Invalid action proposal page");
456
+ for (const proposal of page) proposals.set(proposal.id, proposal);
457
+ const header = response.headers.get("X-Action-Proposals-Next");
458
+ if (header === null) return [...proposals.values()];
459
+ let next;
460
+ try {
461
+ next = JSON.parse(decodeURIComponent(header));
462
+ } catch {
463
+ throw new TypeError("Invalid action proposal cursor");
464
+ }
465
+ if (typeof next !== "object" || next === null || !("createdAt" in next) || typeof next.createdAt !== "number" || !Number.isSafeInteger(next.createdAt) || !("id" in next) || typeof next.id !== "string" || next.id.length === 0 || next.id.length > 255 || after !== void 0 && (next.createdAt < after.createdAt || next.createdAt === after.createdAt && next.id <= after.id))
466
+ throw new TypeError("Invalid or non-advancing action proposal cursor");
467
+ after = { createdAt: next.createdAt, id: next.id };
468
+ }
469
+ }
470
+ approveActionProposal({
471
+ threadId,
472
+ proposalId,
473
+ remember
474
+ }) {
475
+ return this.request(
476
+ "POST",
477
+ `/threads/${encodeURIComponent(threadId)}/action-proposals/${encodeURIComponent(proposalId)}/approve`,
478
+ remember === void 0 ? {} : { remember }
479
+ );
480
+ }
481
+ rejectActionProposal({
482
+ threadId,
483
+ proposalId,
484
+ reason
485
+ }) {
486
+ return this.request(
487
+ "POST",
488
+ `/threads/${encodeURIComponent(threadId)}/action-proposals/${encodeURIComponent(proposalId)}/reject`,
489
+ reason === void 0 ? {} : { reason }
490
+ );
491
+ }
435
492
  approveToolCall(input) {
436
493
  return this.request("POST", "/tool-call/approve", input);
437
494
  }
@@ -488,6 +545,10 @@ var AgentClient = class {
488
545
  return this.options.credentials !== void 0 ? { credentials: this.options.credentials } : {};
489
546
  }
490
547
  async request(method, route, body) {
548
+ const response = await this.requestResponse(method, route, body);
549
+ return this.handleResponse(response, method, `${this.agentPath()}${route}`);
550
+ }
551
+ async requestResponse(method, route, body) {
491
552
  const path = `${this.agentPath()}${route}`;
492
553
  const response = await this.fetchImpl()(`${this.baseUrl()}${path}`, {
493
554
  method,
@@ -499,7 +560,7 @@ var AgentClient = class {
499
560
  ...body !== void 0 ? { body: JSON.stringify(body) } : {},
500
561
  ...this.credentials()
501
562
  });
502
- return this.handleResponse(response, method, path);
563
+ return response;
503
564
  }
504
565
  /** The error for a refused `response`, already reported to `onHttpError`. */
505
566
  async failure(response, method, path) {
@@ -655,6 +716,10 @@ var AgentChatTransport = class {
655
716
  return queuedChunkStream(response.queued);
656
717
  }
657
718
  this.captureHeaderMeta(response);
719
+ if (response.proposalDecision !== void 0) {
720
+ this.attemptLive = false;
721
+ return proposalDecisionChunkStream(response.proposalDecision);
722
+ }
658
723
  return this.toChunkStream(response, {
659
724
  ...headers !== void 0 ? { headers } : {},
660
725
  ...options.abortSignal ? { signal: options.abortSignal } : {}
@@ -781,7 +846,7 @@ var AgentChatTransport = class {
781
846
  let reasoningSeq = 0;
782
847
  const announced = /* @__PURE__ */ new Set();
783
848
  const toolMetadata = /* @__PURE__ */ new Map();
784
- const record = (meta) => {
849
+ const record2 = (meta) => {
785
850
  streamRunId = meta.runId;
786
851
  this.recordMeta(meta);
787
852
  };
@@ -1069,7 +1134,7 @@ var AgentChatTransport = class {
1069
1134
  }
1070
1135
  if (frame.event === "meta") {
1071
1136
  const meta = parseMeta(frame.data);
1072
- if (meta) record(meta);
1137
+ if (meta) record2(meta);
1073
1138
  } else if (frame.data) {
1074
1139
  const event = parseEvent(frame.data);
1075
1140
  if (event) emit(event);
@@ -1137,10 +1202,10 @@ function parseErrorFrame(data) {
1137
1202
  try {
1138
1203
  const parsed = JSON.parse(data);
1139
1204
  if (parsed !== null && typeof parsed === "object") {
1140
- const record = parsed;
1205
+ const record2 = parsed;
1141
1206
  return {
1142
- code: typeof record.code === "string" && record.code !== "" ? record.code : void 0,
1143
- message: typeof record.message === "string" && record.message !== "" ? record.message : fallback2.message
1207
+ code: typeof record2.code === "string" && record2.code !== "" ? record2.code : void 0,
1208
+ message: typeof record2.message === "string" && record2.message !== "" ? record2.message : fallback2.message
1144
1209
  };
1145
1210
  }
1146
1211
  } catch {
@@ -1180,6 +1245,21 @@ function delay(ms, signal) {
1180
1245
  signal?.addEventListener("abort", done, { once: true });
1181
1246
  });
1182
1247
  }
1248
+ function proposalDecisionChunkStream(decision) {
1249
+ return new ReadableStream({
1250
+ start(controller) {
1251
+ controller.enqueue({ type: "start", messageId: `decision:${crypto.randomUUID()}` });
1252
+ controller.enqueue({ type: "data-proposal-decision", data: decision, transient: true });
1253
+ if (decision.text !== void 0 && decision.text.length > 0) {
1254
+ controller.enqueue({ type: "text-start", id: "decision" });
1255
+ controller.enqueue({ type: "text-delta", id: "decision", delta: decision.text });
1256
+ controller.enqueue({ type: "text-end", id: "decision" });
1257
+ }
1258
+ controller.enqueue({ type: "finish", finishReason: "stop" });
1259
+ controller.close();
1260
+ }
1261
+ });
1262
+ }
1183
1263
 
1184
1264
  // src/attachments/files.ts
1185
1265
  function fileKind(mediaType) {
@@ -1312,6 +1392,10 @@ function toGenerativeUIItem(part) {
1312
1392
  return {
1313
1393
  id,
1314
1394
  component: source.component,
1395
+ ...isRecord(source.componentVersions) && Object.values(source.componentVersions).every(
1396
+ (version) => typeof version === "number" && Number.isSafeInteger(version) && version > 0
1397
+ ) ? { componentVersions: { ...source.componentVersions } } : {},
1398
+ ...typeof source.fallbackText === "string" ? { fallbackText: source.fallbackText } : {},
1315
1399
  props: isRecord(source.props) ? source.props : {},
1316
1400
  version: typeof source.version === "number" ? source.version : null,
1317
1401
  toolCallId: typeof source.toolCallId === "string" ? source.toolCallId : null
@@ -1417,20 +1501,29 @@ function useValidatedProps(catalog, name, props) {
1417
1501
  function useGenerativeUIState(part, options, fallbackTree) {
1418
1502
  const item = (0, import_react3.useMemo)(() => toGenerativeUIItem(part), [part]);
1419
1503
  const name = item?.component ?? "";
1504
+ const definition = options.catalog?.get?.(name);
1505
+ const incompatible = item?.fallbackText !== void 0 && item.version !== null && definition !== void 0 && item.version !== (definition.version ?? 1);
1420
1506
  const resolution = useResolvedComponent(
1421
1507
  name,
1422
1508
  item?.version ?? null,
1423
- options.registry,
1509
+ incompatible ? {} : options.registry,
1424
1510
  item === null ? void 0 : options.resolveComponent,
1425
1511
  fallbackTree
1426
1512
  );
1427
1513
  const empty = (0, import_react3.useMemo)(() => ({}), []);
1428
1514
  const validation = useValidatedProps(
1429
- item === null || name === GENUI_TREE_COMPONENT ? void 0 : options.catalog,
1515
+ item === null || name === GENUI_TREE_COMPONENT || incompatible ? void 0 : options.catalog,
1430
1516
  name,
1431
1517
  item?.props ?? empty
1432
1518
  );
1433
1519
  if (item === null) return null;
1520
+ if (name === GENUI_TREE_COMPONENT && item.fallbackText !== void 0 && (!isRecord(item.props.root) || typeof item.props.root.type !== "string"))
1521
+ return {
1522
+ status: "problem",
1523
+ reason: "invalid",
1524
+ item,
1525
+ issues: [{ path: ["root"], message: "Invalid UI tree root" }]
1526
+ };
1434
1527
  if (resolution.status === "loading") return { status: "loading", item };
1435
1528
  if (resolution.status === "unknown") return { status: "problem", reason: "unknown", item };
1436
1529
  if (resolution.status === "error") {
@@ -1448,7 +1541,8 @@ var import_jsx_runtime = require("react/jsx-runtime");
1448
1541
  var TreeScopeContext = (0, import_react4.createContext)(null);
1449
1542
  function renderFallback(fallback2, problem) {
1450
1543
  if (typeof fallback2 === "function") return fallback2(problem);
1451
- return fallback2 ?? null;
1544
+ if (fallback2 !== void 0) return fallback2;
1545
+ return problem.item.fallbackText !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { whiteSpace: "pre-wrap" }, children: problem.item.fallbackText }) : null;
1452
1546
  }
1453
1547
  var ItemBoundary = class extends import_react4.Component {
1454
1548
  state = { error: void 0, failed: false };
@@ -1480,28 +1574,42 @@ function nodeItem(node, id) {
1480
1574
  function TreeNode({ node, id }) {
1481
1575
  const scope = (0, import_react4.useContext)(TreeScopeContext);
1482
1576
  const item = (0, import_react4.useMemo)(() => nodeItem(node, id), [node, id]);
1577
+ const storedVersion = scope?.componentVersions?.[node.type];
1578
+ const definition = scope?.options.catalog?.get?.(node.type);
1579
+ const incompatible = storedVersion !== void 0 && definition !== void 0 && storedVersion !== (definition.version ?? 1);
1483
1580
  const resolution = useResolvedComponent(
1484
1581
  node.type,
1485
- null,
1486
- scope?.options.registry ?? {},
1582
+ storedVersion ?? null,
1583
+ incompatible ? {} : scope?.options.registry ?? {},
1487
1584
  scope?.options.resolveComponent
1488
1585
  );
1489
- const validation = useValidatedProps(scope?.options.catalog, node.type, item.props);
1586
+ const validation = useValidatedProps(
1587
+ incompatible ? void 0 : scope?.options.catalog,
1588
+ node.type,
1589
+ item.props
1590
+ );
1490
1591
  if (scope === null) return null;
1491
1592
  if (resolution.status === "loading" || validation.status === "pending") return scope.loading;
1492
1593
  if (resolution.status === "unknown") {
1594
+ if (scope.fallback === void 0 && scope.fallbackText !== void 0)
1595
+ throw new Error("Unknown tree component");
1493
1596
  return renderFallback(scope.fallback, { reason: "unknown", item });
1494
1597
  }
1495
1598
  if (resolution.status === "error") {
1599
+ if (scope.fallback === void 0 && scope.fallbackText !== void 0) throw resolution.error;
1496
1600
  return renderFallback(scope.fallback, { reason: "error", item, error: resolution.error });
1497
1601
  }
1498
1602
  if (validation.status === "invalid") {
1603
+ if (scope.fallback === void 0 && scope.fallbackText !== void 0)
1604
+ throw new Error("Invalid tree component");
1499
1605
  return renderFallback(scope.fallback, { reason: "invalid", item, issues: validation.issues });
1500
1606
  }
1501
1607
  const Renderer = resolution.Component;
1502
1608
  const children = Array.isArray(node.children) ? node.children : [];
1503
1609
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Renderer, { ...validation.props, children: children.length > 0 ? children.map((child, index) => {
1504
1610
  const childId = `${id}.${index}`;
1611
+ if (scope.fallback === void 0 && scope.fallbackText !== void 0)
1612
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TreeNode, { node: child, id: childId }, childId);
1505
1613
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1506
1614
  ItemBoundary,
1507
1615
  {
@@ -1523,6 +1631,8 @@ function GenerativeUIScope({
1523
1631
  catalog,
1524
1632
  resolveComponent,
1525
1633
  fallback: fallback2,
1634
+ fallbackText,
1635
+ componentVersions,
1526
1636
  loading = null,
1527
1637
  onError,
1528
1638
  children
@@ -1535,10 +1645,21 @@ function GenerativeUIScope({
1535
1645
  ...resolveComponent !== void 0 ? { resolveComponent } : {}
1536
1646
  },
1537
1647
  fallback: fallback2,
1648
+ ...componentVersions !== void 0 ? { componentVersions } : {},
1649
+ ...fallbackText !== void 0 ? { fallbackText } : {},
1538
1650
  loading,
1539
1651
  onError
1540
1652
  }),
1541
- [registry, catalog, resolveComponent, fallback2, loading, onError]
1653
+ [
1654
+ registry,
1655
+ catalog,
1656
+ resolveComponent,
1657
+ fallback2,
1658
+ fallbackText,
1659
+ componentVersions,
1660
+ loading,
1661
+ onError
1662
+ ]
1542
1663
  );
1543
1664
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TreeScopeContext.Provider, { value: scope, children });
1544
1665
  }
@@ -1598,7 +1719,7 @@ function GenerativeUI({
1598
1719
  onError: ownOnError
1599
1720
  }) {
1600
1721
  const provided = (0, import_react4.useContext)(GenuiContext);
1601
- const fallback2 = ownFallback ?? provided?.fallback;
1722
+ const fallback2 = ownFallback !== void 0 ? ownFallback : provided?.fallback;
1602
1723
  const loading = ownLoading ?? provided?.loading ?? null;
1603
1724
  const onError = ownOnError ?? provided?.onError;
1604
1725
  const options = useMergedOptions({
@@ -1623,6 +1744,8 @@ function GenerativeUI({
1623
1744
  GenerativeUIScope,
1624
1745
  {
1625
1746
  ...options,
1747
+ ...state.item.componentVersions !== void 0 ? { componentVersions: state.item.componentVersions } : {},
1748
+ ...state.item.fallbackText !== void 0 ? { fallbackText: state.item.fallbackText } : {},
1626
1749
  fallback: fallback2,
1627
1750
  loading,
1628
1751
  ...onError !== void 0 ? { onError } : {},
@@ -1638,6 +1761,7 @@ var AgentBackendContext = sharedContext(
1638
1761
  );
1639
1762
  function AgentProvider({
1640
1763
  backend,
1764
+ uiCapabilities,
1641
1765
  baseUrl,
1642
1766
  path,
1643
1767
  headers,
@@ -1669,7 +1793,7 @@ function AgentProvider({
1669
1793
  [backend, baseUrl, path, credentials, fetch, upload]
1670
1794
  );
1671
1795
  const content = genui !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(GenuiProvider, { ...genui, children }) : children;
1672
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AgentBackendContext.Provider, { value, children: content });
1796
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AgentUiCapabilitiesContext.Provider, { value: uiCapabilities ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AgentBackendContext.Provider, { value, children: content }) });
1673
1797
  }
1674
1798
  var fallback;
1675
1799
  function defaultBackend() {
@@ -1680,6 +1804,12 @@ function useAgentBackend(own) {
1680
1804
  const provided = (0, import_react5.useContext)(AgentBackendContext);
1681
1805
  return own ?? provided ?? defaultBackend();
1682
1806
  }
1807
+ var AgentUiCapabilitiesContext = sharedContext(
1808
+ "@dudousxd/nestjs-agent-react:ui-capabilities"
1809
+ );
1810
+ function useAgentUiCapabilities() {
1811
+ return (0, import_react5.useContext)(AgentUiCapabilitiesContext) ?? void 0;
1812
+ }
1683
1813
 
1684
1814
  // src/config/use-agent-config.ts
1685
1815
  var shared = /* @__PURE__ */ new WeakMap();
@@ -1945,7 +2075,7 @@ async function defaultRefusal(response) {
1945
2075
  }
1946
2076
  function defaultForwardedProps(body) {
1947
2077
  const out = {};
1948
- for (const key of ["pageContext", "agent", "model"]) {
2078
+ for (const key of ["pageContext", "agent", "model", "persona", "uiCapabilities"]) {
1949
2079
  if (body[key] !== void 0 && body[key] !== null) out[key] = body[key];
1950
2080
  }
1951
2081
  return Object.keys(out).length > 0 ? out : void 0;
@@ -2979,6 +3109,15 @@ function isActionCall(part) {
2979
3109
  }
2980
3110
  function toolCallState(part) {
2981
3111
  const output = "output" in part ? part.output : void 0;
3112
+ if (output !== null && typeof output === "object" && "proposalId" in output && "executed" in output && output.executed === false)
3113
+ return {
3114
+ status: "awaiting-approval",
3115
+ isSettled: false,
3116
+ isFailed: false,
3117
+ isDenied: false,
3118
+ output,
3119
+ error: null
3120
+ };
2982
3121
  const errorText = "errorText" in part ? part.errorText : void 0;
2983
3122
  const outputError = output !== null && typeof output === "object" && "error" in output ? String(output.error) : void 0;
2984
3123
  const isDenied = part.state === "output-denied";
@@ -3023,7 +3162,11 @@ function describeToolCall(part, catalog, options = {}) {
3023
3162
  icon: presentation?.icon ?? null,
3024
3163
  tone: presentation?.tone ?? "neutral",
3025
3164
  detail: presentation?.detail ?? null,
3026
- confirm: confirm === void 0 ? null : {
3165
+ confirm: options.confirmation !== void 0 ? {
3166
+ title: options.confirmation.title,
3167
+ verb: options.confirmation.verb,
3168
+ detail: options.confirmation.detail ?? null
3169
+ } : confirm === void 0 ? null : {
3027
3170
  title: fillTemplate(confirm.title, input),
3028
3171
  verb: confirm.verb,
3029
3172
  detail: confirm.detail !== void 0 ? fillTemplate(confirm.detail, input) : null
@@ -3037,6 +3180,9 @@ function describeToolCall(part, catalog, options = {}) {
3037
3180
  }
3038
3181
  var STATUS_WEIGHT = {
3039
3182
  running: 4,
3183
+ queued: 4,
3184
+ executing: 4,
3185
+ succeeded: 0,
3040
3186
  "awaiting-approval": 3,
3041
3187
  failed: 2,
3042
3188
  denied: 1,
@@ -3056,7 +3202,7 @@ function groupToolActivity(calls, options = {}) {
3056
3202
  if (corrected.has(call.toolCallId)) continue;
3057
3203
  const presentation = options.catalog?.[call.name];
3058
3204
  const key = options.keyOf?.(call, presentation) ?? presentation?.label ?? call.name;
3059
- const status = toolCallState(call.part).status;
3205
+ const status = call.description.status;
3060
3206
  const phrase = phraseFor(
3061
3207
  presentation,
3062
3208
  call.part.input,
@@ -3298,7 +3444,7 @@ function isAncestor(call, candidate, byId) {
3298
3444
  }
3299
3445
  function buildToolCall(part, options, approval, catalog) {
3300
3446
  const toolCallId = part.toolCallId;
3301
- const awaiting = isAwaitingApproval(part);
3447
+ const awaiting = approval?.target?.kind === "proposal" ? approval.status === "pending" : isAwaitingApproval(part);
3302
3448
  const sending = awaiting ? options?.submitting(toolCallId) ?? null : null;
3303
3449
  return {
3304
3450
  part,
@@ -3308,19 +3454,30 @@ function buildToolCall(part, options, approval, catalog) {
3308
3454
  parentId: toolParentId(part),
3309
3455
  children: [],
3310
3456
  approval,
3311
- description: describeToolCall(part, catalog),
3457
+ description: proposalDescription(
3458
+ describeToolCall(
3459
+ part,
3460
+ catalog,
3461
+ approval?.confirmation === void 0 ? {} : { confirmation: approval.confirmation }
3462
+ ),
3463
+ approval
3464
+ ),
3312
3465
  isAwaitingApproval: awaiting,
3313
3466
  approve: {
3314
3467
  available: awaiting && options?.canApprove === true,
3315
3468
  isSubmitting: sending === "approve",
3316
3469
  // Read by field, not passed through: `onClick={call.approve.run}` hands this a click event,
3317
3470
  // and a host's handler must not receive that as its options.
3318
- run: (approveOptions) => approveOptions?.remember === true ? options?.approve(toolCallId, { remember: true }) : options?.approve(toolCallId)
3471
+ run: (approveOptions) => approval?.target === void 0 ? approveOptions?.remember === true ? options?.approve(toolCallId, { remember: true }) : options?.approve(toolCallId) : options?.approve(
3472
+ toolCallId,
3473
+ approveOptions?.remember === true ? { remember: true } : void 0,
3474
+ approval.target
3475
+ )
3319
3476
  },
3320
3477
  reject: {
3321
3478
  available: awaiting && options?.canReject === true,
3322
3479
  isSubmitting: sending === "reject",
3323
- run: () => options?.reject(toolCallId)
3480
+ run: () => approval?.target === void 0 ? options?.reject(toolCallId) : options?.reject(toolCallId, approval.target)
3324
3481
  },
3325
3482
  error: options?.errorOf(toolCallId) ?? null,
3326
3483
  errorCode: options?.errorCodeOf?.(toolCallId) ?? null
@@ -3470,7 +3627,7 @@ function buildElicitationBlock(key, part, request, options) {
3470
3627
  var UI_PART = "data-ui";
3471
3628
  var APPROVAL_PART = "data-approval-requested";
3472
3629
  var SETTLED_PART = "data-approval-settled";
3473
- var SETTLED_STATUSES = ["approved", "rejected", "expired"];
3630
+ var SETTLED_STATUSES = ["approved", "rejected", "expired", "superseded"];
3474
3631
  function readApprovals(parts) {
3475
3632
  const out = /* @__PURE__ */ new Map();
3476
3633
  const entry = (id) => {
@@ -3491,7 +3648,7 @@ function readApprovals(parts) {
3491
3648
  return found;
3492
3649
  };
3493
3650
  for (const part of parts) {
3494
- if (part.type !== APPROVAL_PART && part.type !== SETTLED_PART) {
3651
+ if (part.type !== APPROVAL_PART && part.type !== SETTLED_PART && part.type !== "data-action-proposal") {
3495
3652
  continue;
3496
3653
  }
3497
3654
  const data = part.data;
@@ -3499,12 +3656,35 @@ function readApprovals(parts) {
3499
3656
  continue;
3500
3657
  }
3501
3658
  const approval = entry(data.id);
3502
- if (part.type === APPROVAL_PART) {
3659
+ if (part.type === APPROVAL_PART || part.type === "data-action-proposal") {
3660
+ if (isRecord4(data.target) && data.target.kind === "proposal" && typeof data.target.proposalId === "string") {
3661
+ approval.target = {
3662
+ kind: "proposal",
3663
+ proposalId: data.target.proposalId,
3664
+ ...typeof data.target.threadId === "string" ? { threadId: data.target.threadId } : {}
3665
+ };
3666
+ }
3667
+ if (part.type === "data-action-proposal") {
3668
+ if (typeof data.status === "string" && ["pending", "approved", "rejected", "expired", "superseded"].includes(data.status))
3669
+ approval.status = data.status === "pending" ? null : data.status;
3670
+ if (data.executionStatus === null || data.executionStatus === "queued" || data.executionStatus === "executing" || data.executionStatus === "succeeded" || data.executionStatus === "failed")
3671
+ approval.executionStatus = data.executionStatus;
3672
+ approval.remember = data.remember === true;
3673
+ approval.decidedBy = typeof data.decidedBy === "string" ? data.decidedBy : null;
3674
+ approval.decidedVia = typeof data.decidedVia === "string" ? data.decidedVia : null;
3675
+ }
3503
3676
  if (typeof data.approver !== "string") {
3504
3677
  continue;
3505
3678
  }
3506
3679
  approval.approver = data.approver;
3507
3680
  approval.expiresAt = typeof data.expiresAt === "string" ? data.expiresAt : null;
3681
+ if (isRecord4(data.confirmation) && typeof data.confirmation.title === "string" && typeof data.confirmation.verb === "string" && (data.confirmation.detail === void 0 || typeof data.confirmation.detail === "string")) {
3682
+ approval.confirmation = {
3683
+ title: data.confirmation.title,
3684
+ verb: data.confirmation.verb,
3685
+ ...typeof data.confirmation.detail === "string" ? { detail: data.confirmation.detail } : {}
3686
+ };
3687
+ }
3508
3688
  approval.reason = typeof data.reason === "string" ? data.reason : null;
3509
3689
  continue;
3510
3690
  }
@@ -3528,9 +3708,12 @@ function approvalOf(parts, call) {
3528
3708
  }
3529
3709
  return {
3530
3710
  approver: parts.approver,
3711
+ ...parts.target === void 0 ? {} : { target: parts.target },
3712
+ ...parts.executionStatus === void 0 ? {} : { executionStatus: parts.executionStatus },
3713
+ ...parts.confirmation !== void 0 ? { confirmation: parts.confirmation } : {},
3531
3714
  expiresAt: parts.expiresAt,
3532
3715
  reason: parts.reason,
3533
- status: parts.status ?? statusFromPart(call),
3716
+ status: parts.status ?? (parts.target?.kind === "proposal" ? "pending" : statusFromPart(call)),
3534
3717
  remember: parts.remember,
3535
3718
  decidedBy: parts.decidedBy,
3536
3719
  decidedVia: parts.decidedVia,
@@ -3560,6 +3743,10 @@ function readUiComponent(part) {
3560
3743
  component: data.component,
3561
3744
  props: isRecord4(data.props) ? data.props : {},
3562
3745
  version: typeof data.version === "number" ? data.version : null,
3746
+ ...isRecord4(data.componentVersions) && Object.values(data.componentVersions).every(
3747
+ (version) => typeof version === "number" && Number.isSafeInteger(version) && version > 0
3748
+ ) ? { componentVersions: { ...data.componentVersions } } : {},
3749
+ ...typeof data.fallbackText === "string" ? { fallbackText: data.fallbackText } : {},
3563
3750
  toolCallId: typeof data.toolCallId === "string" ? data.toolCallId : null
3564
3751
  };
3565
3752
  }
@@ -3670,6 +3857,12 @@ function formatCostUsd(cost) {
3670
3857
  }
3671
3858
  return `$${cost.toFixed(2)}`;
3672
3859
  }
3860
+ function proposalDescription(description, approval) {
3861
+ if (approval?.target?.kind !== "proposal") return description;
3862
+ const status = approval.status === "pending" ? "awaiting-approval" : approval.status === "approved" ? approval.executionStatus ?? "queued" : "denied";
3863
+ const phrase = status === "awaiting-approval" ? "Awaiting approval" : status === "queued" ? "Queued" : status === "executing" ? "Executing" : status === "succeeded" ? "Succeeded" : status === "failed" ? "Failed" : approval.status === "expired" ? "Expired" : approval.status === "superseded" ? "Replaced" : "Rejected";
3864
+ return { ...description, status, phrase, result: null };
3865
+ }
3673
3866
 
3674
3867
  // src/transcript/use-stick-to-bottom.ts
3675
3868
  var import_react16 = require("react");
@@ -3962,23 +4155,31 @@ function useTranscriptItems({
3962
4155
  [settle]
3963
4156
  );
3964
4157
  const approve = (0, import_react17.useCallback)(
3965
- (toolCallId, options2) => {
4158
+ (toolCallId, options2, target) => {
3966
4159
  const onApprove = latest.current.handlers.onApprove;
3967
4160
  if (onApprove) {
3968
4161
  settle(
3969
4162
  toolCallId,
3970
4163
  "approve",
3971
- () => onApprove({ toolCallId, ...options2?.remember === true ? { remember: true } : {} })
4164
+ () => onApprove({
4165
+ toolCallId,
4166
+ ...target === void 0 ? {} : { target },
4167
+ ...options2?.remember === true ? { remember: true } : {}
4168
+ })
3972
4169
  );
3973
4170
  }
3974
4171
  },
3975
4172
  [settle]
3976
4173
  );
3977
4174
  const reject = (0, import_react17.useCallback)(
3978
- (toolCallId) => {
4175
+ (toolCallId, target) => {
3979
4176
  const onReject = latest.current.handlers.onReject;
3980
4177
  if (onReject) {
3981
- settle(toolCallId, "reject", () => onReject({ toolCallId }));
4178
+ settle(
4179
+ toolCallId,
4180
+ "reject",
4181
+ () => onReject({ toolCallId, ...target === void 0 ? {} : { target } })
4182
+ );
3982
4183
  }
3983
4184
  },
3984
4185
  [settle]
@@ -4176,14 +4377,36 @@ function settleHandlers(options, backend) {
4176
4377
  ),
4177
4378
  onApprove: pick(
4178
4379
  options.onApprove,
4179
- backend.approveToolCall ? async (input) => {
4180
- await backend.approveToolCall?.(input);
4380
+ backend.approveToolCall || backend.approveActionProposal ? async (input) => {
4381
+ if (input.target?.kind === "proposal") {
4382
+ if (input.target.threadId === void 0)
4383
+ throw new Error("A proposal decision requires its thread scope");
4384
+ await requireBackendMethod(
4385
+ backend,
4386
+ "approveActionProposal"
4387
+ )({
4388
+ threadId: input.target.threadId,
4389
+ proposalId: input.target.proposalId,
4390
+ ...input.remember === void 0 ? {} : { remember: input.remember }
4391
+ });
4392
+ } else await requireBackendMethod(backend, "approveToolCall")(input);
4181
4393
  } : void 0
4182
4394
  ),
4183
4395
  onReject: pick(
4184
4396
  options.onReject,
4185
- backend.rejectToolCall ? async (input) => {
4186
- await backend.rejectToolCall?.(input);
4397
+ backend.rejectToolCall || backend.rejectActionProposal ? async (input) => {
4398
+ if (input.target?.kind === "proposal") {
4399
+ if (input.target.threadId === void 0)
4400
+ throw new Error("A proposal decision requires its thread scope");
4401
+ await requireBackendMethod(
4402
+ backend,
4403
+ "rejectActionProposal"
4404
+ )({
4405
+ threadId: input.target.threadId,
4406
+ proposalId: input.target.proposalId,
4407
+ ...input.reason === void 0 ? {} : { reason: input.reason }
4408
+ });
4409
+ } else await requireBackendMethod(backend, "rejectToolCall")(input);
4187
4410
  } : void 0
4188
4411
  )
4189
4412
  };
@@ -5088,6 +5311,8 @@ function storedMessageToUiMessage(message) {
5088
5311
  id: component.id,
5089
5312
  component: component.component,
5090
5313
  props: component.props,
5314
+ ...component.componentVersions !== void 0 ? { componentVersions: component.componentVersions } : {},
5315
+ ...component.fallbackText !== void 0 ? { fallbackText: component.fallbackText } : {},
5091
5316
  ...component.version !== void 0 ? { version: component.version } : {},
5092
5317
  ...component.toolCallId !== void 0 ? { toolCallId: component.toolCallId } : {}
5093
5318
  }
@@ -5107,6 +5332,8 @@ function storedMessageToUiMessage(message) {
5107
5332
  data: {
5108
5333
  id: approval.toolCallId,
5109
5334
  approver: approval.approver,
5335
+ ...approval.target !== void 0 ? { target: approval.target } : approval.proposalId !== void 0 ? { target: { kind: "proposal", proposalId: approval.proposalId } } : {},
5336
+ ...approval.confirmation !== void 0 ? { confirmation: approval.confirmation } : {},
5110
5337
  ...approval.expiresAt !== void 0 ? { expiresAt: approval.expiresAt } : {}
5111
5338
  }
5112
5339
  });
@@ -5163,6 +5390,7 @@ function storedMessageToUiMessage(message) {
5163
5390
  const metadata = {
5164
5391
  // The host's own facts first: the library's keys win on a clash.
5165
5392
  ...message.metadata,
5393
+ ...message.actionProposalOutcome === void 0 ? {} : { actionProposalOutcomeId: message.actionProposalOutcome.id },
5166
5394
  ...message.feedback !== void 0 ? { feedback: message.feedback } : {},
5167
5395
  ...message.createdAt ? { createdAt: message.createdAt } : {},
5168
5396
  ...message.agentName !== void 0 ? { agentName: message.agentName } : {},
@@ -5198,6 +5426,8 @@ function storedThreadToUiMessages(messages) {
5198
5426
  return turns.map(mergeTurn);
5199
5427
  }
5200
5428
  function sameRun(previous, next) {
5429
+ if (previous.actionProposalOutcome !== void 0 || next.actionProposalOutcome !== void 0)
5430
+ return false;
5201
5431
  return previous.runId === void 0 || next.runId === void 0 || previous.runId === next.runId;
5202
5432
  }
5203
5433
  function mergeTurn(rows) {
@@ -5244,8 +5474,163 @@ function sumUsage(rows) {
5244
5474
  }
5245
5475
 
5246
5476
  // src/use-agent-chat.ts
5247
- var import_react22 = require("@ai-sdk/react");
5248
- var import_react23 = require("react");
5477
+ var import_react23 = require("@ai-sdk/react");
5478
+ var import_react24 = require("react");
5479
+
5480
+ // src/approvals/use-action-proposals.ts
5481
+ var import_react22 = require("react");
5482
+
5483
+ // src/approvals/proposals.ts
5484
+ function proposalNeedsPolling(proposal) {
5485
+ return proposal.decision === "pending" || proposal.execution?.status === "queued" || proposal.execution?.status === "executing" || proposal.outcomeDelivery?.status === "pending";
5486
+ }
5487
+ function reconcileProposalMessages(current, proposals, facts) {
5488
+ const messages = current.map((message) => {
5489
+ const relevant = proposals.filter(
5490
+ (proposal) => message.parts.some((part) => {
5491
+ if ("data" in part && record(part.data) && record(part.data.target) && part.data.target.kind === "proposal")
5492
+ return part.data.target.proposalId === proposal.id;
5493
+ if ("toolCallId" in part && part.toolCallId === proposal.originToolCallId) {
5494
+ if ("output" in part && record(part.output) && typeof part.output.proposalId === "string")
5495
+ return part.output.proposalId === proposal.id;
5496
+ return record(message.metadata) && message.metadata.runId === proposal.originRunId;
5497
+ }
5498
+ return false;
5499
+ })
5500
+ );
5501
+ if (relevant.length === 0) return message;
5502
+ const ids = new Set(relevant.map((proposal) => proposal.originToolCallId));
5503
+ const parts = message.parts.filter(
5504
+ (part) => !(part.type === "data-action-proposal" && "id" in part && typeof part.id === "string" && ids.has(part.id))
5505
+ );
5506
+ for (const proposal of relevant)
5507
+ parts.push({
5508
+ type: "data-action-proposal",
5509
+ id: proposal.originToolCallId,
5510
+ data: {
5511
+ id: proposal.originToolCallId,
5512
+ target: { kind: "proposal", proposalId: proposal.id, threadId: proposal.threadId },
5513
+ approver: proposal.approver,
5514
+ confirmation: proposal.confirmation,
5515
+ expiresAt: proposal.expiresAt === null ? null : Math.abs(proposal.expiresAt) <= 864e13 ? new Date(proposal.expiresAt).toISOString() : null,
5516
+ status: proposal.decision,
5517
+ executionStatus: proposal.execution?.status ?? null,
5518
+ remember: proposal.decisionAudit?.remember === true,
5519
+ decidedBy: proposal.decisionAudit?.actorRef ?? null,
5520
+ decidedVia: proposal.decisionAudit?.via ?? null,
5521
+ reason: proposal.decisionAudit?.reason ?? null
5522
+ }
5523
+ });
5524
+ return { ...message, parts };
5525
+ });
5526
+ const knownMessages = new Set(messages.map((message) => message.id));
5527
+ const knownOutcomes = new Set(
5528
+ messages.map((message) => outcomeId(message.metadata)).filter(Boolean)
5529
+ );
5530
+ for (const fact of facts) {
5531
+ const id = fact.actionProposalOutcome?.id;
5532
+ if (id === void 0 || knownMessages.has(fact.id) || knownOutcomes.has(id)) continue;
5533
+ const message = storedMessageToUiMessage(fact);
5534
+ messages.push({
5535
+ ...message,
5536
+ metadata: {
5537
+ ...record(message.metadata) ? message.metadata : {},
5538
+ actionProposalOutcomeId: id
5539
+ }
5540
+ });
5541
+ knownMessages.add(fact.id);
5542
+ knownOutcomes.add(id);
5543
+ }
5544
+ return messages;
5545
+ }
5546
+ function record(value) {
5547
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5548
+ }
5549
+ function outcomeId(value) {
5550
+ return record(value) && typeof value.actionProposalOutcomeId === "string" ? value.actionProposalOutcomeId : void 0;
5551
+ }
5552
+
5553
+ // src/approvals/use-action-proposals.ts
5554
+ function useActionProposals(backend, threadId, setMessages, refreshKey, pollMs = 1e3) {
5555
+ const [items, setItems] = (0, import_react22.useState)([]);
5556
+ const [error, setError] = (0, import_react22.useState)(null);
5557
+ const latest = (0, import_react22.useRef)({ threadId, setMessages });
5558
+ latest.current = { threadId, setMessages };
5559
+ const mounted = (0, import_react22.useRef)(true);
5560
+ const requests = (0, import_react22.useRef)(/* @__PURE__ */ new Map());
5561
+ (0, import_react22.useEffect)(() => {
5562
+ mounted.current = true;
5563
+ return () => {
5564
+ mounted.current = false;
5565
+ };
5566
+ }, []);
5567
+ const refresh = (0, import_react22.useCallback)(async () => {
5568
+ if (threadId === void 0 || backend.listActionProposals === void 0) return;
5569
+ const pending = requests.current.get(threadId);
5570
+ if (pending) return pending;
5571
+ const request = (async () => {
5572
+ try {
5573
+ const proposals = await backend.listActionProposals?.({ threadId });
5574
+ if (!mounted.current || latest.current.threadId !== threadId || !Array.isArray(proposals))
5575
+ return;
5576
+ const admitted = proposals.some(
5577
+ (proposal) => proposal.outcomeDelivery?.status === "admitted"
5578
+ );
5579
+ const facts = admitted ? (await backend.getThread(threadId)).messages.filter(
5580
+ (message) => message.actionProposalOutcome !== void 0
5581
+ ) : [];
5582
+ if (!mounted.current || latest.current.threadId !== threadId) return;
5583
+ setItems(proposals);
5584
+ setError(null);
5585
+ latest.current.setMessages(
5586
+ (current) => reconcileProposalMessages(current, proposals, facts)
5587
+ );
5588
+ } catch (cause) {
5589
+ if (mounted.current && latest.current.threadId === threadId)
5590
+ setError(cause instanceof Error ? cause : new Error(String(cause)));
5591
+ }
5592
+ })();
5593
+ requests.current.set(threadId, request);
5594
+ try {
5595
+ await request;
5596
+ } finally {
5597
+ requests.current.delete(threadId);
5598
+ }
5599
+ }, [backend, threadId]);
5600
+ (0, import_react22.useEffect)(() => {
5601
+ setItems([]);
5602
+ setError(null);
5603
+ }, [threadId]);
5604
+ (0, import_react22.useEffect)(() => {
5605
+ void refresh();
5606
+ }, [refresh, refreshKey]);
5607
+ (0, import_react22.useEffect)(() => {
5608
+ if (!items.some(proposalNeedsPolling) || pollMs <= 0) return;
5609
+ const timer = setInterval(() => {
5610
+ void refresh();
5611
+ }, pollMs);
5612
+ return () => clearInterval(timer);
5613
+ }, [items, pollMs, refresh]);
5614
+ (0, import_react22.useEffect)(() => {
5615
+ const refetch = () => {
5616
+ void refresh();
5617
+ };
5618
+ const visible = () => {
5619
+ if (document.visibilityState === "visible") refetch();
5620
+ };
5621
+ window.addEventListener("focus", refetch);
5622
+ window.addEventListener("online", refetch);
5623
+ document.addEventListener("visibilitychange", visible);
5624
+ return () => {
5625
+ window.removeEventListener("focus", refetch);
5626
+ window.removeEventListener("online", refetch);
5627
+ document.removeEventListener("visibilitychange", visible);
5628
+ };
5629
+ }, [refresh]);
5630
+ return { items, error, refresh };
5631
+ }
5632
+
5633
+ // src/use-agent-chat.ts
5249
5634
  var EMPTY_QUEUE = { items: [], paused: null };
5250
5635
  var localQueueSequence = 0;
5251
5636
  function queuedUserMessage(turn) {
@@ -5300,13 +5685,16 @@ function localChatId() {
5300
5685
  return `local-chat-${localChatSequence}`;
5301
5686
  }
5302
5687
  function useAgentChat(options = {}) {
5303
- const latest = (0, import_react23.useRef)(options);
5688
+ const providerCapabilities = useAgentUiCapabilities();
5689
+ const capabilitiesRef = (0, import_react24.useRef)(providerCapabilities);
5690
+ capabilitiesRef.current = providerCapabilities;
5691
+ const latest = (0, import_react24.useRef)(options);
5304
5692
  latest.current = options;
5305
5693
  const resolved = useAgentBackend(options.backend);
5306
- const client = (0, import_react23.useRef)(resolved).current;
5307
- const createdThreadId = (0, import_react23.useRef)(void 0);
5308
- const chatIdRef = (0, import_react23.useRef)(options.threadId ?? localChatId());
5309
- const boundThreadRef = (0, import_react23.useRef)(options.threadId);
5694
+ const client = (0, import_react24.useRef)(resolved).current;
5695
+ const createdThreadId = (0, import_react24.useRef)(void 0);
5696
+ const chatIdRef = (0, import_react24.useRef)(options.threadId ?? localChatId());
5697
+ const boundThreadRef = (0, import_react24.useRef)(options.threadId);
5310
5698
  if (options.threadId !== boundThreadRef.current) {
5311
5699
  boundThreadRef.current = options.threadId;
5312
5700
  const adopting = options.threadId !== void 0 && options.threadId === createdThreadId.current;
@@ -5316,39 +5704,41 @@ function useAgentChat(options = {}) {
5316
5704
  }
5317
5705
  }
5318
5706
  const chatId = chatIdRef.current;
5319
- const [runId, setRunId] = (0, import_react23.useState)(options.resumeRunId);
5320
- const runIdRef = (0, import_react23.useRef)(runId);
5707
+ const [runId, setRunId] = (0, import_react24.useState)(options.resumeRunId);
5708
+ const runIdRef = (0, import_react24.useRef)(runId);
5321
5709
  runIdRef.current = runId;
5322
- const settlingRunIdRef = (0, import_react23.useRef)(void 0);
5323
- const autoResumeRunIdRef = (0, import_react23.useRef)(void 0);
5324
- const [activeRunId, setActiveRunId] = (0, import_react23.useState)(null);
5325
- const [historyFor, setHistoryFor] = (0, import_react23.useState)(void 0);
5326
- const [historyError, setHistoryError] = (0, import_react23.useState)(null);
5327
- const [runError, setRunError] = (0, import_react23.useState)(null);
5328
- const [threadModel, setThreadModel] = (0, import_react23.useState)(null);
5329
- const [threadPersona, setThreadPersona] = (0, import_react23.useState)(null);
5330
- const [pickedModel, setPickedModel] = (0, import_react23.useState)(void 0);
5331
- const pickedModelRef = (0, import_react23.useRef)(pickedModel);
5710
+ const settlingRunIdRef = (0, import_react24.useRef)(void 0);
5711
+ const autoResumeRunIdRef = (0, import_react24.useRef)(void 0);
5712
+ const [activeRunId, setActiveRunId] = (0, import_react24.useState)(null);
5713
+ const [historyFor, setHistoryFor] = (0, import_react24.useState)(void 0);
5714
+ const [historyError, setHistoryError] = (0, import_react24.useState)(null);
5715
+ const [runError, setRunError] = (0, import_react24.useState)(null);
5716
+ const [threadModel, setThreadModel] = (0, import_react24.useState)(null);
5717
+ const [threadPersona, setThreadPersona] = (0, import_react24.useState)(null);
5718
+ const [pickedModel, setPickedModel] = (0, import_react24.useState)(void 0);
5719
+ const pickedModelRef = (0, import_react24.useRef)(pickedModel);
5332
5720
  pickedModelRef.current = pickedModel;
5333
- const lockedRef = (0, import_react23.useRef)(false);
5334
- const pendingPin = (0, import_react23.useRef)(void 0);
5335
- const regenerateNext = (0, import_react23.useRef)(false);
5336
- const currentThreadId = (0, import_react23.useCallback)(
5721
+ const lockedRef = (0, import_react24.useRef)(false);
5722
+ const pendingPin = (0, import_react24.useRef)(void 0);
5723
+ const regenerateNext = (0, import_react24.useRef)(false);
5724
+ const currentThreadId = (0, import_react24.useCallback)(
5337
5725
  () => latest.current.threadId ?? createdThreadId.current,
5338
5726
  []
5339
5727
  );
5340
- const [queueState, setQueueState] = (0, import_react23.useState)(EMPTY_QUEUE);
5341
- const queueStateRef = (0, import_react23.useRef)(queueState);
5342
- const applyQueue = (0, import_react23.useCallback)((next) => {
5728
+ const [queueState, setQueueState] = (0, import_react24.useState)(EMPTY_QUEUE);
5729
+ const queueStateRef = (0, import_react24.useRef)(queueState);
5730
+ const applyQueue = (0, import_react24.useCallback)((next) => {
5343
5731
  queueStateRef.current = next;
5344
5732
  setQueueState(next);
5345
5733
  }, []);
5346
- const [pendingSends, setPendingSends] = (0, import_react23.useState)([]);
5347
- const [queueError, setQueueError] = (0, import_react23.useState)(null);
5348
- const handoff = (0, import_react23.useRef)(void 0);
5349
- const queuedSend = (0, import_react23.useRef)(void 0);
5350
- const threadWaiters = (0, import_react23.useRef)([]);
5351
- const turnBody = (0, import_react23.useCallback)(() => {
5734
+ const [pendingSends, setPendingSends] = (0, import_react24.useState)([]);
5735
+ const [queueError, setQueueError] = (0, import_react24.useState)(null);
5736
+ const handoff = (0, import_react24.useRef)(void 0);
5737
+ const queuedSend = (0, import_react24.useRef)(void 0);
5738
+ const proposalRefreshRef = (0, import_react24.useRef)(async () => {
5739
+ });
5740
+ const threadWaiters = (0, import_react24.useRef)([]);
5741
+ const turnBody = (0, import_react24.useCallback)(() => {
5352
5742
  const current = latest.current;
5353
5743
  if (current.persona !== void 0) setThreadPersona(current.persona);
5354
5744
  const pageContext = current.getPageContext?.() ?? null;
@@ -5361,12 +5751,13 @@ function useAgentChat(options = {}) {
5361
5751
  ...current.persona !== void 0 ? { persona: current.persona } : {},
5362
5752
  ...threadId !== void 0 ? { threadId } : {},
5363
5753
  ...model !== void 0 ? { model } : {},
5364
- ...pageContext ? { pageContext } : {}
5754
+ ...pageContext ? { pageContext } : {},
5755
+ ...(current.uiCapabilities ?? capabilitiesRef.current) === void 0 ? {} : { uiCapabilities: current.uiCapabilities ?? capabilitiesRef.current }
5365
5756
  };
5366
5757
  }, [currentThreadId]);
5367
- const [connection, setConnection] = (0, import_react23.useState)({ status: "live" });
5368
- const resyncAfterSettle = (0, import_react23.useRef)(false);
5369
- const transport = (0, import_react23.useMemo)(() => {
5758
+ const [connection, setConnection] = (0, import_react24.useState)({ status: "live" });
5759
+ const resyncAfterSettle = (0, import_react24.useRef)(false);
5760
+ const transport = (0, import_react24.useMemo)(() => {
5370
5761
  function onMeta(meta) {
5371
5762
  settlingRunIdRef.current = meta.runId;
5372
5763
  setRunId(meta.runId);
@@ -5418,14 +5809,15 @@ function useAgentChat(options = {}) {
5418
5809
  }
5419
5810
  });
5420
5811
  }, []);
5421
- const backgroundRef = (0, import_react23.useRef)(() => Promise.resolve());
5422
- const chat = (0, import_react22.useChat)({
5812
+ const backgroundRef = (0, import_react24.useRef)(() => Promise.resolve());
5813
+ const chat = (0, import_react23.useChat)({
5423
5814
  transport,
5424
5815
  id: chatId,
5425
5816
  resume: options.resumeRunId !== void 0,
5426
5817
  ...options.initialMessages !== void 0 ? { messages: options.initialMessages } : {},
5427
5818
  onData: (part) => {
5428
5819
  latest.current.onData?.(part);
5820
+ if (part.type === "data-proposal-decision") void proposalRefreshRef.current();
5429
5821
  if (part.type === "data-queue") {
5430
5822
  onQueueData(part.data);
5431
5823
  }
@@ -5472,7 +5864,7 @@ function useAgentChat(options = {}) {
5472
5864
  }
5473
5865
  }
5474
5866
  });
5475
- const chatRef = (0, import_react23.useRef)(chat);
5867
+ const chatRef = (0, import_react24.useRef)(chat);
5476
5868
  chatRef.current = chat;
5477
5869
  function onQueueData(data) {
5478
5870
  const frame = data ?? {};
@@ -5520,8 +5912,8 @@ function useAgentChat(options = {}) {
5520
5912
  } catch {
5521
5913
  }
5522
5914
  }
5523
- const firstChatId = (0, import_react23.useRef)(chatId);
5524
- (0, import_react23.useEffect)(() => {
5915
+ const firstChatId = (0, import_react24.useRef)(chatId);
5916
+ (0, import_react24.useEffect)(() => {
5525
5917
  if (chatId === firstChatId.current) return;
5526
5918
  firstChatId.current = chatId;
5527
5919
  setRunId(void 0);
@@ -5540,7 +5932,7 @@ function useAgentChat(options = {}) {
5540
5932
  handoff.current = void 0;
5541
5933
  queuedSend.current = void 0;
5542
5934
  }, [chatId]);
5543
- (0, import_react23.useEffect)(() => {
5935
+ (0, import_react24.useEffect)(() => {
5544
5936
  const threadId = options.threadId;
5545
5937
  if (threadId === void 0) return;
5546
5938
  if (threadId === createdThreadId.current) return;
@@ -5587,8 +5979,8 @@ function useAgentChat(options = {}) {
5587
5979
  };
5588
5980
  }, [client, chatId, options.threadId]);
5589
5981
  const isLoadingHistory = options.threadId !== void 0 && options.threadId !== createdThreadId.current && options.history !== false && options.initialMessages === void 0 && historyFor !== options.threadId;
5590
- const turnInFlight = (0, import_react23.useRef)(false);
5591
- const isTurnInFlight = (0, import_react23.useCallback)(
5982
+ const turnInFlight = (0, import_react24.useRef)(false);
5983
+ const isTurnInFlight = (0, import_react24.useCallback)(
5592
5984
  () => turnInFlight.current || transport.isAttemptLive,
5593
5985
  [transport]
5594
5986
  );
@@ -5597,20 +5989,20 @@ function useAgentChat(options = {}) {
5597
5989
  enabled: options.quota !== false && typeof client.getQuota === "function"
5598
5990
  });
5599
5991
  const blocked = options.blocked !== void 0 ? options.blocked : quota.blocked;
5600
- const blockedRef = (0, import_react23.useRef)(blocked);
5992
+ const blockedRef = (0, import_react24.useRef)(blocked);
5601
5993
  blockedRef.current = blocked;
5602
5994
  const whileRunning = typeof client.enqueueMessage === "function" ? options.whileRunning ?? "queue" : "block";
5603
- const whileRunningRef = (0, import_react23.useRef)(whileRunning);
5995
+ const whileRunningRef = (0, import_react24.useRef)(whileRunning);
5604
5996
  whileRunningRef.current = whileRunning;
5605
- const canQueueRef = (0, import_react23.useRef)(false);
5997
+ const canQueueRef = (0, import_react24.useRef)(false);
5606
5998
  canQueueRef.current = typeof client.enqueueMessage === "function";
5607
- const threadForQueue = (0, import_react23.useCallback)(async () => {
5999
+ const threadForQueue = (0, import_react24.useCallback)(async () => {
5608
6000
  const known = currentThreadId();
5609
6001
  if (known !== void 0) return known;
5610
6002
  if (!isTurnInFlight()) throw new Error("queue: this chat has no thread yet");
5611
6003
  return new Promise((resolve) => threadWaiters.current.push(resolve));
5612
6004
  }, [currentThreadId, isTurnInFlight]);
5613
- const queueCall = (0, import_react23.useCallback)(async (run) => {
6005
+ const queueCall = (0, import_react24.useCallback)(async (run) => {
5614
6006
  try {
5615
6007
  const result = await run();
5616
6008
  setQueueError(null);
@@ -5620,7 +6012,7 @@ function useAgentChat(options = {}) {
5620
6012
  throw error;
5621
6013
  }
5622
6014
  }, []);
5623
- const enqueue = (0, import_react23.useCallback)(
6015
+ const enqueue = (0, import_react24.useCallback)(
5624
6016
  async (text2, {
5625
6017
  attachments = [],
5626
6018
  mode = "queue"
@@ -5668,7 +6060,7 @@ function useAgentChat(options = {}) {
5668
6060
  },
5669
6061
  [client, queueCall, threadForQueue, turnBody, applyQueue, isTurnInFlight]
5670
6062
  );
5671
- const sendMessage = (0, import_react23.useCallback)(
6063
+ const sendMessage = (0, import_react24.useCallback)(
5672
6064
  async (message, options2) => {
5673
6065
  const block = blockedRef.current;
5674
6066
  if (block != null) throw new QuotaBlockedError(block);
@@ -5692,7 +6084,7 @@ function useAgentChat(options = {}) {
5692
6084
  },
5693
6085
  [isTurnInFlight, enqueue]
5694
6086
  );
5695
- const removeQueued = (0, import_react23.useCallback)(
6087
+ const removeQueued = (0, import_react24.useCallback)(
5696
6088
  async (id) => {
5697
6089
  const previous = queueStateRef.current;
5698
6090
  applyQueue({ ...previous, items: previous.items.filter((item) => item.id !== id) });
@@ -5705,7 +6097,7 @@ function useAgentChat(options = {}) {
5705
6097
  },
5706
6098
  [client, queueCall, applyQueue]
5707
6099
  );
5708
- const interruptQueued = (0, import_react23.useCallback)(
6100
+ const interruptQueued = (0, import_react24.useCallback)(
5709
6101
  async (id) => {
5710
6102
  const previous = queueStateRef.current;
5711
6103
  const moving = previous.items.find((item) => item.id === id);
@@ -5740,7 +6132,7 @@ function useAgentChat(options = {}) {
5740
6132
  },
5741
6133
  [client, queueCall, applyQueue, isTurnInFlight]
5742
6134
  );
5743
- const editQueued = (0, import_react23.useCallback)(
6135
+ const editQueued = (0, import_react24.useCallback)(
5744
6136
  async (id, text2) => {
5745
6137
  applyQueue(
5746
6138
  await queueCall(
@@ -5750,7 +6142,7 @@ function useAgentChat(options = {}) {
5750
6142
  },
5751
6143
  [client, queueCall, applyQueue]
5752
6144
  );
5753
- const moveQueued = (0, import_react23.useCallback)(
6145
+ const moveQueued = (0, import_react24.useCallback)(
5754
6146
  async (id, index) => {
5755
6147
  const previous = queueStateRef.current;
5756
6148
  const moving = previous.items.find((item) => item.id === id);
@@ -5772,12 +6164,12 @@ function useAgentChat(options = {}) {
5772
6164
  },
5773
6165
  [client, queueCall, applyQueue]
5774
6166
  );
5775
- const clearQueue = (0, import_react23.useCallback)(async () => {
6167
+ const clearQueue = (0, import_react24.useCallback)(async () => {
5776
6168
  const threadId = currentThreadId();
5777
6169
  if (threadId === void 0) return;
5778
6170
  applyQueue(await queueCall(() => requireBackendMethod(client, "clearQueue")(threadId)));
5779
6171
  }, [client, queueCall, currentThreadId, applyQueue]);
5780
- const resumeQueue = (0, import_react23.useCallback)(async () => {
6172
+ const resumeQueue = (0, import_react24.useCallback)(async () => {
5781
6173
  const threadId = currentThreadId();
5782
6174
  if (threadId === void 0) return;
5783
6175
  const before = queueStateRef.current;
@@ -5795,7 +6187,7 @@ function useAgentChat(options = {}) {
5795
6187
  else attachTo(turn);
5796
6188
  }
5797
6189
  }, [client, queueCall, currentThreadId, applyQueue, isTurnInFlight]);
5798
- const resumeQueueRef = (0, import_react23.useRef)(resumeQueue);
6190
+ const resumeQueueRef = (0, import_react24.useRef)(resumeQueue);
5799
6191
  resumeQueueRef.current = resumeQueue;
5800
6192
  const queuedItems = [
5801
6193
  ...pendingSends.filter((item) => item.interrupt),
@@ -5825,7 +6217,7 @@ function useAgentChat(options = {}) {
5825
6217
  resume: resumeQueue,
5826
6218
  error: queueError
5827
6219
  };
5828
- const addToolResult = (0, import_react23.useCallback)(
6220
+ const addToolResult = (0, import_react24.useCallback)(
5829
6221
  ({ tool, toolCallId, output }) => chatRef.current.addToolResult({
5830
6222
  tool,
5831
6223
  toolCallId,
@@ -5833,8 +6225,8 @@ function useAgentChat(options = {}) {
5833
6225
  }),
5834
6226
  []
5835
6227
  );
5836
- const [backgroundRuns, setBackgroundRuns] = (0, import_react23.useState)([]);
5837
- const refreshBackground = (0, import_react23.useCallback)(async () => {
6228
+ const [backgroundRuns, setBackgroundRuns] = (0, import_react24.useState)([]);
6229
+ const refreshBackground = (0, import_react24.useCallback)(async () => {
5838
6230
  const threadId = currentThreadId();
5839
6231
  if (threadId === void 0) {
5840
6232
  return;
@@ -5854,13 +6246,13 @@ function useAgentChat(options = {}) {
5854
6246
  }, [client, currentThreadId]);
5855
6247
  backgroundRef.current = refreshBackground;
5856
6248
  const isWorking = backgroundRuns.some((run) => run.status === "running");
5857
- (0, import_react23.useEffect)(() => {
6249
+ (0, import_react24.useEffect)(() => {
5858
6250
  if (options.background !== true || options.threadId === void 0) {
5859
6251
  return;
5860
6252
  }
5861
6253
  void refreshBackground();
5862
6254
  }, [options.background, options.threadId, chatId, refreshBackground]);
5863
- (0, import_react23.useEffect)(() => {
6255
+ (0, import_react24.useEffect)(() => {
5864
6256
  const every = latest.current.backgroundPollMs ?? 5e3;
5865
6257
  if (latest.current.background !== true || !isWorking || every <= 0) {
5866
6258
  return;
@@ -5868,7 +6260,15 @@ function useAgentChat(options = {}) {
5868
6260
  const timer = setInterval(() => void refreshBackground(), every);
5869
6261
  return () => clearInterval(timer);
5870
6262
  }, [isWorking, refreshBackground]);
5871
- const cancel = (0, import_react23.useCallback)(async () => {
6263
+ const proposals = useActionProposals(
6264
+ client,
6265
+ options.proposals === false || options.proposals !== true && options.history === false && options.resume === false ? void 0 : currentThreadId(),
6266
+ (update) => chatRef.current.setMessages(update),
6267
+ `${chat.status}:${connection.status}`,
6268
+ options.proposalPollMs
6269
+ );
6270
+ proposalRefreshRef.current = proposals.refresh;
6271
+ const cancel = (0, import_react24.useCallback)(async () => {
5872
6272
  chatRef.current.stop();
5873
6273
  const active = runIdRef.current;
5874
6274
  if (active) {
@@ -5885,8 +6285,23 @@ function useAgentChat(options = {}) {
5885
6285
  }
5886
6286
  }
5887
6287
  }, [client, currentThreadId, applyQueue]);
5888
- const approve = (0, import_react23.useCallback)(
5889
- async ({ toolCallId, remember, via }) => {
6288
+ const approve = (0, import_react24.useCallback)(
6289
+ async ({ toolCallId, target, remember, via }) => {
6290
+ if (target?.kind === "proposal") {
6291
+ const threadId = target.threadId ?? currentThreadId();
6292
+ if (threadId === void 0)
6293
+ throw new Error("A proposal decision requires its thread scope");
6294
+ await requireBackendMethod(
6295
+ client,
6296
+ "approveActionProposal"
6297
+ )({
6298
+ threadId,
6299
+ proposalId: target.proposalId,
6300
+ ...remember === true ? { remember: true } : {}
6301
+ });
6302
+ await proposals.refresh();
6303
+ return;
6304
+ }
5890
6305
  await requireBackendMethod(
5891
6306
  client,
5892
6307
  "approveToolCall"
@@ -5896,10 +6311,25 @@ function useAgentChat(options = {}) {
5896
6311
  ...via !== void 0 ? { via } : {}
5897
6312
  });
5898
6313
  },
5899
- [client]
6314
+ [client, currentThreadId, proposals.refresh]
5900
6315
  );
5901
- const reject = (0, import_react23.useCallback)(
5902
- async ({ toolCallId, reason, via }) => {
6316
+ const reject = (0, import_react24.useCallback)(
6317
+ async ({ toolCallId, target, reason, via }) => {
6318
+ if (target?.kind === "proposal") {
6319
+ const threadId = target.threadId ?? currentThreadId();
6320
+ if (threadId === void 0)
6321
+ throw new Error("A proposal decision requires its thread scope");
6322
+ await requireBackendMethod(
6323
+ client,
6324
+ "rejectActionProposal"
6325
+ )({
6326
+ threadId,
6327
+ proposalId: target.proposalId,
6328
+ ...reason === void 0 ? {} : { reason }
6329
+ });
6330
+ await proposals.refresh();
6331
+ return;
6332
+ }
5903
6333
  await requireBackendMethod(
5904
6334
  client,
5905
6335
  "rejectToolCall"
@@ -5909,9 +6339,9 @@ function useAgentChat(options = {}) {
5909
6339
  ...via !== void 0 ? { via } : {}
5910
6340
  });
5911
6341
  },
5912
- [client]
6342
+ [client, currentThreadId, proposals.refresh]
5913
6343
  );
5914
- const answer = (0, import_react23.useCallback)(
6344
+ const answer = (0, import_react24.useCallback)(
5915
6345
  async ({ toolCallId, answers, via }) => {
5916
6346
  await requireBackendMethod(
5917
6347
  client,
@@ -5924,7 +6354,7 @@ function useAgentChat(options = {}) {
5924
6354
  },
5925
6355
  [client]
5926
6356
  );
5927
- const skip = (0, import_react23.useCallback)(
6357
+ const skip = (0, import_react24.useCallback)(
5928
6358
  async ({ toolCallId, via }) => {
5929
6359
  await requireBackendMethod(
5930
6360
  client,
@@ -5933,7 +6363,7 @@ function useAgentChat(options = {}) {
5933
6363
  },
5934
6364
  [client]
5935
6365
  );
5936
- const regenerate = (0, import_react23.useCallback)(
6366
+ const regenerate = (0, import_react24.useCallback)(
5937
6367
  (_input) => {
5938
6368
  const block = blockedRef.current;
5939
6369
  if (block != null) throw new QuotaBlockedError(block);
@@ -5946,7 +6376,7 @@ function useAgentChat(options = {}) {
5946
6376
  },
5947
6377
  [isTurnInFlight]
5948
6378
  );
5949
- const storedIdOf = (0, import_react23.useCallback)(
6379
+ const storedIdOf = (0, import_react24.useCallback)(
5950
6380
  async (messageId, threadId) => {
5951
6381
  const message = chatRef.current.messages.find((candidate) => candidate.id === messageId);
5952
6382
  const runOf = message?.metadata?.runId;
@@ -5957,7 +6387,7 @@ function useAgentChat(options = {}) {
5957
6387
  },
5958
6388
  [client]
5959
6389
  );
5960
- const fork = (0, import_react23.useCallback)(
6390
+ const fork = (0, import_react24.useCallback)(
5961
6391
  async ({
5962
6392
  messageId,
5963
6393
  threadId
@@ -5972,7 +6402,7 @@ function useAgentChat(options = {}) {
5972
6402
  },
5973
6403
  [client, currentThreadId, storedIdOf]
5974
6404
  );
5975
- const truncateFrom = (0, import_react23.useCallback)(
6405
+ const truncateFrom = (0, import_react24.useCallback)(
5976
6406
  async ({ messageId, threadId }) => {
5977
6407
  const target = threadId ?? currentThreadId();
5978
6408
  if (target === void 0) throw new Error("truncateFrom: this chat has no thread yet");
@@ -5981,7 +6411,7 @@ function useAgentChat(options = {}) {
5981
6411
  },
5982
6412
  [client, currentThreadId, storedIdOf]
5983
6413
  );
5984
- const promote = (0, import_react23.useCallback)(
6414
+ const promote = (0, import_react24.useCallback)(
5985
6415
  async ({ threadId } = {}) => {
5986
6416
  const target = threadId ?? currentThreadId();
5987
6417
  if (target === void 0) throw new Error("promote: this chat has no thread yet");
@@ -5990,24 +6420,24 @@ function useAgentChat(options = {}) {
5990
6420
  },
5991
6421
  [client, currentThreadId]
5992
6422
  );
5993
- const [modelsWanted, setModelsWanted] = (0, import_react23.useState)(false);
5994
- const modelsWantedRef = (0, import_react23.useRef)(modelsWanted);
6423
+ const [modelsWanted, setModelsWanted] = (0, import_react24.useState)(false);
6424
+ const modelsWantedRef = (0, import_react24.useRef)(modelsWanted);
5995
6425
  modelsWantedRef.current = modelsWanted;
5996
6426
  const catalog = useModels({
5997
6427
  backend: client,
5998
6428
  ...options.agent !== void 0 ? { agent: options.agent } : {},
5999
6429
  enabled: modelsWanted && typeof client.listModels === "function"
6000
6430
  });
6001
- const wantModels = (0, import_react23.useCallback)(() => {
6431
+ const wantModels = (0, import_react24.useCallback)(() => {
6002
6432
  if (modelsWantedRef.current) return;
6003
6433
  modelsWantedRef.current = true;
6004
6434
  queueMicrotask(() => setModelsWanted(true));
6005
6435
  }, []);
6006
6436
  lockedRef.current = catalog.locked !== null;
6007
- const selectModel = (0, import_react23.useCallback)((id) => {
6437
+ const selectModel = (0, import_react24.useCallback)((id) => {
6008
6438
  if (!lockedRef.current) setPickedModel(id);
6009
6439
  }, []);
6010
- const pinToThread = (0, import_react23.useCallback)(
6440
+ const pinToThread = (0, import_react24.useCallback)(
6011
6441
  async (model) => {
6012
6442
  const threadId = currentThreadId();
6013
6443
  setThreadModel(model);
@@ -6047,14 +6477,14 @@ function useAgentChat(options = {}) {
6047
6477
  };
6048
6478
  const status = connection.status === "reconnecting" ? "reconnecting" : chat.status;
6049
6479
  const isBusy = status === "submitted" || status === "streaming" || status === "reconnecting";
6050
- const isBusyRef = (0, import_react23.useRef)(isBusy);
6480
+ const isBusyRef = (0, import_react24.useRef)(isBusy);
6051
6481
  isBusyRef.current = isBusy;
6052
6482
  const files = useAttachments({ ...options.composer, backend: client });
6053
- const [text, setText] = (0, import_react23.useState)("");
6483
+ const [text, setText] = (0, import_react24.useState)("");
6054
6484
  const blockedBy = blocked != null ? "quota" : files.isUploading ? "uploading" : isBusy && whileRunning === "block" ? "busy" : text.trim().length === 0 && files.refs.length === 0 ? "empty" : null;
6055
- const composerRef = (0, import_react23.useRef)({ text, files, blockedBy });
6485
+ const composerRef = (0, import_react24.useRef)({ text, files, blockedBy });
6056
6486
  composerRef.current = { text, files, blockedBy };
6057
- const submit = (0, import_react23.useCallback)(
6487
+ const submit = (0, import_react24.useCallback)(
6058
6488
  async (options2) => {
6059
6489
  const current = composerRef.current;
6060
6490
  const asked = canQueueRef.current ? options2?.mode : void 0;
@@ -6136,6 +6566,7 @@ function useAgentChat(options = {}) {
6136
6566
  addToolResult,
6137
6567
  /** Sub-agents this conversation started and did not wait for. */
6138
6568
  background,
6569
+ proposals,
6139
6570
  runId,
6140
6571
  /** The loaded thread's running turn, or `null` once read with none. */
6141
6572
  activeRunId,
@@ -6260,8 +6691,10 @@ function isRunNotActiveError(error) {
6260
6691
  onThreadsEvent,
6261
6692
  persistSpeechLang,
6262
6693
  phraseFor,
6694
+ proposalNeedsPolling,
6263
6695
  readPath,
6264
6696
  readReasoningMs,
6697
+ reconcileProposalMessages,
6265
6698
  reframeAgUiStream,
6266
6699
  requireBackendMethod,
6267
6700
  resolveResultView,
@@ -6269,6 +6702,7 @@ function isRunNotActiveError(error) {
6269
6702
  storedThreadToUiMessages,
6270
6703
  toolCallState,
6271
6704
  toolCatalogFrom,
6705
+ useActionProposals,
6272
6706
  useAgentBackend,
6273
6707
  useAgentChat,
6274
6708
  useAgentConfig,