@dudousxd/nestjs-agent-react 0.31.5 → 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) {
@@ -639,6 +700,7 @@ var AgentChatTransport = class {
639
700
  const message = lastMessage ? extractText(lastMessage) : "";
640
701
  const body = {
641
702
  ...this.options.agent !== void 0 ? { agent: this.options.agent } : {},
703
+ ...this.options.persona !== void 0 ? { persona: this.options.persona } : {},
642
704
  ...this.options.getBody?.() ?? {},
643
705
  ...options.body ?? {},
644
706
  message
@@ -654,6 +716,10 @@ var AgentChatTransport = class {
654
716
  return queuedChunkStream(response.queued);
655
717
  }
656
718
  this.captureHeaderMeta(response);
719
+ if (response.proposalDecision !== void 0) {
720
+ this.attemptLive = false;
721
+ return proposalDecisionChunkStream(response.proposalDecision);
722
+ }
657
723
  return this.toChunkStream(response, {
658
724
  ...headers !== void 0 ? { headers } : {},
659
725
  ...options.abortSignal ? { signal: options.abortSignal } : {}
@@ -780,7 +846,7 @@ var AgentChatTransport = class {
780
846
  let reasoningSeq = 0;
781
847
  const announced = /* @__PURE__ */ new Set();
782
848
  const toolMetadata = /* @__PURE__ */ new Map();
783
- const record = (meta) => {
849
+ const record2 = (meta) => {
784
850
  streamRunId = meta.runId;
785
851
  this.recordMeta(meta);
786
852
  };
@@ -1068,7 +1134,7 @@ var AgentChatTransport = class {
1068
1134
  }
1069
1135
  if (frame.event === "meta") {
1070
1136
  const meta = parseMeta(frame.data);
1071
- if (meta) record(meta);
1137
+ if (meta) record2(meta);
1072
1138
  } else if (frame.data) {
1073
1139
  const event = parseEvent(frame.data);
1074
1140
  if (event) emit(event);
@@ -1136,10 +1202,10 @@ function parseErrorFrame(data) {
1136
1202
  try {
1137
1203
  const parsed = JSON.parse(data);
1138
1204
  if (parsed !== null && typeof parsed === "object") {
1139
- const record = parsed;
1205
+ const record2 = parsed;
1140
1206
  return {
1141
- code: typeof record.code === "string" && record.code !== "" ? record.code : void 0,
1142
- 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
1143
1209
  };
1144
1210
  }
1145
1211
  } catch {
@@ -1179,6 +1245,21 @@ function delay(ms, signal) {
1179
1245
  signal?.addEventListener("abort", done, { once: true });
1180
1246
  });
1181
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
+ }
1182
1263
 
1183
1264
  // src/attachments/files.ts
1184
1265
  function fileKind(mediaType) {
@@ -1311,6 +1392,10 @@ function toGenerativeUIItem(part) {
1311
1392
  return {
1312
1393
  id,
1313
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 } : {},
1314
1399
  props: isRecord(source.props) ? source.props : {},
1315
1400
  version: typeof source.version === "number" ? source.version : null,
1316
1401
  toolCallId: typeof source.toolCallId === "string" ? source.toolCallId : null
@@ -1416,20 +1501,29 @@ function useValidatedProps(catalog, name, props) {
1416
1501
  function useGenerativeUIState(part, options, fallbackTree) {
1417
1502
  const item = (0, import_react3.useMemo)(() => toGenerativeUIItem(part), [part]);
1418
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);
1419
1506
  const resolution = useResolvedComponent(
1420
1507
  name,
1421
1508
  item?.version ?? null,
1422
- options.registry,
1509
+ incompatible ? {} : options.registry,
1423
1510
  item === null ? void 0 : options.resolveComponent,
1424
1511
  fallbackTree
1425
1512
  );
1426
1513
  const empty = (0, import_react3.useMemo)(() => ({}), []);
1427
1514
  const validation = useValidatedProps(
1428
- item === null || name === GENUI_TREE_COMPONENT ? void 0 : options.catalog,
1515
+ item === null || name === GENUI_TREE_COMPONENT || incompatible ? void 0 : options.catalog,
1429
1516
  name,
1430
1517
  item?.props ?? empty
1431
1518
  );
1432
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
+ };
1433
1527
  if (resolution.status === "loading") return { status: "loading", item };
1434
1528
  if (resolution.status === "unknown") return { status: "problem", reason: "unknown", item };
1435
1529
  if (resolution.status === "error") {
@@ -1447,7 +1541,8 @@ var import_jsx_runtime = require("react/jsx-runtime");
1447
1541
  var TreeScopeContext = (0, import_react4.createContext)(null);
1448
1542
  function renderFallback(fallback2, problem) {
1449
1543
  if (typeof fallback2 === "function") return fallback2(problem);
1450
- 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;
1451
1546
  }
1452
1547
  var ItemBoundary = class extends import_react4.Component {
1453
1548
  state = { error: void 0, failed: false };
@@ -1479,28 +1574,42 @@ function nodeItem(node, id) {
1479
1574
  function TreeNode({ node, id }) {
1480
1575
  const scope = (0, import_react4.useContext)(TreeScopeContext);
1481
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);
1482
1580
  const resolution = useResolvedComponent(
1483
1581
  node.type,
1484
- null,
1485
- scope?.options.registry ?? {},
1582
+ storedVersion ?? null,
1583
+ incompatible ? {} : scope?.options.registry ?? {},
1486
1584
  scope?.options.resolveComponent
1487
1585
  );
1488
- 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
+ );
1489
1591
  if (scope === null) return null;
1490
1592
  if (resolution.status === "loading" || validation.status === "pending") return scope.loading;
1491
1593
  if (resolution.status === "unknown") {
1594
+ if (scope.fallback === void 0 && scope.fallbackText !== void 0)
1595
+ throw new Error("Unknown tree component");
1492
1596
  return renderFallback(scope.fallback, { reason: "unknown", item });
1493
1597
  }
1494
1598
  if (resolution.status === "error") {
1599
+ if (scope.fallback === void 0 && scope.fallbackText !== void 0) throw resolution.error;
1495
1600
  return renderFallback(scope.fallback, { reason: "error", item, error: resolution.error });
1496
1601
  }
1497
1602
  if (validation.status === "invalid") {
1603
+ if (scope.fallback === void 0 && scope.fallbackText !== void 0)
1604
+ throw new Error("Invalid tree component");
1498
1605
  return renderFallback(scope.fallback, { reason: "invalid", item, issues: validation.issues });
1499
1606
  }
1500
1607
  const Renderer = resolution.Component;
1501
1608
  const children = Array.isArray(node.children) ? node.children : [];
1502
1609
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Renderer, { ...validation.props, children: children.length > 0 ? children.map((child, index) => {
1503
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);
1504
1613
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1505
1614
  ItemBoundary,
1506
1615
  {
@@ -1522,6 +1631,8 @@ function GenerativeUIScope({
1522
1631
  catalog,
1523
1632
  resolveComponent,
1524
1633
  fallback: fallback2,
1634
+ fallbackText,
1635
+ componentVersions,
1525
1636
  loading = null,
1526
1637
  onError,
1527
1638
  children
@@ -1534,10 +1645,21 @@ function GenerativeUIScope({
1534
1645
  ...resolveComponent !== void 0 ? { resolveComponent } : {}
1535
1646
  },
1536
1647
  fallback: fallback2,
1648
+ ...componentVersions !== void 0 ? { componentVersions } : {},
1649
+ ...fallbackText !== void 0 ? { fallbackText } : {},
1537
1650
  loading,
1538
1651
  onError
1539
1652
  }),
1540
- [registry, catalog, resolveComponent, fallback2, loading, onError]
1653
+ [
1654
+ registry,
1655
+ catalog,
1656
+ resolveComponent,
1657
+ fallback2,
1658
+ fallbackText,
1659
+ componentVersions,
1660
+ loading,
1661
+ onError
1662
+ ]
1541
1663
  );
1542
1664
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TreeScopeContext.Provider, { value: scope, children });
1543
1665
  }
@@ -1597,7 +1719,7 @@ function GenerativeUI({
1597
1719
  onError: ownOnError
1598
1720
  }) {
1599
1721
  const provided = (0, import_react4.useContext)(GenuiContext);
1600
- const fallback2 = ownFallback ?? provided?.fallback;
1722
+ const fallback2 = ownFallback !== void 0 ? ownFallback : provided?.fallback;
1601
1723
  const loading = ownLoading ?? provided?.loading ?? null;
1602
1724
  const onError = ownOnError ?? provided?.onError;
1603
1725
  const options = useMergedOptions({
@@ -1622,6 +1744,8 @@ function GenerativeUI({
1622
1744
  GenerativeUIScope,
1623
1745
  {
1624
1746
  ...options,
1747
+ ...state.item.componentVersions !== void 0 ? { componentVersions: state.item.componentVersions } : {},
1748
+ ...state.item.fallbackText !== void 0 ? { fallbackText: state.item.fallbackText } : {},
1625
1749
  fallback: fallback2,
1626
1750
  loading,
1627
1751
  ...onError !== void 0 ? { onError } : {},
@@ -1637,6 +1761,7 @@ var AgentBackendContext = sharedContext(
1637
1761
  );
1638
1762
  function AgentProvider({
1639
1763
  backend,
1764
+ uiCapabilities,
1640
1765
  baseUrl,
1641
1766
  path,
1642
1767
  headers,
@@ -1668,7 +1793,7 @@ function AgentProvider({
1668
1793
  [backend, baseUrl, path, credentials, fetch, upload]
1669
1794
  );
1670
1795
  const content = genui !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(GenuiProvider, { ...genui, children }) : children;
1671
- 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 }) });
1672
1797
  }
1673
1798
  var fallback;
1674
1799
  function defaultBackend() {
@@ -1679,6 +1804,12 @@ function useAgentBackend(own) {
1679
1804
  const provided = (0, import_react5.useContext)(AgentBackendContext);
1680
1805
  return own ?? provided ?? defaultBackend();
1681
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
+ }
1682
1813
 
1683
1814
  // src/config/use-agent-config.ts
1684
1815
  var shared = /* @__PURE__ */ new WeakMap();
@@ -1944,7 +2075,7 @@ async function defaultRefusal(response) {
1944
2075
  }
1945
2076
  function defaultForwardedProps(body) {
1946
2077
  const out = {};
1947
- for (const key of ["pageContext", "agent", "model"]) {
2078
+ for (const key of ["pageContext", "agent", "model", "persona", "uiCapabilities"]) {
1948
2079
  if (body[key] !== void 0 && body[key] !== null) out[key] = body[key];
1949
2080
  }
1950
2081
  return Object.keys(out).length > 0 ? out : void 0;
@@ -2303,16 +2434,24 @@ function useAgents(options = {}) {
2303
2434
  "",
2304
2435
  enabled
2305
2436
  );
2437
+ const find = (0, import_react9.useCallback)(
2438
+ (name) => (data ?? []).find(
2439
+ (agent) => name === void 0 ? agent.isDefault === true : agent.name === name
2440
+ ),
2441
+ [data]
2442
+ );
2306
2443
  return (0, import_react9.useMemo)(() => {
2307
2444
  const agents = data ?? [];
2308
2445
  return {
2309
2446
  agents,
2310
2447
  defaultAgent: agents.find((agent) => agent.isDefault === true)?.name ?? null,
2448
+ personasOf: (agent) => find(agent)?.personas ?? [],
2449
+ defaultPersonaOf: (agent) => find(agent)?.defaultPersona ?? null,
2311
2450
  isLoading,
2312
2451
  error,
2313
2452
  refresh
2314
2453
  };
2315
- }, [data, isLoading, error, refresh]);
2454
+ }, [data, find, isLoading, error, refresh]);
2316
2455
  }
2317
2456
 
2318
2457
  // src/catalog/use-models.ts
@@ -2970,6 +3109,15 @@ function isActionCall(part) {
2970
3109
  }
2971
3110
  function toolCallState(part) {
2972
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
+ };
2973
3121
  const errorText = "errorText" in part ? part.errorText : void 0;
2974
3122
  const outputError = output !== null && typeof output === "object" && "error" in output ? String(output.error) : void 0;
2975
3123
  const isDenied = part.state === "output-denied";
@@ -3014,7 +3162,11 @@ function describeToolCall(part, catalog, options = {}) {
3014
3162
  icon: presentation?.icon ?? null,
3015
3163
  tone: presentation?.tone ?? "neutral",
3016
3164
  detail: presentation?.detail ?? null,
3017
- 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 : {
3018
3170
  title: fillTemplate(confirm.title, input),
3019
3171
  verb: confirm.verb,
3020
3172
  detail: confirm.detail !== void 0 ? fillTemplate(confirm.detail, input) : null
@@ -3028,6 +3180,9 @@ function describeToolCall(part, catalog, options = {}) {
3028
3180
  }
3029
3181
  var STATUS_WEIGHT = {
3030
3182
  running: 4,
3183
+ queued: 4,
3184
+ executing: 4,
3185
+ succeeded: 0,
3031
3186
  "awaiting-approval": 3,
3032
3187
  failed: 2,
3033
3188
  denied: 1,
@@ -3047,7 +3202,7 @@ function groupToolActivity(calls, options = {}) {
3047
3202
  if (corrected.has(call.toolCallId)) continue;
3048
3203
  const presentation = options.catalog?.[call.name];
3049
3204
  const key = options.keyOf?.(call, presentation) ?? presentation?.label ?? call.name;
3050
- const status = toolCallState(call.part).status;
3205
+ const status = call.description.status;
3051
3206
  const phrase = phraseFor(
3052
3207
  presentation,
3053
3208
  call.part.input,
@@ -3289,7 +3444,7 @@ function isAncestor(call, candidate, byId) {
3289
3444
  }
3290
3445
  function buildToolCall(part, options, approval, catalog) {
3291
3446
  const toolCallId = part.toolCallId;
3292
- const awaiting = isAwaitingApproval(part);
3447
+ const awaiting = approval?.target?.kind === "proposal" ? approval.status === "pending" : isAwaitingApproval(part);
3293
3448
  const sending = awaiting ? options?.submitting(toolCallId) ?? null : null;
3294
3449
  return {
3295
3450
  part,
@@ -3299,19 +3454,30 @@ function buildToolCall(part, options, approval, catalog) {
3299
3454
  parentId: toolParentId(part),
3300
3455
  children: [],
3301
3456
  approval,
3302
- 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
+ ),
3303
3465
  isAwaitingApproval: awaiting,
3304
3466
  approve: {
3305
3467
  available: awaiting && options?.canApprove === true,
3306
3468
  isSubmitting: sending === "approve",
3307
3469
  // Read by field, not passed through: `onClick={call.approve.run}` hands this a click event,
3308
3470
  // and a host's handler must not receive that as its options.
3309
- 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
+ )
3310
3476
  },
3311
3477
  reject: {
3312
3478
  available: awaiting && options?.canReject === true,
3313
3479
  isSubmitting: sending === "reject",
3314
- run: () => options?.reject(toolCallId)
3480
+ run: () => approval?.target === void 0 ? options?.reject(toolCallId) : options?.reject(toolCallId, approval.target)
3315
3481
  },
3316
3482
  error: options?.errorOf(toolCallId) ?? null,
3317
3483
  errorCode: options?.errorCodeOf?.(toolCallId) ?? null
@@ -3461,7 +3627,7 @@ function buildElicitationBlock(key, part, request, options) {
3461
3627
  var UI_PART = "data-ui";
3462
3628
  var APPROVAL_PART = "data-approval-requested";
3463
3629
  var SETTLED_PART = "data-approval-settled";
3464
- var SETTLED_STATUSES = ["approved", "rejected", "expired"];
3630
+ var SETTLED_STATUSES = ["approved", "rejected", "expired", "superseded"];
3465
3631
  function readApprovals(parts) {
3466
3632
  const out = /* @__PURE__ */ new Map();
3467
3633
  const entry = (id) => {
@@ -3482,7 +3648,7 @@ function readApprovals(parts) {
3482
3648
  return found;
3483
3649
  };
3484
3650
  for (const part of parts) {
3485
- if (part.type !== APPROVAL_PART && part.type !== SETTLED_PART) {
3651
+ if (part.type !== APPROVAL_PART && part.type !== SETTLED_PART && part.type !== "data-action-proposal") {
3486
3652
  continue;
3487
3653
  }
3488
3654
  const data = part.data;
@@ -3490,12 +3656,35 @@ function readApprovals(parts) {
3490
3656
  continue;
3491
3657
  }
3492
3658
  const approval = entry(data.id);
3493
- 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
+ }
3494
3676
  if (typeof data.approver !== "string") {
3495
3677
  continue;
3496
3678
  }
3497
3679
  approval.approver = data.approver;
3498
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
+ }
3499
3688
  approval.reason = typeof data.reason === "string" ? data.reason : null;
3500
3689
  continue;
3501
3690
  }
@@ -3519,9 +3708,12 @@ function approvalOf(parts, call) {
3519
3708
  }
3520
3709
  return {
3521
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 } : {},
3522
3714
  expiresAt: parts.expiresAt,
3523
3715
  reason: parts.reason,
3524
- status: parts.status ?? statusFromPart(call),
3716
+ status: parts.status ?? (parts.target?.kind === "proposal" ? "pending" : statusFromPart(call)),
3525
3717
  remember: parts.remember,
3526
3718
  decidedBy: parts.decidedBy,
3527
3719
  decidedVia: parts.decidedVia,
@@ -3551,6 +3743,10 @@ function readUiComponent(part) {
3551
3743
  component: data.component,
3552
3744
  props: isRecord4(data.props) ? data.props : {},
3553
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 } : {},
3554
3750
  toolCallId: typeof data.toolCallId === "string" ? data.toolCallId : null
3555
3751
  };
3556
3752
  }
@@ -3661,6 +3857,12 @@ function formatCostUsd(cost) {
3661
3857
  }
3662
3858
  return `$${cost.toFixed(2)}`;
3663
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
+ }
3664
3866
 
3665
3867
  // src/transcript/use-stick-to-bottom.ts
3666
3868
  var import_react16 = require("react");
@@ -3953,23 +4155,31 @@ function useTranscriptItems({
3953
4155
  [settle]
3954
4156
  );
3955
4157
  const approve = (0, import_react17.useCallback)(
3956
- (toolCallId, options2) => {
4158
+ (toolCallId, options2, target) => {
3957
4159
  const onApprove = latest.current.handlers.onApprove;
3958
4160
  if (onApprove) {
3959
4161
  settle(
3960
4162
  toolCallId,
3961
4163
  "approve",
3962
- () => onApprove({ toolCallId, ...options2?.remember === true ? { remember: true } : {} })
4164
+ () => onApprove({
4165
+ toolCallId,
4166
+ ...target === void 0 ? {} : { target },
4167
+ ...options2?.remember === true ? { remember: true } : {}
4168
+ })
3963
4169
  );
3964
4170
  }
3965
4171
  },
3966
4172
  [settle]
3967
4173
  );
3968
4174
  const reject = (0, import_react17.useCallback)(
3969
- (toolCallId) => {
4175
+ (toolCallId, target) => {
3970
4176
  const onReject = latest.current.handlers.onReject;
3971
4177
  if (onReject) {
3972
- settle(toolCallId, "reject", () => onReject({ toolCallId }));
4178
+ settle(
4179
+ toolCallId,
4180
+ "reject",
4181
+ () => onReject({ toolCallId, ...target === void 0 ? {} : { target } })
4182
+ );
3973
4183
  }
3974
4184
  },
3975
4185
  [settle]
@@ -4167,14 +4377,36 @@ function settleHandlers(options, backend) {
4167
4377
  ),
4168
4378
  onApprove: pick(
4169
4379
  options.onApprove,
4170
- backend.approveToolCall ? async (input) => {
4171
- 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);
4172
4393
  } : void 0
4173
4394
  ),
4174
4395
  onReject: pick(
4175
4396
  options.onReject,
4176
- backend.rejectToolCall ? async (input) => {
4177
- 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);
4178
4410
  } : void 0
4179
4411
  )
4180
4412
  };
@@ -5079,6 +5311,8 @@ function storedMessageToUiMessage(message) {
5079
5311
  id: component.id,
5080
5312
  component: component.component,
5081
5313
  props: component.props,
5314
+ ...component.componentVersions !== void 0 ? { componentVersions: component.componentVersions } : {},
5315
+ ...component.fallbackText !== void 0 ? { fallbackText: component.fallbackText } : {},
5082
5316
  ...component.version !== void 0 ? { version: component.version } : {},
5083
5317
  ...component.toolCallId !== void 0 ? { toolCallId: component.toolCallId } : {}
5084
5318
  }
@@ -5098,6 +5332,8 @@ function storedMessageToUiMessage(message) {
5098
5332
  data: {
5099
5333
  id: approval.toolCallId,
5100
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 } : {},
5101
5337
  ...approval.expiresAt !== void 0 ? { expiresAt: approval.expiresAt } : {}
5102
5338
  }
5103
5339
  });
@@ -5154,8 +5390,11 @@ function storedMessageToUiMessage(message) {
5154
5390
  const metadata = {
5155
5391
  // The host's own facts first: the library's keys win on a clash.
5156
5392
  ...message.metadata,
5393
+ ...message.actionProposalOutcome === void 0 ? {} : { actionProposalOutcomeId: message.actionProposalOutcome.id },
5157
5394
  ...message.feedback !== void 0 ? { feedback: message.feedback } : {},
5158
5395
  ...message.createdAt ? { createdAt: message.createdAt } : {},
5396
+ ...message.agentName !== void 0 ? { agentName: message.agentName } : {},
5397
+ ...message.persona !== void 0 ? { persona: message.persona } : {},
5159
5398
  ...message.usage !== void 0 ? {
5160
5399
  usage: {
5161
5400
  inputTokens: message.usage.inputTokens,
@@ -5187,6 +5426,8 @@ function storedThreadToUiMessages(messages) {
5187
5426
  return turns.map(mergeTurn);
5188
5427
  }
5189
5428
  function sameRun(previous, next) {
5429
+ if (previous.actionProposalOutcome !== void 0 || next.actionProposalOutcome !== void 0)
5430
+ return false;
5190
5431
  return previous.runId === void 0 || next.runId === void 0 || previous.runId === next.runId;
5191
5432
  }
5192
5433
  function mergeTurn(rows) {
@@ -5206,7 +5447,9 @@ function mergeTurn(rows) {
5206
5447
  ...Object.assign({}, ...rows.map((row) => row.metadata ?? {})),
5207
5448
  ...usage !== void 0 ? { usage } : {},
5208
5449
  ...feedback !== void 0 ? { feedback } : {},
5209
- ...createdAt ? { createdAt } : {}
5450
+ ...createdAt ? { createdAt } : {},
5451
+ ...lastRow.agentName !== void 0 ? { agentName: lastRow.agentName } : {},
5452
+ ...lastRow.persona !== void 0 ? { persona: lastRow.persona } : {}
5210
5453
  };
5211
5454
  return {
5212
5455
  id: lastRow.id,
@@ -5231,8 +5474,163 @@ function sumUsage(rows) {
5231
5474
  }
5232
5475
 
5233
5476
  // src/use-agent-chat.ts
5234
- var import_react22 = require("@ai-sdk/react");
5235
- 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
5236
5634
  var EMPTY_QUEUE = { items: [], paused: null };
5237
5635
  var localQueueSequence = 0;
5238
5636
  function queuedUserMessage(turn) {
@@ -5287,13 +5685,16 @@ function localChatId() {
5287
5685
  return `local-chat-${localChatSequence}`;
5288
5686
  }
5289
5687
  function useAgentChat(options = {}) {
5290
- 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);
5291
5692
  latest.current = options;
5292
5693
  const resolved = useAgentBackend(options.backend);
5293
- const client = (0, import_react23.useRef)(resolved).current;
5294
- const createdThreadId = (0, import_react23.useRef)(void 0);
5295
- const chatIdRef = (0, import_react23.useRef)(options.threadId ?? localChatId());
5296
- 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);
5297
5698
  if (options.threadId !== boundThreadRef.current) {
5298
5699
  boundThreadRef.current = options.threadId;
5299
5700
  const adopting = options.threadId !== void 0 && options.threadId === createdThreadId.current;
@@ -5303,39 +5704,43 @@ function useAgentChat(options = {}) {
5303
5704
  }
5304
5705
  }
5305
5706
  const chatId = chatIdRef.current;
5306
- const [runId, setRunId] = (0, import_react23.useState)(options.resumeRunId);
5307
- 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);
5308
5709
  runIdRef.current = runId;
5309
- const settlingRunIdRef = (0, import_react23.useRef)(void 0);
5310
- const autoResumeRunIdRef = (0, import_react23.useRef)(void 0);
5311
- const [activeRunId, setActiveRunId] = (0, import_react23.useState)(null);
5312
- const [historyFor, setHistoryFor] = (0, import_react23.useState)(void 0);
5313
- const [historyError, setHistoryError] = (0, import_react23.useState)(null);
5314
- const [runError, setRunError] = (0, import_react23.useState)(null);
5315
- const [threadModel, setThreadModel] = (0, import_react23.useState)(null);
5316
- const [pickedModel, setPickedModel] = (0, import_react23.useState)(void 0);
5317
- 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);
5318
5720
  pickedModelRef.current = pickedModel;
5319
- const lockedRef = (0, import_react23.useRef)(false);
5320
- const pendingPin = (0, import_react23.useRef)(void 0);
5321
- const regenerateNext = (0, import_react23.useRef)(false);
5322
- 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)(
5323
5725
  () => latest.current.threadId ?? createdThreadId.current,
5324
5726
  []
5325
5727
  );
5326
- const [queueState, setQueueState] = (0, import_react23.useState)(EMPTY_QUEUE);
5327
- const queueStateRef = (0, import_react23.useRef)(queueState);
5328
- 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) => {
5329
5731
  queueStateRef.current = next;
5330
5732
  setQueueState(next);
5331
5733
  }, []);
5332
- const [pendingSends, setPendingSends] = (0, import_react23.useState)([]);
5333
- const [queueError, setQueueError] = (0, import_react23.useState)(null);
5334
- const handoff = (0, import_react23.useRef)(void 0);
5335
- const queuedSend = (0, import_react23.useRef)(void 0);
5336
- const threadWaiters = (0, import_react23.useRef)([]);
5337
- 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)(() => {
5338
5742
  const current = latest.current;
5743
+ if (current.persona !== void 0) setThreadPersona(current.persona);
5339
5744
  const pageContext = current.getPageContext?.() ?? null;
5340
5745
  const threadId = currentThreadId();
5341
5746
  const model = current.model ?? (lockedRef.current ? void 0 : pickedModelRef.current);
@@ -5343,14 +5748,16 @@ function useAgentChat(options = {}) {
5343
5748
  // Read per send: a host that switches agents before the first message (a picker on a new
5344
5749
  // chat) sends the one picked now, not the one this chat mounted with.
5345
5750
  ...current.agent !== void 0 ? { agent: current.agent } : {},
5751
+ ...current.persona !== void 0 ? { persona: current.persona } : {},
5346
5752
  ...threadId !== void 0 ? { threadId } : {},
5347
5753
  ...model !== void 0 ? { model } : {},
5348
- ...pageContext ? { pageContext } : {}
5754
+ ...pageContext ? { pageContext } : {},
5755
+ ...(current.uiCapabilities ?? capabilitiesRef.current) === void 0 ? {} : { uiCapabilities: current.uiCapabilities ?? capabilitiesRef.current }
5349
5756
  };
5350
5757
  }, [currentThreadId]);
5351
- const [connection, setConnection] = (0, import_react23.useState)({ status: "live" });
5352
- const resyncAfterSettle = (0, import_react23.useRef)(false);
5353
- 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)(() => {
5354
5761
  function onMeta(meta) {
5355
5762
  settlingRunIdRef.current = meta.runId;
5356
5763
  setRunId(meta.runId);
@@ -5402,14 +5809,15 @@ function useAgentChat(options = {}) {
5402
5809
  }
5403
5810
  });
5404
5811
  }, []);
5405
- const backgroundRef = (0, import_react23.useRef)(() => Promise.resolve());
5406
- const chat = (0, import_react22.useChat)({
5812
+ const backgroundRef = (0, import_react24.useRef)(() => Promise.resolve());
5813
+ const chat = (0, import_react23.useChat)({
5407
5814
  transport,
5408
5815
  id: chatId,
5409
5816
  resume: options.resumeRunId !== void 0,
5410
5817
  ...options.initialMessages !== void 0 ? { messages: options.initialMessages } : {},
5411
5818
  onData: (part) => {
5412
5819
  latest.current.onData?.(part);
5820
+ if (part.type === "data-proposal-decision") void proposalRefreshRef.current();
5413
5821
  if (part.type === "data-queue") {
5414
5822
  onQueueData(part.data);
5415
5823
  }
@@ -5456,7 +5864,7 @@ function useAgentChat(options = {}) {
5456
5864
  }
5457
5865
  }
5458
5866
  });
5459
- const chatRef = (0, import_react23.useRef)(chat);
5867
+ const chatRef = (0, import_react24.useRef)(chat);
5460
5868
  chatRef.current = chat;
5461
5869
  function onQueueData(data) {
5462
5870
  const frame = data ?? {};
@@ -5504,13 +5912,14 @@ function useAgentChat(options = {}) {
5504
5912
  } catch {
5505
5913
  }
5506
5914
  }
5507
- const firstChatId = (0, import_react23.useRef)(chatId);
5508
- (0, import_react23.useEffect)(() => {
5915
+ const firstChatId = (0, import_react24.useRef)(chatId);
5916
+ (0, import_react24.useEffect)(() => {
5509
5917
  if (chatId === firstChatId.current) return;
5510
5918
  firstChatId.current = chatId;
5511
5919
  setRunId(void 0);
5512
5920
  setActiveRunId(null);
5513
5921
  setThreadModel(null);
5922
+ setThreadPersona(null);
5514
5923
  setPickedModel(void 0);
5515
5924
  setBackgroundRuns([]);
5516
5925
  setHistoryError(null);
@@ -5523,7 +5932,7 @@ function useAgentChat(options = {}) {
5523
5932
  handoff.current = void 0;
5524
5933
  queuedSend.current = void 0;
5525
5934
  }, [chatId]);
5526
- (0, import_react23.useEffect)(() => {
5935
+ (0, import_react24.useEffect)(() => {
5527
5936
  const threadId = options.threadId;
5528
5937
  if (threadId === void 0) return;
5529
5938
  if (threadId === createdThreadId.current) return;
@@ -5539,6 +5948,7 @@ function useAgentChat(options = {}) {
5539
5948
  const stored = Array.isArray(thread.messages) ? thread.messages : [];
5540
5949
  setActiveRunId(active);
5541
5950
  setThreadModel(thread.model ?? null);
5951
+ setThreadPersona(thread.persona ?? null);
5542
5952
  setHistoryError(null);
5543
5953
  const queue2 = thread.queue ?? EMPTY_QUEUE;
5544
5954
  applyQueue(queue2);
@@ -5569,8 +5979,8 @@ function useAgentChat(options = {}) {
5569
5979
  };
5570
5980
  }, [client, chatId, options.threadId]);
5571
5981
  const isLoadingHistory = options.threadId !== void 0 && options.threadId !== createdThreadId.current && options.history !== false && options.initialMessages === void 0 && historyFor !== options.threadId;
5572
- const turnInFlight = (0, import_react23.useRef)(false);
5573
- const isTurnInFlight = (0, import_react23.useCallback)(
5982
+ const turnInFlight = (0, import_react24.useRef)(false);
5983
+ const isTurnInFlight = (0, import_react24.useCallback)(
5574
5984
  () => turnInFlight.current || transport.isAttemptLive,
5575
5985
  [transport]
5576
5986
  );
@@ -5579,20 +5989,20 @@ function useAgentChat(options = {}) {
5579
5989
  enabled: options.quota !== false && typeof client.getQuota === "function"
5580
5990
  });
5581
5991
  const blocked = options.blocked !== void 0 ? options.blocked : quota.blocked;
5582
- const blockedRef = (0, import_react23.useRef)(blocked);
5992
+ const blockedRef = (0, import_react24.useRef)(blocked);
5583
5993
  blockedRef.current = blocked;
5584
5994
  const whileRunning = typeof client.enqueueMessage === "function" ? options.whileRunning ?? "queue" : "block";
5585
- const whileRunningRef = (0, import_react23.useRef)(whileRunning);
5995
+ const whileRunningRef = (0, import_react24.useRef)(whileRunning);
5586
5996
  whileRunningRef.current = whileRunning;
5587
- const canQueueRef = (0, import_react23.useRef)(false);
5997
+ const canQueueRef = (0, import_react24.useRef)(false);
5588
5998
  canQueueRef.current = typeof client.enqueueMessage === "function";
5589
- const threadForQueue = (0, import_react23.useCallback)(async () => {
5999
+ const threadForQueue = (0, import_react24.useCallback)(async () => {
5590
6000
  const known = currentThreadId();
5591
6001
  if (known !== void 0) return known;
5592
6002
  if (!isTurnInFlight()) throw new Error("queue: this chat has no thread yet");
5593
6003
  return new Promise((resolve) => threadWaiters.current.push(resolve));
5594
6004
  }, [currentThreadId, isTurnInFlight]);
5595
- const queueCall = (0, import_react23.useCallback)(async (run) => {
6005
+ const queueCall = (0, import_react24.useCallback)(async (run) => {
5596
6006
  try {
5597
6007
  const result = await run();
5598
6008
  setQueueError(null);
@@ -5602,7 +6012,7 @@ function useAgentChat(options = {}) {
5602
6012
  throw error;
5603
6013
  }
5604
6014
  }, []);
5605
- const enqueue = (0, import_react23.useCallback)(
6015
+ const enqueue = (0, import_react24.useCallback)(
5606
6016
  async (text2, {
5607
6017
  attachments = [],
5608
6018
  mode = "queue"
@@ -5650,7 +6060,7 @@ function useAgentChat(options = {}) {
5650
6060
  },
5651
6061
  [client, queueCall, threadForQueue, turnBody, applyQueue, isTurnInFlight]
5652
6062
  );
5653
- const sendMessage = (0, import_react23.useCallback)(
6063
+ const sendMessage = (0, import_react24.useCallback)(
5654
6064
  async (message, options2) => {
5655
6065
  const block = blockedRef.current;
5656
6066
  if (block != null) throw new QuotaBlockedError(block);
@@ -5674,7 +6084,7 @@ function useAgentChat(options = {}) {
5674
6084
  },
5675
6085
  [isTurnInFlight, enqueue]
5676
6086
  );
5677
- const removeQueued = (0, import_react23.useCallback)(
6087
+ const removeQueued = (0, import_react24.useCallback)(
5678
6088
  async (id) => {
5679
6089
  const previous = queueStateRef.current;
5680
6090
  applyQueue({ ...previous, items: previous.items.filter((item) => item.id !== id) });
@@ -5687,7 +6097,7 @@ function useAgentChat(options = {}) {
5687
6097
  },
5688
6098
  [client, queueCall, applyQueue]
5689
6099
  );
5690
- const interruptQueued = (0, import_react23.useCallback)(
6100
+ const interruptQueued = (0, import_react24.useCallback)(
5691
6101
  async (id) => {
5692
6102
  const previous = queueStateRef.current;
5693
6103
  const moving = previous.items.find((item) => item.id === id);
@@ -5722,7 +6132,7 @@ function useAgentChat(options = {}) {
5722
6132
  },
5723
6133
  [client, queueCall, applyQueue, isTurnInFlight]
5724
6134
  );
5725
- const editQueued = (0, import_react23.useCallback)(
6135
+ const editQueued = (0, import_react24.useCallback)(
5726
6136
  async (id, text2) => {
5727
6137
  applyQueue(
5728
6138
  await queueCall(
@@ -5732,7 +6142,7 @@ function useAgentChat(options = {}) {
5732
6142
  },
5733
6143
  [client, queueCall, applyQueue]
5734
6144
  );
5735
- const moveQueued = (0, import_react23.useCallback)(
6145
+ const moveQueued = (0, import_react24.useCallback)(
5736
6146
  async (id, index) => {
5737
6147
  const previous = queueStateRef.current;
5738
6148
  const moving = previous.items.find((item) => item.id === id);
@@ -5754,12 +6164,12 @@ function useAgentChat(options = {}) {
5754
6164
  },
5755
6165
  [client, queueCall, applyQueue]
5756
6166
  );
5757
- const clearQueue = (0, import_react23.useCallback)(async () => {
6167
+ const clearQueue = (0, import_react24.useCallback)(async () => {
5758
6168
  const threadId = currentThreadId();
5759
6169
  if (threadId === void 0) return;
5760
6170
  applyQueue(await queueCall(() => requireBackendMethod(client, "clearQueue")(threadId)));
5761
6171
  }, [client, queueCall, currentThreadId, applyQueue]);
5762
- const resumeQueue = (0, import_react23.useCallback)(async () => {
6172
+ const resumeQueue = (0, import_react24.useCallback)(async () => {
5763
6173
  const threadId = currentThreadId();
5764
6174
  if (threadId === void 0) return;
5765
6175
  const before = queueStateRef.current;
@@ -5777,7 +6187,7 @@ function useAgentChat(options = {}) {
5777
6187
  else attachTo(turn);
5778
6188
  }
5779
6189
  }, [client, queueCall, currentThreadId, applyQueue, isTurnInFlight]);
5780
- const resumeQueueRef = (0, import_react23.useRef)(resumeQueue);
6190
+ const resumeQueueRef = (0, import_react24.useRef)(resumeQueue);
5781
6191
  resumeQueueRef.current = resumeQueue;
5782
6192
  const queuedItems = [
5783
6193
  ...pendingSends.filter((item) => item.interrupt),
@@ -5807,7 +6217,7 @@ function useAgentChat(options = {}) {
5807
6217
  resume: resumeQueue,
5808
6218
  error: queueError
5809
6219
  };
5810
- const addToolResult = (0, import_react23.useCallback)(
6220
+ const addToolResult = (0, import_react24.useCallback)(
5811
6221
  ({ tool, toolCallId, output }) => chatRef.current.addToolResult({
5812
6222
  tool,
5813
6223
  toolCallId,
@@ -5815,8 +6225,8 @@ function useAgentChat(options = {}) {
5815
6225
  }),
5816
6226
  []
5817
6227
  );
5818
- const [backgroundRuns, setBackgroundRuns] = (0, import_react23.useState)([]);
5819
- const refreshBackground = (0, import_react23.useCallback)(async () => {
6228
+ const [backgroundRuns, setBackgroundRuns] = (0, import_react24.useState)([]);
6229
+ const refreshBackground = (0, import_react24.useCallback)(async () => {
5820
6230
  const threadId = currentThreadId();
5821
6231
  if (threadId === void 0) {
5822
6232
  return;
@@ -5836,13 +6246,13 @@ function useAgentChat(options = {}) {
5836
6246
  }, [client, currentThreadId]);
5837
6247
  backgroundRef.current = refreshBackground;
5838
6248
  const isWorking = backgroundRuns.some((run) => run.status === "running");
5839
- (0, import_react23.useEffect)(() => {
6249
+ (0, import_react24.useEffect)(() => {
5840
6250
  if (options.background !== true || options.threadId === void 0) {
5841
6251
  return;
5842
6252
  }
5843
6253
  void refreshBackground();
5844
6254
  }, [options.background, options.threadId, chatId, refreshBackground]);
5845
- (0, import_react23.useEffect)(() => {
6255
+ (0, import_react24.useEffect)(() => {
5846
6256
  const every = latest.current.backgroundPollMs ?? 5e3;
5847
6257
  if (latest.current.background !== true || !isWorking || every <= 0) {
5848
6258
  return;
@@ -5850,7 +6260,15 @@ function useAgentChat(options = {}) {
5850
6260
  const timer = setInterval(() => void refreshBackground(), every);
5851
6261
  return () => clearInterval(timer);
5852
6262
  }, [isWorking, refreshBackground]);
5853
- 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 () => {
5854
6272
  chatRef.current.stop();
5855
6273
  const active = runIdRef.current;
5856
6274
  if (active) {
@@ -5867,8 +6285,23 @@ function useAgentChat(options = {}) {
5867
6285
  }
5868
6286
  }
5869
6287
  }, [client, currentThreadId, applyQueue]);
5870
- const approve = (0, import_react23.useCallback)(
5871
- 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
+ }
5872
6305
  await requireBackendMethod(
5873
6306
  client,
5874
6307
  "approveToolCall"
@@ -5878,10 +6311,25 @@ function useAgentChat(options = {}) {
5878
6311
  ...via !== void 0 ? { via } : {}
5879
6312
  });
5880
6313
  },
5881
- [client]
6314
+ [client, currentThreadId, proposals.refresh]
5882
6315
  );
5883
- const reject = (0, import_react23.useCallback)(
5884
- 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
+ }
5885
6333
  await requireBackendMethod(
5886
6334
  client,
5887
6335
  "rejectToolCall"
@@ -5891,9 +6339,9 @@ function useAgentChat(options = {}) {
5891
6339
  ...via !== void 0 ? { via } : {}
5892
6340
  });
5893
6341
  },
5894
- [client]
6342
+ [client, currentThreadId, proposals.refresh]
5895
6343
  );
5896
- const answer = (0, import_react23.useCallback)(
6344
+ const answer = (0, import_react24.useCallback)(
5897
6345
  async ({ toolCallId, answers, via }) => {
5898
6346
  await requireBackendMethod(
5899
6347
  client,
@@ -5906,7 +6354,7 @@ function useAgentChat(options = {}) {
5906
6354
  },
5907
6355
  [client]
5908
6356
  );
5909
- const skip = (0, import_react23.useCallback)(
6357
+ const skip = (0, import_react24.useCallback)(
5910
6358
  async ({ toolCallId, via }) => {
5911
6359
  await requireBackendMethod(
5912
6360
  client,
@@ -5915,7 +6363,7 @@ function useAgentChat(options = {}) {
5915
6363
  },
5916
6364
  [client]
5917
6365
  );
5918
- const regenerate = (0, import_react23.useCallback)(
6366
+ const regenerate = (0, import_react24.useCallback)(
5919
6367
  (_input) => {
5920
6368
  const block = blockedRef.current;
5921
6369
  if (block != null) throw new QuotaBlockedError(block);
@@ -5928,7 +6376,7 @@ function useAgentChat(options = {}) {
5928
6376
  },
5929
6377
  [isTurnInFlight]
5930
6378
  );
5931
- const storedIdOf = (0, import_react23.useCallback)(
6379
+ const storedIdOf = (0, import_react24.useCallback)(
5932
6380
  async (messageId, threadId) => {
5933
6381
  const message = chatRef.current.messages.find((candidate) => candidate.id === messageId);
5934
6382
  const runOf = message?.metadata?.runId;
@@ -5939,7 +6387,7 @@ function useAgentChat(options = {}) {
5939
6387
  },
5940
6388
  [client]
5941
6389
  );
5942
- const fork = (0, import_react23.useCallback)(
6390
+ const fork = (0, import_react24.useCallback)(
5943
6391
  async ({
5944
6392
  messageId,
5945
6393
  threadId
@@ -5954,7 +6402,7 @@ function useAgentChat(options = {}) {
5954
6402
  },
5955
6403
  [client, currentThreadId, storedIdOf]
5956
6404
  );
5957
- const truncateFrom = (0, import_react23.useCallback)(
6405
+ const truncateFrom = (0, import_react24.useCallback)(
5958
6406
  async ({ messageId, threadId }) => {
5959
6407
  const target = threadId ?? currentThreadId();
5960
6408
  if (target === void 0) throw new Error("truncateFrom: this chat has no thread yet");
@@ -5963,7 +6411,7 @@ function useAgentChat(options = {}) {
5963
6411
  },
5964
6412
  [client, currentThreadId, storedIdOf]
5965
6413
  );
5966
- const promote = (0, import_react23.useCallback)(
6414
+ const promote = (0, import_react24.useCallback)(
5967
6415
  async ({ threadId } = {}) => {
5968
6416
  const target = threadId ?? currentThreadId();
5969
6417
  if (target === void 0) throw new Error("promote: this chat has no thread yet");
@@ -5972,24 +6420,24 @@ function useAgentChat(options = {}) {
5972
6420
  },
5973
6421
  [client, currentThreadId]
5974
6422
  );
5975
- const [modelsWanted, setModelsWanted] = (0, import_react23.useState)(false);
5976
- const modelsWantedRef = (0, import_react23.useRef)(modelsWanted);
6423
+ const [modelsWanted, setModelsWanted] = (0, import_react24.useState)(false);
6424
+ const modelsWantedRef = (0, import_react24.useRef)(modelsWanted);
5977
6425
  modelsWantedRef.current = modelsWanted;
5978
6426
  const catalog = useModels({
5979
6427
  backend: client,
5980
6428
  ...options.agent !== void 0 ? { agent: options.agent } : {},
5981
6429
  enabled: modelsWanted && typeof client.listModels === "function"
5982
6430
  });
5983
- const wantModels = (0, import_react23.useCallback)(() => {
6431
+ const wantModels = (0, import_react24.useCallback)(() => {
5984
6432
  if (modelsWantedRef.current) return;
5985
6433
  modelsWantedRef.current = true;
5986
6434
  queueMicrotask(() => setModelsWanted(true));
5987
6435
  }, []);
5988
6436
  lockedRef.current = catalog.locked !== null;
5989
- const selectModel = (0, import_react23.useCallback)((id) => {
6437
+ const selectModel = (0, import_react24.useCallback)((id) => {
5990
6438
  if (!lockedRef.current) setPickedModel(id);
5991
6439
  }, []);
5992
- const pinToThread = (0, import_react23.useCallback)(
6440
+ const pinToThread = (0, import_react24.useCallback)(
5993
6441
  async (model) => {
5994
6442
  const threadId = currentThreadId();
5995
6443
  setThreadModel(model);
@@ -6029,14 +6477,14 @@ function useAgentChat(options = {}) {
6029
6477
  };
6030
6478
  const status = connection.status === "reconnecting" ? "reconnecting" : chat.status;
6031
6479
  const isBusy = status === "submitted" || status === "streaming" || status === "reconnecting";
6032
- const isBusyRef = (0, import_react23.useRef)(isBusy);
6480
+ const isBusyRef = (0, import_react24.useRef)(isBusy);
6033
6481
  isBusyRef.current = isBusy;
6034
6482
  const files = useAttachments({ ...options.composer, backend: client });
6035
- const [text, setText] = (0, import_react23.useState)("");
6483
+ const [text, setText] = (0, import_react24.useState)("");
6036
6484
  const blockedBy = blocked != null ? "quota" : files.isUploading ? "uploading" : isBusy && whileRunning === "block" ? "busy" : text.trim().length === 0 && files.refs.length === 0 ? "empty" : null;
6037
- const composerRef = (0, import_react23.useRef)({ text, files, blockedBy });
6485
+ const composerRef = (0, import_react24.useRef)({ text, files, blockedBy });
6038
6486
  composerRef.current = { text, files, blockedBy };
6039
- const submit = (0, import_react23.useCallback)(
6487
+ const submit = (0, import_react24.useCallback)(
6040
6488
  async (options2) => {
6041
6489
  const current = composerRef.current;
6042
6490
  const asked = canQueueRef.current ? options2?.mode : void 0;
@@ -6118,6 +6566,7 @@ function useAgentChat(options = {}) {
6118
6566
  addToolResult,
6119
6567
  /** Sub-agents this conversation started and did not wait for. */
6120
6568
  background,
6569
+ proposals,
6121
6570
  runId,
6122
6571
  /** The loaded thread's running turn, or `null` once read with none. */
6123
6572
  activeRunId,
@@ -6150,6 +6599,11 @@ function useAgentChat(options = {}) {
6150
6599
  queue,
6151
6600
  /** The model picker: `list`, `selected`, `select(id)`, `pinToThread(id)`. */
6152
6601
  models,
6602
+ /**
6603
+ * The persona pinned on this thread (`ThreadSummary.persona`), or `null` when none is — what a
6604
+ * persona picker on a reopened thread starts from (`?? agent.defaultPersona`).
6605
+ */
6606
+ threadPersona,
6153
6607
  /** The caller's budget (`GET <base>/quota`); `quota.blocked` gates sends. */
6154
6608
  quota,
6155
6609
  /** The window blocking sends right now (the `blocked` option, else the quota report's). */
@@ -6237,8 +6691,10 @@ function isRunNotActiveError(error) {
6237
6691
  onThreadsEvent,
6238
6692
  persistSpeechLang,
6239
6693
  phraseFor,
6694
+ proposalNeedsPolling,
6240
6695
  readPath,
6241
6696
  readReasoningMs,
6697
+ reconcileProposalMessages,
6242
6698
  reframeAgUiStream,
6243
6699
  requireBackendMethod,
6244
6700
  resolveResultView,
@@ -6246,6 +6702,7 @@ function isRunNotActiveError(error) {
6246
6702
  storedThreadToUiMessages,
6247
6703
  toolCallState,
6248
6704
  toolCatalogFrom,
6705
+ useActionProposals,
6249
6706
  useAgentBackend,
6250
6707
  useAgentChat,
6251
6708
  useAgentConfig,