@dudousxd/nestjs-agent-react 0.10.0 → 0.12.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
@@ -41,8 +41,10 @@ __export(src_exports, {
41
41
  MessageList: () => MessageList,
42
42
  SPEECH_LANGUAGES: () => SPEECH_LANGUAGES,
43
43
  applyCompletion: () => applyCompletion,
44
+ approvalCountdown: () => approvalCountdown,
44
45
  backgroundRunsFromThread: () => backgroundRunsFromThread,
45
46
  buildTranscriptBlocks: () => buildTranscriptBlocks,
47
+ coerceAnswer: () => coerceAnswer,
46
48
  correctedCallIds: () => correctedCallIds,
47
49
  createSkillsSource: () => createSkillsSource,
48
50
  describeTimestamp: () => describeTimestamp,
@@ -68,13 +70,16 @@ __export(src_exports, {
68
70
  toolCallState: () => toolCallState,
69
71
  toolCatalogFrom: () => toolCatalogFrom,
70
72
  useAgentChat: () => useAgentChat,
73
+ useApprovalCountdown: () => useApprovalCountdown,
71
74
  useChatTranscript: () => useChatTranscript,
72
75
  useComposerAutocomplete: () => useComposerAutocomplete,
73
76
  useElapsed: () => useElapsed,
74
77
  useSpeechRecognition: () => useSpeechRecognition,
75
78
  useStickToBottom: () => useStickToBottom,
76
79
  useToolCatalog: () => useToolCatalog,
77
- useTranscriptItem: () => useTranscriptItem
80
+ useTranscriptItem: () => useTranscriptItem,
81
+ validateAnswer: () => import_nestjs_agent_core.validateElicitationAnswer,
82
+ validateAnswerValue: () => import_nestjs_agent_core.validateElicitationValue
78
83
  });
79
84
  module.exports = __toCommonJS(src_exports);
80
85
 
@@ -521,6 +526,10 @@ var AgentChatTransport = class {
521
526
  });
522
527
  }
523
528
  break;
529
+ case "approval-settled":
530
+ ensureStep();
531
+ forwardAsData(event, false);
532
+ break;
524
533
  case "ui":
525
534
  ensureStep();
526
535
  closeRuns();
@@ -820,6 +829,10 @@ var AgentClient = class {
820
829
  cancelStream(runId) {
821
830
  return this.request("POST", `/agent/chat/${encodeURIComponent(runId)}/cancel`);
822
831
  }
832
+ /**
833
+ * `remember` approves later calls of the same tool in the same thread; `via` names the surface
834
+ * the decision came through (the server records `'web'` when omitted).
835
+ */
823
836
  approveToolCall(input) {
824
837
  return this.request("POST", "/agent/tool-call/approve", input);
825
838
  }
@@ -1047,8 +1060,66 @@ var import_react6 = __toESM(require("react"), 1);
1047
1060
  var import_react5 = require("react");
1048
1061
 
1049
1062
  // src/transcript/model.ts
1063
+ var import_nestjs_agent_core2 = require("@dudousxd/nestjs-agent-core");
1050
1064
  var import_ai2 = require("ai");
1051
1065
 
1066
+ // src/elicitation/answers.ts
1067
+ var import_nestjs_agent_core = require("@dudousxd/nestjs-agent-core");
1068
+ function pad(value) {
1069
+ return String(value).padStart(2, "0");
1070
+ }
1071
+ __name(pad, "pad");
1072
+ function toDay(date) {
1073
+ if (Number.isNaN(date.getTime())) {
1074
+ return null;
1075
+ }
1076
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
1077
+ }
1078
+ __name(toDay, "toDay");
1079
+ function coerceOne(type, raw) {
1080
+ if (raw === null || raw === void 0) {
1081
+ return null;
1082
+ }
1083
+ if (Array.isArray(raw)) {
1084
+ return null;
1085
+ }
1086
+ if (raw instanceof Date) {
1087
+ return type === "date" || type === void 0 ? toDay(raw) : raw.toISOString();
1088
+ }
1089
+ if (typeof raw === "boolean") {
1090
+ return raw ? "true" : "false";
1091
+ }
1092
+ if (typeof raw === "number") {
1093
+ return Number.isFinite(raw) ? String(raw) : null;
1094
+ }
1095
+ const text = raw;
1096
+ switch (type) {
1097
+ case "number":
1098
+ case "email":
1099
+ case "url":
1100
+ case "date":
1101
+ return text.trim() === "" ? null : text.trim();
1102
+ case "boolean": {
1103
+ const lowered = text.trim().toLowerCase();
1104
+ if (lowered === "true" || lowered === "on" || lowered === "yes") return "true";
1105
+ if (lowered === "false" || lowered === "off" || lowered === "no") return "false";
1106
+ return lowered === "" ? null : text;
1107
+ }
1108
+ default:
1109
+ return text === "" ? null : text;
1110
+ }
1111
+ }
1112
+ __name(coerceOne, "coerceOne");
1113
+ function coerceAnswer(question, raw) {
1114
+ const type = question.input?.type;
1115
+ const items = Array.isArray(raw) ? raw : [
1116
+ raw
1117
+ ];
1118
+ const values = items.map((item) => coerceOne(type, item)).filter((value) => value !== null);
1119
+ return question.multiple === true ? values : values.slice(0, 1);
1120
+ }
1121
+ __name(coerceAnswer, "coerceAnswer");
1122
+
1052
1123
  // src/presentation/tool-activity.ts
1053
1124
  var import_ai = require("ai");
1054
1125
 
@@ -1337,7 +1408,7 @@ function buildTranscriptBlocks(message, options) {
1337
1408
  if (toolBuffer.length === 0) {
1338
1409
  return;
1339
1410
  }
1340
- const calls = toolBuffer.map((part) => buildToolCall(part, options.approval, approvals.get(part.toolCallId) ?? null, options.toolCatalog));
1411
+ const calls = toolBuffer.map((part) => buildToolCall(part, options.approval, approvalOf(approvals.get(part.toolCallId), part), options.toolCatalog));
1341
1412
  const roots = nestToolCalls(calls);
1342
1413
  blocks.push({
1343
1414
  kind: "tools",
@@ -1412,7 +1483,7 @@ function buildTranscriptBlocks(message, options) {
1412
1483
  fileBuffer.push(part);
1413
1484
  continue;
1414
1485
  }
1415
- if (part.type === APPROVAL_PART) {
1486
+ if (part.type === APPROVAL_PART || part.type === SETTLED_PART) {
1416
1487
  continue;
1417
1488
  }
1418
1489
  flushAll();
@@ -1563,7 +1634,11 @@ function buildToolCall(part, options, approval, catalog) {
1563
1634
  approve: {
1564
1635
  available: awaiting && options?.canApprove === true,
1565
1636
  isSubmitting: sending === "approve",
1566
- run: /* @__PURE__ */ __name(() => options?.approve(toolCallId), "run")
1637
+ // Read by field, not passed through: `onClick={call.approve.run}` hands this a click event,
1638
+ // and a host's handler must not receive that as its options.
1639
+ run: /* @__PURE__ */ __name((approveOptions) => approveOptions?.remember === true ? options?.approve(toolCallId, {
1640
+ remember: true
1641
+ }) : options?.approve(toolCallId), "run")
1567
1642
  },
1568
1643
  reject: {
1569
1644
  available: awaiting && options?.canReject === true,
@@ -1581,11 +1656,16 @@ function readElicitationRequest(part) {
1581
1656
  }
1582
1657
  const questions = [];
1583
1658
  for (const candidate of input.questions) {
1584
- if (!isRecord2(candidate) || typeof candidate.id !== "string" || typeof candidate.prompt !== "string" || !Array.isArray(candidate.options) || candidate.options.length === 0) {
1659
+ if (!isRecord2(candidate) || typeof candidate.id !== "string" || typeof candidate.prompt !== "string") {
1660
+ return null;
1661
+ }
1662
+ const input2 = (0, import_nestjs_agent_core2.readElicitationInput)(candidate.input) ?? null;
1663
+ const rawOptions = Array.isArray(candidate.options) ? candidate.options : [];
1664
+ if (rawOptions.length === 0 && (input2 === null || input2.type === "select")) {
1585
1665
  return null;
1586
1666
  }
1587
1667
  const options = [];
1588
- for (const option of candidate.options) {
1668
+ for (const option of rawOptions) {
1589
1669
  if (!isRecord2(option) || typeof option.value !== "string" || typeof option.label !== "string") {
1590
1670
  return null;
1591
1671
  }
@@ -1598,7 +1678,10 @@ function readElicitationRequest(part) {
1598
1678
  questions.push({
1599
1679
  id: candidate.id,
1600
1680
  prompt: candidate.prompt,
1681
+ description: typeof candidate.description === "string" ? candidate.description : null,
1682
+ input: input2,
1601
1683
  multiple: candidate.multiple === true,
1684
+ allowFreeText: candidate.allowFreeText === true,
1602
1685
  defaults: Array.isArray(candidate.defaults) ? candidate.defaults.filter((value) => typeof value === "string") : [],
1603
1686
  options
1604
1687
  });
@@ -1634,12 +1717,38 @@ function buildElicitationBlock(key, part, request, options) {
1634
1717
  const picked = isPending ? options.picked(toolCallId, question.id) : void 0;
1635
1718
  const selected = picked ?? outcome?.answers[question.id] ?? question.defaults;
1636
1719
  const pick = /* @__PURE__ */ __name((values) => options.pick(toolCallId, question.id, values), "pick");
1720
+ const error = isPending ? (0, import_nestjs_agent_core2.validateElicitationAnswer)({
1721
+ id: question.id,
1722
+ prompt: question.prompt,
1723
+ options: question.options.map(({ value, label }) => ({
1724
+ value,
1725
+ label
1726
+ })),
1727
+ ...question.input !== null ? {
1728
+ input: question.input
1729
+ } : {},
1730
+ ...question.multiple ? {
1731
+ multiple: true
1732
+ } : {},
1733
+ ...question.allowFreeText ? {
1734
+ allowFreeText: true
1735
+ } : {}
1736
+ }, selected) : null;
1637
1737
  return {
1638
1738
  id: question.id,
1639
1739
  prompt: question.prompt,
1740
+ description: question.description,
1741
+ input: question.input,
1640
1742
  multiple: question.multiple,
1641
1743
  position: index + 1,
1642
1744
  selected,
1745
+ value: selected[0] ?? "",
1746
+ error,
1747
+ setValue: /* @__PURE__ */ __name((raw) => {
1748
+ if (isPending) {
1749
+ pick(coerceAnswer(question, raw));
1750
+ }
1751
+ }, "setValue"),
1643
1752
  isPristine: isPending ? picked === void 0 : outcome?.defaulted.includes(question.id) ?? true,
1644
1753
  options: question.options.map((option) => ({
1645
1754
  value: option.value,
@@ -1674,6 +1783,7 @@ function buildElicitationBlock(key, part, request, options) {
1674
1783
  questions,
1675
1784
  questionCount: questions.length,
1676
1785
  isPending,
1786
+ isValid: questions.every((question) => question.error === null),
1677
1787
  outcome,
1678
1788
  error: options.errorOf(toolCallId),
1679
1789
  answer: {
@@ -1691,25 +1801,90 @@ function buildElicitationBlock(key, part, request, options) {
1691
1801
  __name(buildElicitationBlock, "buildElicitationBlock");
1692
1802
  var UI_PART = "data-ui";
1693
1803
  var APPROVAL_PART = "data-approval-requested";
1804
+ var SETTLED_PART = "data-approval-settled";
1805
+ var SETTLED_STATUSES = [
1806
+ "approved",
1807
+ "rejected",
1808
+ "expired"
1809
+ ];
1694
1810
  function readApprovals(parts) {
1695
1811
  const out = /* @__PURE__ */ new Map();
1812
+ const entry = /* @__PURE__ */ __name((id) => {
1813
+ let found = out.get(id);
1814
+ if (found === void 0) {
1815
+ found = {
1816
+ approver: null,
1817
+ expiresAt: null,
1818
+ reason: null,
1819
+ status: null,
1820
+ remember: false,
1821
+ decidedBy: null,
1822
+ decidedVia: null,
1823
+ decisionReason: null
1824
+ };
1825
+ out.set(id, found);
1826
+ }
1827
+ return found;
1828
+ }, "entry");
1696
1829
  for (const part of parts) {
1697
- if (part.type !== APPROVAL_PART) {
1830
+ if (part.type !== APPROVAL_PART && part.type !== SETTLED_PART) {
1698
1831
  continue;
1699
1832
  }
1700
1833
  const data = part.data;
1701
- if (!isRecord2(data) || typeof data.id !== "string" || typeof data.approver !== "string") {
1834
+ if (!isRecord2(data) || typeof data.id !== "string") {
1702
1835
  continue;
1703
1836
  }
1704
- out.set(data.id, {
1705
- approver: data.approver,
1706
- expiresAt: typeof data.expiresAt === "string" ? data.expiresAt : null,
1707
- reason: typeof data.reason === "string" ? data.reason : null
1708
- });
1837
+ const approval = entry(data.id);
1838
+ if (part.type === APPROVAL_PART) {
1839
+ if (typeof data.approver !== "string") {
1840
+ continue;
1841
+ }
1842
+ approval.approver = data.approver;
1843
+ approval.expiresAt = typeof data.expiresAt === "string" ? data.expiresAt : null;
1844
+ approval.reason = typeof data.reason === "string" ? data.reason : null;
1845
+ continue;
1846
+ }
1847
+ if (typeof data.status !== "string" || !SETTLED_STATUSES.includes(data.status)) {
1848
+ continue;
1849
+ }
1850
+ approval.status = data.status;
1851
+ if (approval.approver === null && typeof data.approver === "string") {
1852
+ approval.approver = data.approver;
1853
+ }
1854
+ approval.remember = data.remember === true;
1855
+ approval.decidedBy = typeof data.decidedBy === "string" ? data.decidedBy : null;
1856
+ approval.decidedVia = typeof data.decidedVia === "string" ? data.decidedVia : null;
1857
+ approval.decisionReason = typeof data.reason === "string" ? data.reason : null;
1709
1858
  }
1710
1859
  return out;
1711
1860
  }
1712
1861
  __name(readApprovals, "readApprovals");
1862
+ function approvalOf(parts, call) {
1863
+ if (parts === void 0 || parts.approver === null) {
1864
+ return null;
1865
+ }
1866
+ return {
1867
+ approver: parts.approver,
1868
+ expiresAt: parts.expiresAt,
1869
+ reason: parts.reason,
1870
+ status: parts.status ?? statusFromPart(call),
1871
+ remember: parts.remember,
1872
+ decidedBy: parts.decidedBy,
1873
+ decidedVia: parts.decidedVia,
1874
+ decisionReason: parts.decisionReason
1875
+ };
1876
+ }
1877
+ __name(approvalOf, "approvalOf");
1878
+ function statusFromPart(call) {
1879
+ if (call.state === "output-denied") {
1880
+ return "rejected";
1881
+ }
1882
+ if (call.state === "output-available" || call.state === "output-error") {
1883
+ return "approved";
1884
+ }
1885
+ return "pending";
1886
+ }
1887
+ __name(statusFromPart, "statusFromPart");
1713
1888
  function readUiComponent(part) {
1714
1889
  const data = part.data;
1715
1890
  if (!isRecord2(data) || typeof data.component !== "string") {
@@ -2095,10 +2270,10 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2095
2270
  }, [
2096
2271
  settle
2097
2272
  ]);
2098
- const approve = (0, import_react5.useCallback)((toolCallId) => {
2273
+ const approve = (0, import_react5.useCallback)((toolCallId, options2) => {
2099
2274
  const onApprove = latest.current.options.onApprove;
2100
2275
  if (onApprove) {
2101
- settle(toolCallId, "approve", () => onApprove(toolCallId));
2276
+ settle(toolCallId, "approve", () => options2 === void 0 ? onApprove(toolCallId) : onApprove(toolCallId, options2));
2102
2277
  }
2103
2278
  }, [
2104
2279
  settle
@@ -3073,8 +3248,51 @@ function useToolCatalog(options) {
3073
3248
  }
3074
3249
  __name(useToolCatalog, "useToolCatalog");
3075
3250
 
3251
+ // src/approvals/countdown.ts
3252
+ var import_react9 = require("react");
3253
+ function approvalCountdown(expiresAt, now = Date.now()) {
3254
+ const deadline = expiresAt == null ? Number.NaN : Date.parse(expiresAt);
3255
+ if (!Number.isFinite(deadline)) {
3256
+ return {
3257
+ remainingMs: null,
3258
+ isExpired: false
3259
+ };
3260
+ }
3261
+ const remainingMs = Math.max(0, deadline - now);
3262
+ return {
3263
+ remainingMs,
3264
+ isExpired: remainingMs === 0
3265
+ };
3266
+ }
3267
+ __name(approvalCountdown, "approvalCountdown");
3268
+ function useApprovalCountdown(expiresAt, options = {}) {
3269
+ const { intervalMs = 1e3 } = options;
3270
+ const nowRef = (0, import_react9.useRef)(options.now ?? Date.now);
3271
+ nowRef.current = options.now ?? Date.now;
3272
+ const [state, setState] = (0, import_react9.useState)(() => approvalCountdown(expiresAt, nowRef.current()));
3273
+ (0, import_react9.useEffect)(() => {
3274
+ const read = /* @__PURE__ */ __name(() => approvalCountdown(expiresAt, nowRef.current()), "read");
3275
+ const first = read();
3276
+ setState(first);
3277
+ if (first.remainingMs === null || first.isExpired) {
3278
+ return;
3279
+ }
3280
+ const timer = setInterval(() => {
3281
+ const next = read();
3282
+ setState(next);
3283
+ if (next.isExpired) clearInterval(timer);
3284
+ }, intervalMs);
3285
+ return () => clearInterval(timer);
3286
+ }, [
3287
+ expiresAt,
3288
+ intervalMs
3289
+ ]);
3290
+ return state;
3291
+ }
3292
+ __name(useApprovalCountdown, "useApprovalCountdown");
3293
+
3076
3294
  // src/stored-message-to-ui-message.ts
3077
- var import_nestjs_agent_core = require("@dudousxd/nestjs-agent-core");
3295
+ var import_nestjs_agent_core3 = require("@dudousxd/nestjs-agent-core");
3078
3296
  function isRefusal(result) {
3079
3297
  if (result.denied === true) {
3080
3298
  return true;
@@ -3087,7 +3305,7 @@ function refusalReason(result) {
3087
3305
  const { output } = result;
3088
3306
  if (output !== null && typeof output === "object" && "reason" in output) {
3089
3307
  const reason = output.reason;
3090
- return typeof reason === "string" && reason !== import_nestjs_agent_core.DEFAULT_REFUSAL_REASON ? reason : void 0;
3308
+ return typeof reason === "string" && reason !== import_nestjs_agent_core3.DEFAULT_REFUSAL_REASON ? reason : void 0;
3091
3309
  }
3092
3310
  return void 0;
3093
3311
  }
@@ -3132,6 +3350,41 @@ function storedMessageToUiMessage(message) {
3132
3350
  }
3133
3351
  });
3134
3352
  }
3353
+ for (const approval of message.approvals ?? []) {
3354
+ parts.push({
3355
+ type: "data-approval-requested",
3356
+ id: approval.toolCallId,
3357
+ data: {
3358
+ id: approval.toolCallId,
3359
+ approver: approval.approver,
3360
+ ...approval.expiresAt !== void 0 ? {
3361
+ expiresAt: approval.expiresAt
3362
+ } : {}
3363
+ }
3364
+ });
3365
+ if (approval.status !== "pending") {
3366
+ parts.push({
3367
+ type: "data-approval-settled",
3368
+ id: approval.toolCallId,
3369
+ data: {
3370
+ id: approval.toolCallId,
3371
+ status: approval.status,
3372
+ ...approval.decidedBy !== void 0 ? {
3373
+ decidedBy: approval.decidedBy
3374
+ } : {},
3375
+ ...approval.decidedVia !== void 0 ? {
3376
+ decidedVia: approval.decidedVia
3377
+ } : {},
3378
+ ...approval.remember === true ? {
3379
+ remember: true
3380
+ } : {},
3381
+ ...approval.reason !== void 0 ? {
3382
+ reason: approval.reason
3383
+ } : {}
3384
+ }
3385
+ });
3386
+ }
3387
+ }
3135
3388
  for (const call of message.toolCalls ?? []) {
3136
3389
  const result = message.toolResults?.find((candidate) => candidate.id === call.id);
3137
3390
  const toolKind2 = call.kind;
@@ -3233,21 +3486,21 @@ function sumUsage(rows) {
3233
3486
  __name(sumUsage, "sumUsage");
3234
3487
 
3235
3488
  // src/use-agent-chat.ts
3236
- var import_react9 = require("@ai-sdk/react");
3237
- var import_react10 = require("react");
3489
+ var import_react10 = require("@ai-sdk/react");
3490
+ var import_react11 = require("react");
3238
3491
  function useAgentChat(options) {
3239
- const latest = (0, import_react10.useRef)(options);
3492
+ const latest = (0, import_react11.useRef)(options);
3240
3493
  latest.current = options;
3241
- const [runId, setRunId] = (0, import_react10.useState)(options.resumeRunId);
3242
- const runIdRef = (0, import_react10.useRef)(runId);
3494
+ const [runId, setRunId] = (0, import_react11.useState)(options.resumeRunId);
3495
+ const runIdRef = (0, import_react11.useRef)(runId);
3243
3496
  runIdRef.current = runId;
3244
- const settlingRunIdRef = (0, import_react10.useRef)(void 0);
3245
- const autoResumeRunIdRef = (0, import_react10.useRef)(void 0);
3246
- const [autoResume, setAutoResume] = (0, import_react10.useState)(false);
3247
- const [activeRunId, setActiveRunId] = (0, import_react10.useState)(null);
3248
- const regenerateNext = (0, import_react10.useRef)(false);
3249
- const createdThreadId = (0, import_react10.useRef)(void 0);
3250
- const client = (0, import_react10.useMemo)(() => {
3497
+ const settlingRunIdRef = (0, import_react11.useRef)(void 0);
3498
+ const autoResumeRunIdRef = (0, import_react11.useRef)(void 0);
3499
+ const [autoResume, setAutoResume] = (0, import_react11.useState)(false);
3500
+ const [activeRunId, setActiveRunId] = (0, import_react11.useState)(null);
3501
+ const regenerateNext = (0, import_react11.useRef)(false);
3502
+ const createdThreadId = (0, import_react11.useRef)(void 0);
3503
+ const client = (0, import_react11.useMemo)(() => {
3251
3504
  if (options.client) return options.client;
3252
3505
  return new AgentClient({
3253
3506
  ...options.baseUrl !== void 0 ? {
@@ -3262,7 +3515,7 @@ function useAgentChat(options) {
3262
3515
  getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders")
3263
3516
  });
3264
3517
  }, []);
3265
- (0, import_react10.useEffect)(() => {
3518
+ (0, import_react11.useEffect)(() => {
3266
3519
  if (!options.resume || options.threadId === void 0) return;
3267
3520
  let cancelled = false;
3268
3521
  const threadId = options.threadId;
@@ -3282,7 +3535,7 @@ function useAgentChat(options) {
3282
3535
  options.resume,
3283
3536
  options.threadId
3284
3537
  ]);
3285
- const transport = (0, import_react10.useMemo)(() => {
3538
+ const transport = (0, import_react11.useMemo)(() => {
3286
3539
  function onMeta(meta) {
3287
3540
  settlingRunIdRef.current = meta.runId;
3288
3541
  setRunId(meta.runId);
@@ -3331,8 +3584,8 @@ function useAgentChat(options) {
3331
3584
  }, "onAttemptStart")
3332
3585
  });
3333
3586
  }, []);
3334
- const backgroundRef = (0, import_react10.useRef)(() => Promise.resolve());
3335
- const chat = (0, import_react9.useChat)({
3587
+ const backgroundRef = (0, import_react11.useRef)(() => Promise.resolve());
3588
+ const chat = (0, import_react10.useChat)({
3336
3589
  transport,
3337
3590
  resume: options.resumeRunId !== void 0 || autoResume,
3338
3591
  ...options.threadId !== void 0 ? {
@@ -3362,13 +3615,13 @@ function useAgentChat(options) {
3362
3615
  }
3363
3616
  }, "onFinish")
3364
3617
  });
3365
- const chatRef = (0, import_react10.useRef)(chat);
3618
+ const chatRef = (0, import_react11.useRef)(chat);
3366
3619
  chatRef.current = chat;
3367
- const turnInFlight = (0, import_react10.useRef)(false);
3368
- const isTurnInFlight = (0, import_react10.useCallback)(() => turnInFlight.current || transport.isAttemptLive, [
3620
+ const turnInFlight = (0, import_react11.useRef)(false);
3621
+ const isTurnInFlight = (0, import_react11.useCallback)(() => turnInFlight.current || transport.isAttemptLive, [
3369
3622
  transport
3370
3623
  ]);
3371
- const sendMessage = (0, import_react10.useCallback)(async (...args) => {
3624
+ const sendMessage = (0, import_react11.useCallback)(async (...args) => {
3372
3625
  if (isTurnInFlight()) return;
3373
3626
  turnInFlight.current = true;
3374
3627
  try {
@@ -3379,15 +3632,15 @@ function useAgentChat(options) {
3379
3632
  }, [
3380
3633
  isTurnInFlight
3381
3634
  ]);
3382
- const addToolResult = (0, import_react10.useCallback)(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
3635
+ const addToolResult = (0, import_react11.useCallback)(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
3383
3636
  tool,
3384
3637
  toolCallId,
3385
3638
  output
3386
3639
  }), []);
3387
- const [threads, setThreads] = (0, import_react10.useState)([]);
3388
- const [quota, setQuota] = (0, import_react10.useState)(null);
3389
- const [backgroundRuns, setBackgroundRuns] = (0, import_react10.useState)([]);
3390
- const refreshBackground = (0, import_react10.useCallback)(async () => {
3640
+ const [threads, setThreads] = (0, import_react11.useState)([]);
3641
+ const [quota, setQuota] = (0, import_react11.useState)(null);
3642
+ const [backgroundRuns, setBackgroundRuns] = (0, import_react11.useState)([]);
3643
+ const refreshBackground = (0, import_react11.useCallback)(async () => {
3391
3644
  const threadId = latest.current.threadId ?? createdThreadId.current;
3392
3645
  if (threadId === void 0) {
3393
3646
  return;
@@ -3412,7 +3665,7 @@ function useAgentChat(options) {
3412
3665
  ]);
3413
3666
  backgroundRef.current = refreshBackground;
3414
3667
  const isWorking = backgroundRuns.some((run) => run.status === "running");
3415
- (0, import_react10.useEffect)(() => {
3668
+ (0, import_react11.useEffect)(() => {
3416
3669
  if (options.background !== true || options.threadId === void 0) {
3417
3670
  return;
3418
3671
  }
@@ -3422,7 +3675,7 @@ function useAgentChat(options) {
3422
3675
  options.threadId,
3423
3676
  refreshBackground
3424
3677
  ]);
3425
- (0, import_react10.useEffect)(() => {
3678
+ (0, import_react11.useEffect)(() => {
3426
3679
  const every = latest.current.backgroundPollMs ?? 5e3;
3427
3680
  if (latest.current.background !== true || !isWorking || every <= 0) {
3428
3681
  return;
@@ -3433,26 +3686,26 @@ function useAgentChat(options) {
3433
3686
  isWorking,
3434
3687
  refreshBackground
3435
3688
  ]);
3436
- const loadThreads = (0, import_react10.useCallback)(async () => {
3689
+ const loadThreads = (0, import_react11.useCallback)(async () => {
3437
3690
  const list = await client.listThreads();
3438
3691
  setThreads(list);
3439
3692
  return list;
3440
3693
  }, [
3441
3694
  client
3442
3695
  ]);
3443
- const loadThread = (0, import_react10.useCallback)((id) => client.getThread(id), [
3696
+ const loadThread = (0, import_react11.useCallback)((id) => client.getThread(id), [
3444
3697
  client
3445
3698
  ]);
3446
- const deleteThread = (0, import_react10.useCallback)(async (id) => {
3699
+ const deleteThread = (0, import_react11.useCallback)(async (id) => {
3447
3700
  await client.deleteThread(id);
3448
3701
  setThreads((current) => current.filter((thread) => thread.id !== id));
3449
3702
  }, [
3450
3703
  client
3451
3704
  ]);
3452
- const forkThread = (0, import_react10.useCallback)((threadId, messageId) => client.forkFromMessage(threadId, messageId), [
3705
+ const forkThread = (0, import_react11.useCallback)((threadId, messageId) => client.forkFromMessage(threadId, messageId), [
3453
3706
  client
3454
3707
  ]);
3455
- const renameThread = (0, import_react10.useCallback)(async (id, title) => {
3708
+ const renameThread = (0, import_react11.useCallback)(async (id, title) => {
3456
3709
  await client.renameThread(id, title);
3457
3710
  setThreads((current) => current.map((thread) => thread.id === id ? {
3458
3711
  ...thread,
@@ -3461,24 +3714,24 @@ function useAgentChat(options) {
3461
3714
  }, [
3462
3715
  client
3463
3716
  ]);
3464
- const promoteThread = (0, import_react10.useCallback)(async (id) => {
3717
+ const promoteThread = (0, import_react11.useCallback)(async (id) => {
3465
3718
  await client.promoteThread(id);
3466
3719
  await loadThreads();
3467
3720
  }, [
3468
3721
  client,
3469
3722
  loadThreads
3470
3723
  ]);
3471
- const truncateFromMessage = (0, import_react10.useCallback)((threadId, messageId) => client.truncateFromMessage(threadId, messageId).then(() => void 0), [
3724
+ const truncateFromMessage = (0, import_react11.useCallback)((threadId, messageId) => client.truncateFromMessage(threadId, messageId).then(() => void 0), [
3472
3725
  client
3473
3726
  ]);
3474
- const loadQuota = (0, import_react10.useCallback)(async () => {
3727
+ const loadQuota = (0, import_react11.useCallback)(async () => {
3475
3728
  const today = await client.getQuotaToday();
3476
3729
  setQuota(today);
3477
3730
  return today;
3478
3731
  }, [
3479
3732
  client
3480
3733
  ]);
3481
- const cancel = (0, import_react10.useCallback)(async () => {
3734
+ const cancel = (0, import_react11.useCallback)(async () => {
3482
3735
  chatRef.current.stop();
3483
3736
  const activeRunId2 = runIdRef.current;
3484
3737
  if (activeRunId2) {
@@ -3490,24 +3743,33 @@ function useAgentChat(options) {
3490
3743
  }, [
3491
3744
  client
3492
3745
  ]);
3493
- const approve = (0, import_react10.useCallback)(async ({ toolCallId }) => {
3746
+ const approve = (0, import_react11.useCallback)(async ({ toolCallId, remember, via }) => {
3494
3747
  await client.approveToolCall({
3495
- toolCallId
3748
+ toolCallId,
3749
+ ...remember === true ? {
3750
+ remember: true
3751
+ } : {},
3752
+ ...via !== void 0 ? {
3753
+ via
3754
+ } : {}
3496
3755
  });
3497
3756
  }, [
3498
3757
  client
3499
3758
  ]);
3500
- const reject = (0, import_react10.useCallback)(async ({ toolCallId, reason }) => {
3759
+ const reject = (0, import_react11.useCallback)(async ({ toolCallId, reason, via }) => {
3501
3760
  await client.rejectToolCall({
3502
3761
  toolCallId,
3503
3762
  ...reason !== void 0 ? {
3504
3763
  reason
3764
+ } : {},
3765
+ ...via !== void 0 ? {
3766
+ via
3505
3767
  } : {}
3506
3768
  });
3507
3769
  }, [
3508
3770
  client
3509
3771
  ]);
3510
- const answer = (0, import_react10.useCallback)(async ({ toolCallId, answers }) => {
3772
+ const answer = (0, import_react11.useCallback)(async ({ toolCallId, answers }) => {
3511
3773
  await client.answerToolCall({
3512
3774
  toolCallId,
3513
3775
  ...answers !== void 0 ? {
@@ -3517,14 +3779,14 @@ function useAgentChat(options) {
3517
3779
  }, [
3518
3780
  client
3519
3781
  ]);
3520
- const skip = (0, import_react10.useCallback)(async ({ toolCallId }) => {
3782
+ const skip = (0, import_react11.useCallback)(async ({ toolCallId }) => {
3521
3783
  await client.skipToolCall({
3522
3784
  toolCallId
3523
3785
  });
3524
3786
  }, [
3525
3787
  client
3526
3788
  ]);
3527
- const regenerate = (0, import_react10.useCallback)(() => {
3789
+ const regenerate = (0, import_react11.useCallback)(() => {
3528
3790
  if (isTurnInFlight()) return;
3529
3791
  regenerateNext.current = true;
3530
3792
  turnInFlight.current = true;
@@ -3588,8 +3850,10 @@ __name(mergeHeaders, "mergeHeaders");
3588
3850
  MessageList,
3589
3851
  SPEECH_LANGUAGES,
3590
3852
  applyCompletion,
3853
+ approvalCountdown,
3591
3854
  backgroundRunsFromThread,
3592
3855
  buildTranscriptBlocks,
3856
+ coerceAnswer,
3593
3857
  correctedCallIds,
3594
3858
  createSkillsSource,
3595
3859
  describeTimestamp,
@@ -3615,12 +3879,15 @@ __name(mergeHeaders, "mergeHeaders");
3615
3879
  toolCallState,
3616
3880
  toolCatalogFrom,
3617
3881
  useAgentChat,
3882
+ useApprovalCountdown,
3618
3883
  useChatTranscript,
3619
3884
  useComposerAutocomplete,
3620
3885
  useElapsed,
3621
3886
  useSpeechRecognition,
3622
3887
  useStickToBottom,
3623
3888
  useToolCatalog,
3624
- useTranscriptItem
3889
+ useTranscriptItem,
3890
+ validateAnswer,
3891
+ validateAnswerValue
3625
3892
  });
3626
3893
  //# sourceMappingURL=index.cjs.map