@dudousxd/nestjs-agent-react 0.10.0 → 0.11.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/README.md CHANGED
@@ -112,7 +112,7 @@ function Chat() {
112
112
  |---|---|
113
113
  | `items` | The mounted window, oldest first. Each item carries `blocks`, `text`, `usage`, `timestamp`, `isStreaming`, `isLastAssistant`, and its action machines. |
114
114
  | `items[i].blocks` | `{ kind: 'text' \| 'reasoning' \| 'tools' \| 'sources' \| 'elicitation' \| 'ui' }`. A `ui` block is a component the server pushed (`{ id, component, props, version }`) — look `component` up in your own registry. A `tools` block is a run of CONSECUTIVE tool parts — any other part between two calls (including a `step-start` marker) ends the run. A `reasoning` block carries `isOpen`/`toggle`, open while it streams. A `sources` block appears only under `sources: true`, an `elicitation` block only under `onAnswer` — see below. |
115
- | `items[i].blocks[n]` (`tools`) | Also carries `calls`: the same parts, each with `{ toolCallId, name, toolKind, parentId, children, approval, isAwaitingApproval, approve, reject, error }`, and `roots`: the same calls as a tree (a call nested under another by the stream's `parentId` sits in its parent's `children`). `approval` is `{ approver, expiresAt, reason }` when the runner said who has to decide, else `null`. |
115
+ | `items[i].blocks[n]` (`tools`) | Also carries `calls`: the same parts, each with `{ toolCallId, name, toolKind, parentId, children, approval, isAwaitingApproval, approve, reject, error }`, and `roots`: the same calls as a tree (a call nested under another by the stream's `parentId` sits in its parent's `children`). `approval` is `{ approver, expiresAt, reason, status, remember, decidedBy, decidedVia, decisionReason }` when the runner said who has to decide, else `null` — `status` is `pending` / `approved` / `rejected` / `expired`. |
116
116
  | `items[i].copy` | `{ available, copied, copy() }` — `copied` flashes for `copyResetMs` (default 1500). |
117
117
  | `items[i].edit` | `{ available, isEditing, draft, canSave, start(), cancel(), setDraft(), save(), getTextareaProps() }`. The prop-getter focuses with the caret at the end, saves on Enter, cancels on Escape. |
118
118
  | `items[i].fork` / `.regenerate` | `{ available, run() }`. Regenerate is offered on the last assistant message only. |
@@ -223,7 +223,20 @@ An `action` tool's input lands and its output never follows on its own — the l
223
223
  between the two — so a call stuck at `input-available` IS the pending approval. A question set parks
224
224
  the same way and is deliberately excluded: approving one settles nothing.
225
225
 
226
- A refused settlement (403 "not your thread", a network failure) lands on `block.error` /
226
+ `call.approve.run({ remember: true })` approves this tool for the rest of the thread (the server
227
+ stops asking); `call.approval` says who has to decide and, once settled, who did and through what.
228
+ For a request with an expiry, `useApprovalCountdown(call.approval?.expiresAt)` ticks the time left
229
+ (`{ remainingMs, isExpired }`, headless — pair it with `formatElapsed`):
230
+
231
+ ```tsx
232
+ function ApprovalDeadline({ expiresAt }: { expiresAt: string | null }) {
233
+ const { remainingMs, isExpired } = useApprovalCountdown(expiresAt);
234
+ if (remainingMs === null) return null;
235
+ return <span>{isExpired ? 'Expired' : `${formatElapsed(remainingMs)} left`}</span>;
236
+ }
237
+ ```
238
+
239
+ A refused settlement (403 "not your thread" or "not your approval", 410 "expired", a network failure) lands on `block.error` /
227
240
  `call.error` with the affordance still live, rather than escaping as an unhandled rejection. Render
228
241
  it — a button that silently does nothing is indistinguishable from a broken one.
229
242
 
@@ -420,6 +433,7 @@ data parts, which `useChat`'s `onData` (and `useAgentChat({ onData })`) sees as
420
433
  |---|---|
421
434
  | `ui` | a `data-ui` part keyed by the component id (a repeat id updates it in place) |
422
435
  | `approval-requested` | a `data-approval-requested` part keyed by the call id, plus the SDK's native approval request — the tool part moves to `state: 'approval-requested'` |
436
+ | `approval-settled` | a `data-approval-settled` part keyed by the call id — who decided, through what, remembered or not; folded into `call.approval` |
423
437
  | `title` / `cancelled` | transient `data-title` / `data-cancelled` (never stored on the message); `useAgentChat({ onTitle })` |
424
438
  | a kind this version does not know | a `data-<kind>` part — forwarded, never dropped |
425
439
 
package/dist/index.cjs CHANGED
@@ -41,6 +41,7 @@ __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,
46
47
  correctedCallIds: () => correctedCallIds,
@@ -68,6 +69,7 @@ __export(src_exports, {
68
69
  toolCallState: () => toolCallState,
69
70
  toolCatalogFrom: () => toolCatalogFrom,
70
71
  useAgentChat: () => useAgentChat,
72
+ useApprovalCountdown: () => useApprovalCountdown,
71
73
  useChatTranscript: () => useChatTranscript,
72
74
  useComposerAutocomplete: () => useComposerAutocomplete,
73
75
  useElapsed: () => useElapsed,
@@ -521,6 +523,10 @@ var AgentChatTransport = class {
521
523
  });
522
524
  }
523
525
  break;
526
+ case "approval-settled":
527
+ ensureStep();
528
+ forwardAsData(event, false);
529
+ break;
524
530
  case "ui":
525
531
  ensureStep();
526
532
  closeRuns();
@@ -820,6 +826,10 @@ var AgentClient = class {
820
826
  cancelStream(runId) {
821
827
  return this.request("POST", `/agent/chat/${encodeURIComponent(runId)}/cancel`);
822
828
  }
829
+ /**
830
+ * `remember` approves later calls of the same tool in the same thread; `via` names the surface
831
+ * the decision came through (the server records `'web'` when omitted).
832
+ */
823
833
  approveToolCall(input) {
824
834
  return this.request("POST", "/agent/tool-call/approve", input);
825
835
  }
@@ -1337,7 +1347,7 @@ function buildTranscriptBlocks(message, options) {
1337
1347
  if (toolBuffer.length === 0) {
1338
1348
  return;
1339
1349
  }
1340
- const calls = toolBuffer.map((part) => buildToolCall(part, options.approval, approvals.get(part.toolCallId) ?? null, options.toolCatalog));
1350
+ const calls = toolBuffer.map((part) => buildToolCall(part, options.approval, approvalOf(approvals.get(part.toolCallId), part), options.toolCatalog));
1341
1351
  const roots = nestToolCalls(calls);
1342
1352
  blocks.push({
1343
1353
  kind: "tools",
@@ -1412,7 +1422,7 @@ function buildTranscriptBlocks(message, options) {
1412
1422
  fileBuffer.push(part);
1413
1423
  continue;
1414
1424
  }
1415
- if (part.type === APPROVAL_PART) {
1425
+ if (part.type === APPROVAL_PART || part.type === SETTLED_PART) {
1416
1426
  continue;
1417
1427
  }
1418
1428
  flushAll();
@@ -1563,7 +1573,11 @@ function buildToolCall(part, options, approval, catalog) {
1563
1573
  approve: {
1564
1574
  available: awaiting && options?.canApprove === true,
1565
1575
  isSubmitting: sending === "approve",
1566
- run: /* @__PURE__ */ __name(() => options?.approve(toolCallId), "run")
1576
+ // Read by field, not passed through: `onClick={call.approve.run}` hands this a click event,
1577
+ // and a host's handler must not receive that as its options.
1578
+ run: /* @__PURE__ */ __name((approveOptions) => approveOptions?.remember === true ? options?.approve(toolCallId, {
1579
+ remember: true
1580
+ }) : options?.approve(toolCallId), "run")
1567
1581
  },
1568
1582
  reject: {
1569
1583
  available: awaiting && options?.canReject === true,
@@ -1691,25 +1705,90 @@ function buildElicitationBlock(key, part, request, options) {
1691
1705
  __name(buildElicitationBlock, "buildElicitationBlock");
1692
1706
  var UI_PART = "data-ui";
1693
1707
  var APPROVAL_PART = "data-approval-requested";
1708
+ var SETTLED_PART = "data-approval-settled";
1709
+ var SETTLED_STATUSES = [
1710
+ "approved",
1711
+ "rejected",
1712
+ "expired"
1713
+ ];
1694
1714
  function readApprovals(parts) {
1695
1715
  const out = /* @__PURE__ */ new Map();
1716
+ const entry = /* @__PURE__ */ __name((id) => {
1717
+ let found = out.get(id);
1718
+ if (found === void 0) {
1719
+ found = {
1720
+ approver: null,
1721
+ expiresAt: null,
1722
+ reason: null,
1723
+ status: null,
1724
+ remember: false,
1725
+ decidedBy: null,
1726
+ decidedVia: null,
1727
+ decisionReason: null
1728
+ };
1729
+ out.set(id, found);
1730
+ }
1731
+ return found;
1732
+ }, "entry");
1696
1733
  for (const part of parts) {
1697
- if (part.type !== APPROVAL_PART) {
1734
+ if (part.type !== APPROVAL_PART && part.type !== SETTLED_PART) {
1698
1735
  continue;
1699
1736
  }
1700
1737
  const data = part.data;
1701
- if (!isRecord2(data) || typeof data.id !== "string" || typeof data.approver !== "string") {
1738
+ if (!isRecord2(data) || typeof data.id !== "string") {
1702
1739
  continue;
1703
1740
  }
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
- });
1741
+ const approval = entry(data.id);
1742
+ if (part.type === APPROVAL_PART) {
1743
+ if (typeof data.approver !== "string") {
1744
+ continue;
1745
+ }
1746
+ approval.approver = data.approver;
1747
+ approval.expiresAt = typeof data.expiresAt === "string" ? data.expiresAt : null;
1748
+ approval.reason = typeof data.reason === "string" ? data.reason : null;
1749
+ continue;
1750
+ }
1751
+ if (typeof data.status !== "string" || !SETTLED_STATUSES.includes(data.status)) {
1752
+ continue;
1753
+ }
1754
+ approval.status = data.status;
1755
+ if (approval.approver === null && typeof data.approver === "string") {
1756
+ approval.approver = data.approver;
1757
+ }
1758
+ approval.remember = data.remember === true;
1759
+ approval.decidedBy = typeof data.decidedBy === "string" ? data.decidedBy : null;
1760
+ approval.decidedVia = typeof data.decidedVia === "string" ? data.decidedVia : null;
1761
+ approval.decisionReason = typeof data.reason === "string" ? data.reason : null;
1709
1762
  }
1710
1763
  return out;
1711
1764
  }
1712
1765
  __name(readApprovals, "readApprovals");
1766
+ function approvalOf(parts, call) {
1767
+ if (parts === void 0 || parts.approver === null) {
1768
+ return null;
1769
+ }
1770
+ return {
1771
+ approver: parts.approver,
1772
+ expiresAt: parts.expiresAt,
1773
+ reason: parts.reason,
1774
+ status: parts.status ?? statusFromPart(call),
1775
+ remember: parts.remember,
1776
+ decidedBy: parts.decidedBy,
1777
+ decidedVia: parts.decidedVia,
1778
+ decisionReason: parts.decisionReason
1779
+ };
1780
+ }
1781
+ __name(approvalOf, "approvalOf");
1782
+ function statusFromPart(call) {
1783
+ if (call.state === "output-denied") {
1784
+ return "rejected";
1785
+ }
1786
+ if (call.state === "output-available" || call.state === "output-error") {
1787
+ return "approved";
1788
+ }
1789
+ return "pending";
1790
+ }
1791
+ __name(statusFromPart, "statusFromPart");
1713
1792
  function readUiComponent(part) {
1714
1793
  const data = part.data;
1715
1794
  if (!isRecord2(data) || typeof data.component !== "string") {
@@ -2095,10 +2174,10 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
2095
2174
  }, [
2096
2175
  settle
2097
2176
  ]);
2098
- const approve = (0, import_react5.useCallback)((toolCallId) => {
2177
+ const approve = (0, import_react5.useCallback)((toolCallId, options2) => {
2099
2178
  const onApprove = latest.current.options.onApprove;
2100
2179
  if (onApprove) {
2101
- settle(toolCallId, "approve", () => onApprove(toolCallId));
2180
+ settle(toolCallId, "approve", () => options2 === void 0 ? onApprove(toolCallId) : onApprove(toolCallId, options2));
2102
2181
  }
2103
2182
  }, [
2104
2183
  settle
@@ -3073,6 +3152,49 @@ function useToolCatalog(options) {
3073
3152
  }
3074
3153
  __name(useToolCatalog, "useToolCatalog");
3075
3154
 
3155
+ // src/approvals/countdown.ts
3156
+ var import_react9 = require("react");
3157
+ function approvalCountdown(expiresAt, now = Date.now()) {
3158
+ const deadline = expiresAt == null ? Number.NaN : Date.parse(expiresAt);
3159
+ if (!Number.isFinite(deadline)) {
3160
+ return {
3161
+ remainingMs: null,
3162
+ isExpired: false
3163
+ };
3164
+ }
3165
+ const remainingMs = Math.max(0, deadline - now);
3166
+ return {
3167
+ remainingMs,
3168
+ isExpired: remainingMs === 0
3169
+ };
3170
+ }
3171
+ __name(approvalCountdown, "approvalCountdown");
3172
+ function useApprovalCountdown(expiresAt, options = {}) {
3173
+ const { intervalMs = 1e3 } = options;
3174
+ const nowRef = (0, import_react9.useRef)(options.now ?? Date.now);
3175
+ nowRef.current = options.now ?? Date.now;
3176
+ const [state, setState] = (0, import_react9.useState)(() => approvalCountdown(expiresAt, nowRef.current()));
3177
+ (0, import_react9.useEffect)(() => {
3178
+ const read = /* @__PURE__ */ __name(() => approvalCountdown(expiresAt, nowRef.current()), "read");
3179
+ const first = read();
3180
+ setState(first);
3181
+ if (first.remainingMs === null || first.isExpired) {
3182
+ return;
3183
+ }
3184
+ const timer = setInterval(() => {
3185
+ const next = read();
3186
+ setState(next);
3187
+ if (next.isExpired) clearInterval(timer);
3188
+ }, intervalMs);
3189
+ return () => clearInterval(timer);
3190
+ }, [
3191
+ expiresAt,
3192
+ intervalMs
3193
+ ]);
3194
+ return state;
3195
+ }
3196
+ __name(useApprovalCountdown, "useApprovalCountdown");
3197
+
3076
3198
  // src/stored-message-to-ui-message.ts
3077
3199
  var import_nestjs_agent_core = require("@dudousxd/nestjs-agent-core");
3078
3200
  function isRefusal(result) {
@@ -3132,6 +3254,41 @@ function storedMessageToUiMessage(message) {
3132
3254
  }
3133
3255
  });
3134
3256
  }
3257
+ for (const approval of message.approvals ?? []) {
3258
+ parts.push({
3259
+ type: "data-approval-requested",
3260
+ id: approval.toolCallId,
3261
+ data: {
3262
+ id: approval.toolCallId,
3263
+ approver: approval.approver,
3264
+ ...approval.expiresAt !== void 0 ? {
3265
+ expiresAt: approval.expiresAt
3266
+ } : {}
3267
+ }
3268
+ });
3269
+ if (approval.status !== "pending") {
3270
+ parts.push({
3271
+ type: "data-approval-settled",
3272
+ id: approval.toolCallId,
3273
+ data: {
3274
+ id: approval.toolCallId,
3275
+ status: approval.status,
3276
+ ...approval.decidedBy !== void 0 ? {
3277
+ decidedBy: approval.decidedBy
3278
+ } : {},
3279
+ ...approval.decidedVia !== void 0 ? {
3280
+ decidedVia: approval.decidedVia
3281
+ } : {},
3282
+ ...approval.remember === true ? {
3283
+ remember: true
3284
+ } : {},
3285
+ ...approval.reason !== void 0 ? {
3286
+ reason: approval.reason
3287
+ } : {}
3288
+ }
3289
+ });
3290
+ }
3291
+ }
3135
3292
  for (const call of message.toolCalls ?? []) {
3136
3293
  const result = message.toolResults?.find((candidate) => candidate.id === call.id);
3137
3294
  const toolKind2 = call.kind;
@@ -3233,21 +3390,21 @@ function sumUsage(rows) {
3233
3390
  __name(sumUsage, "sumUsage");
3234
3391
 
3235
3392
  // src/use-agent-chat.ts
3236
- var import_react9 = require("@ai-sdk/react");
3237
- var import_react10 = require("react");
3393
+ var import_react10 = require("@ai-sdk/react");
3394
+ var import_react11 = require("react");
3238
3395
  function useAgentChat(options) {
3239
- const latest = (0, import_react10.useRef)(options);
3396
+ const latest = (0, import_react11.useRef)(options);
3240
3397
  latest.current = options;
3241
- const [runId, setRunId] = (0, import_react10.useState)(options.resumeRunId);
3242
- const runIdRef = (0, import_react10.useRef)(runId);
3398
+ const [runId, setRunId] = (0, import_react11.useState)(options.resumeRunId);
3399
+ const runIdRef = (0, import_react11.useRef)(runId);
3243
3400
  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)(() => {
3401
+ const settlingRunIdRef = (0, import_react11.useRef)(void 0);
3402
+ const autoResumeRunIdRef = (0, import_react11.useRef)(void 0);
3403
+ const [autoResume, setAutoResume] = (0, import_react11.useState)(false);
3404
+ const [activeRunId, setActiveRunId] = (0, import_react11.useState)(null);
3405
+ const regenerateNext = (0, import_react11.useRef)(false);
3406
+ const createdThreadId = (0, import_react11.useRef)(void 0);
3407
+ const client = (0, import_react11.useMemo)(() => {
3251
3408
  if (options.client) return options.client;
3252
3409
  return new AgentClient({
3253
3410
  ...options.baseUrl !== void 0 ? {
@@ -3262,7 +3419,7 @@ function useAgentChat(options) {
3262
3419
  getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders")
3263
3420
  });
3264
3421
  }, []);
3265
- (0, import_react10.useEffect)(() => {
3422
+ (0, import_react11.useEffect)(() => {
3266
3423
  if (!options.resume || options.threadId === void 0) return;
3267
3424
  let cancelled = false;
3268
3425
  const threadId = options.threadId;
@@ -3282,7 +3439,7 @@ function useAgentChat(options) {
3282
3439
  options.resume,
3283
3440
  options.threadId
3284
3441
  ]);
3285
- const transport = (0, import_react10.useMemo)(() => {
3442
+ const transport = (0, import_react11.useMemo)(() => {
3286
3443
  function onMeta(meta) {
3287
3444
  settlingRunIdRef.current = meta.runId;
3288
3445
  setRunId(meta.runId);
@@ -3331,8 +3488,8 @@ function useAgentChat(options) {
3331
3488
  }, "onAttemptStart")
3332
3489
  });
3333
3490
  }, []);
3334
- const backgroundRef = (0, import_react10.useRef)(() => Promise.resolve());
3335
- const chat = (0, import_react9.useChat)({
3491
+ const backgroundRef = (0, import_react11.useRef)(() => Promise.resolve());
3492
+ const chat = (0, import_react10.useChat)({
3336
3493
  transport,
3337
3494
  resume: options.resumeRunId !== void 0 || autoResume,
3338
3495
  ...options.threadId !== void 0 ? {
@@ -3362,13 +3519,13 @@ function useAgentChat(options) {
3362
3519
  }
3363
3520
  }, "onFinish")
3364
3521
  });
3365
- const chatRef = (0, import_react10.useRef)(chat);
3522
+ const chatRef = (0, import_react11.useRef)(chat);
3366
3523
  chatRef.current = chat;
3367
- const turnInFlight = (0, import_react10.useRef)(false);
3368
- const isTurnInFlight = (0, import_react10.useCallback)(() => turnInFlight.current || transport.isAttemptLive, [
3524
+ const turnInFlight = (0, import_react11.useRef)(false);
3525
+ const isTurnInFlight = (0, import_react11.useCallback)(() => turnInFlight.current || transport.isAttemptLive, [
3369
3526
  transport
3370
3527
  ]);
3371
- const sendMessage = (0, import_react10.useCallback)(async (...args) => {
3528
+ const sendMessage = (0, import_react11.useCallback)(async (...args) => {
3372
3529
  if (isTurnInFlight()) return;
3373
3530
  turnInFlight.current = true;
3374
3531
  try {
@@ -3379,15 +3536,15 @@ function useAgentChat(options) {
3379
3536
  }, [
3380
3537
  isTurnInFlight
3381
3538
  ]);
3382
- const addToolResult = (0, import_react10.useCallback)(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
3539
+ const addToolResult = (0, import_react11.useCallback)(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
3383
3540
  tool,
3384
3541
  toolCallId,
3385
3542
  output
3386
3543
  }), []);
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 () => {
3544
+ const [threads, setThreads] = (0, import_react11.useState)([]);
3545
+ const [quota, setQuota] = (0, import_react11.useState)(null);
3546
+ const [backgroundRuns, setBackgroundRuns] = (0, import_react11.useState)([]);
3547
+ const refreshBackground = (0, import_react11.useCallback)(async () => {
3391
3548
  const threadId = latest.current.threadId ?? createdThreadId.current;
3392
3549
  if (threadId === void 0) {
3393
3550
  return;
@@ -3412,7 +3569,7 @@ function useAgentChat(options) {
3412
3569
  ]);
3413
3570
  backgroundRef.current = refreshBackground;
3414
3571
  const isWorking = backgroundRuns.some((run) => run.status === "running");
3415
- (0, import_react10.useEffect)(() => {
3572
+ (0, import_react11.useEffect)(() => {
3416
3573
  if (options.background !== true || options.threadId === void 0) {
3417
3574
  return;
3418
3575
  }
@@ -3422,7 +3579,7 @@ function useAgentChat(options) {
3422
3579
  options.threadId,
3423
3580
  refreshBackground
3424
3581
  ]);
3425
- (0, import_react10.useEffect)(() => {
3582
+ (0, import_react11.useEffect)(() => {
3426
3583
  const every = latest.current.backgroundPollMs ?? 5e3;
3427
3584
  if (latest.current.background !== true || !isWorking || every <= 0) {
3428
3585
  return;
@@ -3433,26 +3590,26 @@ function useAgentChat(options) {
3433
3590
  isWorking,
3434
3591
  refreshBackground
3435
3592
  ]);
3436
- const loadThreads = (0, import_react10.useCallback)(async () => {
3593
+ const loadThreads = (0, import_react11.useCallback)(async () => {
3437
3594
  const list = await client.listThreads();
3438
3595
  setThreads(list);
3439
3596
  return list;
3440
3597
  }, [
3441
3598
  client
3442
3599
  ]);
3443
- const loadThread = (0, import_react10.useCallback)((id) => client.getThread(id), [
3600
+ const loadThread = (0, import_react11.useCallback)((id) => client.getThread(id), [
3444
3601
  client
3445
3602
  ]);
3446
- const deleteThread = (0, import_react10.useCallback)(async (id) => {
3603
+ const deleteThread = (0, import_react11.useCallback)(async (id) => {
3447
3604
  await client.deleteThread(id);
3448
3605
  setThreads((current) => current.filter((thread) => thread.id !== id));
3449
3606
  }, [
3450
3607
  client
3451
3608
  ]);
3452
- const forkThread = (0, import_react10.useCallback)((threadId, messageId) => client.forkFromMessage(threadId, messageId), [
3609
+ const forkThread = (0, import_react11.useCallback)((threadId, messageId) => client.forkFromMessage(threadId, messageId), [
3453
3610
  client
3454
3611
  ]);
3455
- const renameThread = (0, import_react10.useCallback)(async (id, title) => {
3612
+ const renameThread = (0, import_react11.useCallback)(async (id, title) => {
3456
3613
  await client.renameThread(id, title);
3457
3614
  setThreads((current) => current.map((thread) => thread.id === id ? {
3458
3615
  ...thread,
@@ -3461,24 +3618,24 @@ function useAgentChat(options) {
3461
3618
  }, [
3462
3619
  client
3463
3620
  ]);
3464
- const promoteThread = (0, import_react10.useCallback)(async (id) => {
3621
+ const promoteThread = (0, import_react11.useCallback)(async (id) => {
3465
3622
  await client.promoteThread(id);
3466
3623
  await loadThreads();
3467
3624
  }, [
3468
3625
  client,
3469
3626
  loadThreads
3470
3627
  ]);
3471
- const truncateFromMessage = (0, import_react10.useCallback)((threadId, messageId) => client.truncateFromMessage(threadId, messageId).then(() => void 0), [
3628
+ const truncateFromMessage = (0, import_react11.useCallback)((threadId, messageId) => client.truncateFromMessage(threadId, messageId).then(() => void 0), [
3472
3629
  client
3473
3630
  ]);
3474
- const loadQuota = (0, import_react10.useCallback)(async () => {
3631
+ const loadQuota = (0, import_react11.useCallback)(async () => {
3475
3632
  const today = await client.getQuotaToday();
3476
3633
  setQuota(today);
3477
3634
  return today;
3478
3635
  }, [
3479
3636
  client
3480
3637
  ]);
3481
- const cancel = (0, import_react10.useCallback)(async () => {
3638
+ const cancel = (0, import_react11.useCallback)(async () => {
3482
3639
  chatRef.current.stop();
3483
3640
  const activeRunId2 = runIdRef.current;
3484
3641
  if (activeRunId2) {
@@ -3490,24 +3647,33 @@ function useAgentChat(options) {
3490
3647
  }, [
3491
3648
  client
3492
3649
  ]);
3493
- const approve = (0, import_react10.useCallback)(async ({ toolCallId }) => {
3650
+ const approve = (0, import_react11.useCallback)(async ({ toolCallId, remember, via }) => {
3494
3651
  await client.approveToolCall({
3495
- toolCallId
3652
+ toolCallId,
3653
+ ...remember === true ? {
3654
+ remember: true
3655
+ } : {},
3656
+ ...via !== void 0 ? {
3657
+ via
3658
+ } : {}
3496
3659
  });
3497
3660
  }, [
3498
3661
  client
3499
3662
  ]);
3500
- const reject = (0, import_react10.useCallback)(async ({ toolCallId, reason }) => {
3663
+ const reject = (0, import_react11.useCallback)(async ({ toolCallId, reason, via }) => {
3501
3664
  await client.rejectToolCall({
3502
3665
  toolCallId,
3503
3666
  ...reason !== void 0 ? {
3504
3667
  reason
3668
+ } : {},
3669
+ ...via !== void 0 ? {
3670
+ via
3505
3671
  } : {}
3506
3672
  });
3507
3673
  }, [
3508
3674
  client
3509
3675
  ]);
3510
- const answer = (0, import_react10.useCallback)(async ({ toolCallId, answers }) => {
3676
+ const answer = (0, import_react11.useCallback)(async ({ toolCallId, answers }) => {
3511
3677
  await client.answerToolCall({
3512
3678
  toolCallId,
3513
3679
  ...answers !== void 0 ? {
@@ -3517,14 +3683,14 @@ function useAgentChat(options) {
3517
3683
  }, [
3518
3684
  client
3519
3685
  ]);
3520
- const skip = (0, import_react10.useCallback)(async ({ toolCallId }) => {
3686
+ const skip = (0, import_react11.useCallback)(async ({ toolCallId }) => {
3521
3687
  await client.skipToolCall({
3522
3688
  toolCallId
3523
3689
  });
3524
3690
  }, [
3525
3691
  client
3526
3692
  ]);
3527
- const regenerate = (0, import_react10.useCallback)(() => {
3693
+ const regenerate = (0, import_react11.useCallback)(() => {
3528
3694
  if (isTurnInFlight()) return;
3529
3695
  regenerateNext.current = true;
3530
3696
  turnInFlight.current = true;
@@ -3588,6 +3754,7 @@ __name(mergeHeaders, "mergeHeaders");
3588
3754
  MessageList,
3589
3755
  SPEECH_LANGUAGES,
3590
3756
  applyCompletion,
3757
+ approvalCountdown,
3591
3758
  backgroundRunsFromThread,
3592
3759
  buildTranscriptBlocks,
3593
3760
  correctedCallIds,
@@ -3615,6 +3782,7 @@ __name(mergeHeaders, "mergeHeaders");
3615
3782
  toolCallState,
3616
3783
  toolCatalogFrom,
3617
3784
  useAgentChat,
3785
+ useApprovalCountdown,
3618
3786
  useChatTranscript,
3619
3787
  useComposerAutocomplete,
3620
3788
  useElapsed,