@dudousxd/nestjs-agent-react 0.8.0 → 0.9.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
@@ -49,20 +49,82 @@ __export(src_exports, {
49
49
  extractMessageText: () => extractMessageText,
50
50
  filterAutocompleteItems: () => filterAutocompleteItems,
51
51
  findActiveTrigger: () => findActiveTrigger,
52
+ formatElapsed: () => formatElapsed,
52
53
  formatRelativeTime: () => formatRelativeTime,
53
54
  loadStoredSpeechLang: () => loadStoredSpeechLang,
54
55
  persistSpeechLang: () => persistSpeechLang,
56
+ readReasoningMs: () => readReasoningMs,
55
57
  storedMessageToUiMessage: () => storedMessageToUiMessage,
56
58
  storedThreadToUiMessages: () => storedThreadToUiMessages,
57
59
  useAgentChat: () => useAgentChat,
58
60
  useChatTranscript: () => useChatTranscript,
59
61
  useComposerAutocomplete: () => useComposerAutocomplete,
62
+ useElapsed: () => useElapsed,
60
63
  useSpeechRecognition: () => useSpeechRecognition,
61
64
  useStickToBottom: () => useStickToBottom,
62
65
  useTranscriptItem: () => useTranscriptItem
63
66
  });
64
67
  module.exports = __toCommonJS(src_exports);
65
68
 
69
+ // src/reasoning/timing.ts
70
+ var import_react = require("react");
71
+ var REASONING_METADATA_KEY = "agent";
72
+ function reasoningDurationMetadata(reasoningMs) {
73
+ return {
74
+ [REASONING_METADATA_KEY]: {
75
+ reasoningMs
76
+ }
77
+ };
78
+ }
79
+ __name(reasoningDurationMetadata, "reasoningDurationMetadata");
80
+ function readReasoningMs(part) {
81
+ const metadata = part.providerMetadata;
82
+ if (typeof metadata !== "object" || metadata === null) return null;
83
+ const scoped = metadata[REASONING_METADATA_KEY];
84
+ if (typeof scoped !== "object" || scoped === null) return null;
85
+ const value = scoped.reasoningMs;
86
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
87
+ }
88
+ __name(readReasoningMs, "readReasoningMs");
89
+ function useElapsed(running, options = {}) {
90
+ const { intervalMs = 1e3 } = options;
91
+ const nowRef = (0, import_react.useRef)(options.now ?? Date.now);
92
+ nowRef.current = options.now ?? Date.now;
93
+ const startedAt = (0, import_react.useRef)(null);
94
+ const [elapsed, setElapsed] = (0, import_react.useState)(0);
95
+ (0, import_react.useEffect)(() => {
96
+ if (!running) {
97
+ if (startedAt.current !== null) {
98
+ setElapsed(nowRef.current() - startedAt.current);
99
+ startedAt.current = null;
100
+ }
101
+ return;
102
+ }
103
+ startedAt.current = nowRef.current();
104
+ setElapsed(0);
105
+ const timer = setInterval(() => {
106
+ if (startedAt.current !== null) setElapsed(nowRef.current() - startedAt.current);
107
+ }, intervalMs);
108
+ return () => clearInterval(timer);
109
+ }, [
110
+ running,
111
+ intervalMs
112
+ ]);
113
+ return elapsed;
114
+ }
115
+ __name(useElapsed, "useElapsed");
116
+ function formatElapsed(ms) {
117
+ if (!Number.isFinite(ms) || ms < 1e3) return "<1s";
118
+ const totalSeconds = Math.floor(ms / 1e3);
119
+ const hours = Math.floor(totalSeconds / 3600);
120
+ const minutes = Math.floor(totalSeconds % 3600 / 60);
121
+ const seconds = totalSeconds % 60;
122
+ if (hours > 0) return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;
123
+ if (minutes > 0) return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
124
+ return `${seconds}s`;
125
+ }
126
+ __name(formatElapsed, "formatElapsed");
127
+
66
128
  // src/agent-chat-transport.ts
67
129
  var HEADER_RUN_ID = "x-agent-run-id";
68
130
  var HEADER_THREAD_ID = "x-agent-thread-id";
@@ -220,6 +282,7 @@ var AgentChatTransport = class {
220
282
  let stepIndex = 0;
221
283
  let textId = null;
222
284
  let reasoningId = null;
285
+ let reasoningStartedAt = 0;
223
286
  let textSeq = 0;
224
287
  let reasoningSeq = 0;
225
288
  const announced = /* @__PURE__ */ new Set();
@@ -256,7 +319,7 @@ var AgentChatTransport = class {
256
319
  if (!stepOpen) openStep();
257
320
  }
258
321
  __name(ensureStep, "ensureStep");
259
- function closeRuns() {
322
+ function closeRuns(reasoningMs) {
260
323
  if (textId !== null) {
261
324
  controller.enqueue({
262
325
  type: "text-end",
@@ -267,15 +330,16 @@ var AgentChatTransport = class {
267
330
  if (reasoningId !== null) {
268
331
  controller.enqueue({
269
332
  type: "reasoning-end",
270
- id: reasoningId
333
+ id: reasoningId,
334
+ providerMetadata: reasoningDurationMetadata(reasoningMs ?? Math.max(0, Date.now() - reasoningStartedAt))
271
335
  });
272
336
  reasoningId = null;
273
337
  }
274
338
  }
275
339
  __name(closeRuns, "closeRuns");
276
- function closeStep() {
340
+ function closeStep(reasoningMs) {
277
341
  if (!stepOpen) return;
278
- closeRuns();
342
+ closeRuns(reasoningMs);
279
343
  controller.enqueue({
280
344
  type: "finish-step"
281
345
  });
@@ -314,7 +378,7 @@ var AgentChatTransport = class {
314
378
  openStep();
315
379
  break;
316
380
  case "step-finish":
317
- closeStep();
381
+ closeStep(event.reasoningMs);
318
382
  if (event.costUsd !== void 0) {
319
383
  controller.enqueue({
320
384
  type: "message-metadata",
@@ -345,6 +409,7 @@ var AgentChatTransport = class {
345
409
  if (reasoningId === null) {
346
410
  reasoningId = reasoningSeq === 0 ? `rsn-${stepIndex}` : `rsn-${stepIndex}.${reasoningSeq}`;
347
411
  reasoningSeq += 1;
412
+ reasoningStartedAt = Date.now();
348
413
  controller.enqueue({
349
414
  type: "reasoning-start",
350
415
  id: reasoningId
@@ -792,10 +857,10 @@ var AgentClient = class {
792
857
  };
793
858
 
794
859
  // src/components/chat-input.tsx
795
- var import_react2 = __toESM(require("react"), 1);
860
+ var import_react3 = __toESM(require("react"), 1);
796
861
 
797
862
  // src/components/use-speech-recognition.ts
798
- var import_react = require("react");
863
+ var import_react2 = require("react");
799
864
  function getRecognitionCtor() {
800
865
  if (typeof window === "undefined") return null;
801
866
  const scope = window;
@@ -803,15 +868,15 @@ function getRecognitionCtor() {
803
868
  }
804
869
  __name(getRecognitionCtor, "getRecognitionCtor");
805
870
  function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTranscript }) {
806
- const recognitionRef = (0, import_react.useRef)(null);
807
- const [isListening, setIsListening] = (0, import_react.useState)(false);
808
- const ctor = (0, import_react.useMemo)(() => getRecognitionCtor(), []);
871
+ const recognitionRef = (0, import_react2.useRef)(null);
872
+ const [isListening, setIsListening] = (0, import_react2.useState)(false);
873
+ const ctor = (0, import_react2.useMemo)(() => getRecognitionCtor(), []);
809
874
  const isSupported = ctor !== null;
810
- const finalRef = (0, import_react.useRef)(onFinalTranscript);
811
- const interimRef = (0, import_react.useRef)(onInterimTranscript);
875
+ const finalRef = (0, import_react2.useRef)(onFinalTranscript);
876
+ const interimRef = (0, import_react2.useRef)(onInterimTranscript);
812
877
  finalRef.current = onFinalTranscript;
813
878
  interimRef.current = onInterimTranscript;
814
- (0, import_react.useEffect)(() => {
879
+ (0, import_react2.useEffect)(() => {
815
880
  if (!ctor) return;
816
881
  const recognition = new ctor();
817
882
  recognition.lang = lang;
@@ -850,7 +915,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
850
915
  ctor,
851
916
  lang
852
917
  ]);
853
- const start = (0, import_react.useCallback)(() => {
918
+ const start = (0, import_react2.useCallback)(() => {
854
919
  const recognition = recognitionRef.current;
855
920
  if (!recognition) return;
856
921
  try {
@@ -860,7 +925,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
860
925
  console.warn("SpeechRecognition start failed", error);
861
926
  }
862
927
  }, []);
863
- const stop = (0, import_react.useCallback)(() => {
928
+ const stop = (0, import_react2.useCallback)(() => {
864
929
  const recognition = recognitionRef.current;
865
930
  if (!recognition) return;
866
931
  try {
@@ -879,8 +944,8 @@ __name(useSpeechRecognition, "useSpeechRecognition");
879
944
 
880
945
  // src/components/chat-input.tsx
881
946
  function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = true, speechLang = "en-US", sendLabel = "Send", onStop, isStreaming = false, stopLabel = "Stop generating", className, textareaClassName, sendButtonClassName, micButtonClassName, stopButtonClassName, renderMic }) {
882
- const [value, setValue] = (0, import_react2.useState)("");
883
- const [interim, setInterim] = (0, import_react2.useState)("");
947
+ const [value, setValue] = (0, import_react3.useState)("");
948
+ const [interim, setInterim] = (0, import_react3.useState)("");
884
949
  const speech = useSpeechRecognition({
885
950
  lang: speechLang,
886
951
  onFinalTranscript: /* @__PURE__ */ __name((text) => {
@@ -891,7 +956,7 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
891
956
  }, "onFinalTranscript"),
892
957
  onInterimTranscript: /* @__PURE__ */ __name((text) => setInterim(text), "onInterimTranscript")
893
958
  });
894
- (0, import_react2.useEffect)(() => {
959
+ (0, import_react3.useEffect)(() => {
895
960
  if (!speech.isListening) setInterim("");
896
961
  }, [
897
962
  speech.isListening
@@ -919,9 +984,9 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
919
984
  const displayValue = speech.isListening && interim ? `${value}${value && !value.endsWith(" ") ? " " : ""}${interim}` : value;
920
985
  const showMic = enableSpeech && speech.isSupported;
921
986
  const showStop = isStreaming && onStop !== void 0;
922
- return /* @__PURE__ */ import_react2.default.createElement("div", {
987
+ return /* @__PURE__ */ import_react3.default.createElement("div", {
923
988
  className
924
- }, /* @__PURE__ */ import_react2.default.createElement("textarea", {
989
+ }, /* @__PURE__ */ import_react3.default.createElement("textarea", {
925
990
  className: textareaClassName,
926
991
  rows,
927
992
  value: displayValue,
@@ -932,17 +997,17 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
932
997
  }, "onChange"),
933
998
  onKeyDown: handleKeyDown,
934
999
  placeholder: placeholder ?? "Ask something (Enter to send, Shift+Enter for newline)"
935
- }), /* @__PURE__ */ import_react2.default.createElement("button", {
1000
+ }), /* @__PURE__ */ import_react3.default.createElement("button", {
936
1001
  type: "button",
937
1002
  className: sendButtonClassName,
938
1003
  onClick: submit,
939
1004
  disabled: disabled || !value.trim()
940
- }, sendLabel), showStop ? /* @__PURE__ */ import_react2.default.createElement("button", {
1005
+ }, sendLabel), showStop ? /* @__PURE__ */ import_react3.default.createElement("button", {
941
1006
  type: "button",
942
1007
  className: stopButtonClassName,
943
1008
  onClick: /* @__PURE__ */ __name(() => void onStop(), "onClick"),
944
1009
  "aria-label": "Stop generating"
945
- }, stopLabel) : null, showMic ? /* @__PURE__ */ import_react2.default.createElement("button", {
1010
+ }, stopLabel) : null, showMic ? /* @__PURE__ */ import_react3.default.createElement("button", {
946
1011
  type: "button",
947
1012
  className: micButtonClassName,
948
1013
  onClick: toggleMic,
@@ -955,10 +1020,10 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
955
1020
  __name(ChatInput, "ChatInput");
956
1021
 
957
1022
  // src/components/message-item.tsx
958
- var import_react5 = __toESM(require("react"), 1);
1023
+ var import_react6 = __toESM(require("react"), 1);
959
1024
 
960
1025
  // src/transcript/use-chat-transcript.ts
961
- var import_react4 = require("react");
1026
+ var import_react5 = require("react");
962
1027
 
963
1028
  // src/transcript/model.ts
964
1029
  var import_ai = require("ai");
@@ -1079,6 +1144,7 @@ function buildTranscriptBlocks(message, options) {
1079
1144
  key,
1080
1145
  text: part.text,
1081
1146
  isStreaming,
1147
+ durationMs: readReasoningMs(part),
1082
1148
  isOpen: options.isReasoningOpen(key, isStreaming),
1083
1149
  toggle: /* @__PURE__ */ __name((open) => options.toggleReasoning(key, open), "toggle")
1084
1150
  });
@@ -1491,11 +1557,11 @@ function formatCostUsd(cost) {
1491
1557
  __name(formatCostUsd, "formatCostUsd");
1492
1558
 
1493
1559
  // src/transcript/use-stick-to-bottom.ts
1494
- var import_react3 = require("react");
1560
+ var import_react4 = require("react");
1495
1561
  function useStickToBottom({ contentKey, threshold = 32 }) {
1496
- const containerRef = (0, import_react3.useRef)(null);
1497
- const [isAtBottom, setIsAtBottom] = (0, import_react3.useState)(true);
1498
- const measure = (0, import_react3.useCallback)(() => {
1562
+ const containerRef = (0, import_react4.useRef)(null);
1563
+ const [isAtBottom, setIsAtBottom] = (0, import_react4.useState)(true);
1564
+ const measure = (0, import_react4.useCallback)(() => {
1499
1565
  const element = containerRef.current;
1500
1566
  if (!element) {
1501
1567
  return;
@@ -1505,7 +1571,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
1505
1571
  }, [
1506
1572
  threshold
1507
1573
  ]);
1508
- const scrollToBottom = (0, import_react3.useCallback)(() => {
1574
+ const scrollToBottom = (0, import_react4.useCallback)(() => {
1509
1575
  const element = containerRef.current;
1510
1576
  if (!element) {
1511
1577
  return;
@@ -1513,7 +1579,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
1513
1579
  element.scrollTop = element.scrollHeight;
1514
1580
  setIsAtBottom(true);
1515
1581
  }, []);
1516
- const setContainer = (0, import_react3.useCallback)((element) => {
1582
+ const setContainer = (0, import_react4.useCallback)((element) => {
1517
1583
  containerRef.current = element;
1518
1584
  if (element) {
1519
1585
  measure();
@@ -1521,7 +1587,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
1521
1587
  }, [
1522
1588
  measure
1523
1589
  ]);
1524
- (0, import_react3.useEffect)(() => {
1590
+ (0, import_react4.useEffect)(() => {
1525
1591
  if (isAtBottom) {
1526
1592
  scrollToBottom();
1527
1593
  }
@@ -1530,7 +1596,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
1530
1596
  isAtBottom,
1531
1597
  scrollToBottom
1532
1598
  ]);
1533
- const getContainerProps = (0, import_react3.useCallback)(() => ({
1599
+ const getContainerProps = (0, import_react4.useCallback)(() => ({
1534
1600
  ref: setContainer,
1535
1601
  onScroll: measure
1536
1602
  }), [
@@ -1552,14 +1618,14 @@ var DEFAULT_LOAD_EARLIER_STEP = 50;
1552
1618
  var DEFAULT_COPY_RESET_MS = 1500;
1553
1619
  function useChatTranscript(options) {
1554
1620
  const { messages, status, initialVisibleCount = DEFAULT_VISIBLE_COUNT, loadEarlierStep = DEFAULT_LOAD_EARLIER_STEP, followUps, stickToBottomThreshold } = options;
1555
- const latest = (0, import_react4.useRef)(options);
1621
+ const latest = (0, import_react5.useRef)(options);
1556
1622
  latest.current = options;
1557
- const [visibleCount, setVisibleCount] = (0, import_react4.useState)(initialVisibleCount);
1558
- const [stopRequested, setStopRequested] = (0, import_react4.useState)(false);
1623
+ const [visibleCount, setVisibleCount] = (0, import_react5.useState)(initialVisibleCount);
1624
+ const [stopRequested, setStopRequested] = (0, import_react5.useState)(false);
1559
1625
  const isStreaming = status === "streaming";
1560
1626
  const isBusy = status === "submitted" || isStreaming;
1561
1627
  const isEmpty = messages.length === 0;
1562
- (0, import_react4.useEffect)(() => {
1628
+ (0, import_react5.useEffect)(() => {
1563
1629
  if (!isBusy) {
1564
1630
  setStopRequested(false);
1565
1631
  }
@@ -1576,7 +1642,7 @@ function useChatTranscript(options) {
1576
1642
  streamingMessageId,
1577
1643
  options
1578
1644
  });
1579
- const stop = (0, import_react4.useCallback)(() => {
1645
+ const stop = (0, import_react5.useCallback)(() => {
1580
1646
  const current = latest.current;
1581
1647
  if (current.status !== "submitted" && current.status !== "streaming") {
1582
1648
  return;
@@ -1584,8 +1650,8 @@ function useChatTranscript(options) {
1584
1650
  setStopRequested(true);
1585
1651
  void current.onStop?.();
1586
1652
  }, []);
1587
- const loadEarlier = (0, import_react4.useCallback)(() => setVisibleCount((count) => count + (latest.current.loadEarlierStep ?? DEFAULT_LOAD_EARLIER_STEP)), []);
1588
- const showAll = (0, import_react4.useCallback)(() => setVisibleCount(latest.current.messages.length), []);
1653
+ const loadEarlier = (0, import_react5.useCallback)(() => setVisibleCount((count) => count + (latest.current.loadEarlierStep ?? DEFAULT_LOAD_EARLIER_STEP)), []);
1654
+ const showAll = (0, import_react5.useCallback)(() => setVisibleCount(latest.current.messages.length), []);
1589
1655
  const scroll = useStickToBottom({
1590
1656
  contentKey: contentSignature(messages),
1591
1657
  ...stickToBottomThreshold !== void 0 ? {
@@ -1621,7 +1687,7 @@ function useChatTranscript(options) {
1621
1687
  __name(useChatTranscript, "useChatTranscript");
1622
1688
  function useTranscriptItem(options) {
1623
1689
  const { message, isStreaming = false } = options;
1624
- const messagesRef = (0, import_react4.useRef)([
1690
+ const messagesRef = (0, import_react5.useRef)([
1625
1691
  message
1626
1692
  ]);
1627
1693
  if (messagesRef.current[0] !== message) {
@@ -1639,13 +1705,13 @@ function useTranscriptItem(options) {
1639
1705
  }
1640
1706
  __name(useTranscriptItem, "useTranscriptItem");
1641
1707
  function useTranscriptItems({ messages, visibleStart, streamingMessageId, options }) {
1642
- const [drafts, setDrafts] = (0, import_react4.useState)(() => /* @__PURE__ */ new Map());
1643
- const [copied, setCopied] = (0, import_react4.useState)(() => /* @__PURE__ */ new Set());
1644
- const [openReasoning, setOpenReasoning] = (0, import_react4.useState)(() => /* @__PURE__ */ new Map());
1645
- const [picks, setPicks] = (0, import_react4.useState)(() => /* @__PURE__ */ new Map());
1646
- const [settling, setSettling] = (0, import_react4.useState)(() => /* @__PURE__ */ new Map());
1647
- const [settleErrors, setSettleErrors] = (0, import_react4.useState)(() => /* @__PURE__ */ new Map());
1648
- const latest = (0, import_react4.useRef)({
1708
+ const [drafts, setDrafts] = (0, import_react5.useState)(() => /* @__PURE__ */ new Map());
1709
+ const [copied, setCopied] = (0, import_react5.useState)(() => /* @__PURE__ */ new Set());
1710
+ const [openReasoning, setOpenReasoning] = (0, import_react5.useState)(() => /* @__PURE__ */ new Map());
1711
+ const [picks, setPicks] = (0, import_react5.useState)(() => /* @__PURE__ */ new Map());
1712
+ const [settling, setSettling] = (0, import_react5.useState)(() => /* @__PURE__ */ new Map());
1713
+ const [settleErrors, setSettleErrors] = (0, import_react5.useState)(() => /* @__PURE__ */ new Map());
1714
+ const latest = (0, import_react5.useRef)({
1649
1715
  options,
1650
1716
  drafts,
1651
1717
  picks,
@@ -1657,23 +1723,23 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
1657
1723
  picks,
1658
1724
  messages
1659
1725
  };
1660
- const copyTimers = (0, import_react4.useRef)(/* @__PURE__ */ new Map());
1661
- (0, import_react4.useEffect)(() => () => {
1726
+ const copyTimers = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
1727
+ (0, import_react5.useEffect)(() => () => {
1662
1728
  for (const timer of copyTimers.current.values()) {
1663
1729
  clearTimeout(timer);
1664
1730
  }
1665
1731
  copyTimers.current.clear();
1666
1732
  }, []);
1667
- const callbackCache = (0, import_react4.useRef)(/* @__PURE__ */ new Map());
1668
- const reasoningToggleCache = (0, import_react4.useRef)(/* @__PURE__ */ new Map());
1669
- const toggleReasoning = (0, import_react4.useCallback)((key, open) => {
1733
+ const callbackCache = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
1734
+ const reasoningToggleCache = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
1735
+ const toggleReasoning = (0, import_react5.useCallback)((key, open) => {
1670
1736
  setOpenReasoning((current) => {
1671
1737
  const next = new Map(current);
1672
1738
  next.set(key, open ?? !(current.get(key) ?? false));
1673
1739
  return next;
1674
1740
  });
1675
1741
  }, []);
1676
- const stableToggle = (0, import_react4.useCallback)((key) => {
1742
+ const stableToggle = (0, import_react5.useCallback)((key) => {
1677
1743
  let cached = reasoningToggleCache.current.get(key);
1678
1744
  if (!cached) {
1679
1745
  cached = /* @__PURE__ */ __name((open) => toggleReasoning(key, open), "cached");
@@ -1683,10 +1749,10 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
1683
1749
  }, [
1684
1750
  toggleReasoning
1685
1751
  ]);
1686
- const pick = (0, import_react4.useCallback)((toolCallId, questionId, values) => {
1752
+ const pick = (0, import_react5.useCallback)((toolCallId, questionId, values) => {
1687
1753
  setPicks((current) => new Map(current).set(pickKey(toolCallId, questionId), values));
1688
1754
  }, []);
1689
- const settle = (0, import_react4.useCallback)((toolCallId, action, send) => {
1755
+ const settle = (0, import_react5.useCallback)((toolCallId, action, send) => {
1690
1756
  setSettleErrors((current) => {
1691
1757
  if (!current.has(toolCallId)) {
1692
1758
  return current;
@@ -1710,7 +1776,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
1710
1776
  failed(error);
1711
1777
  }
1712
1778
  }, []);
1713
- const answer = (0, import_react4.useCallback)((toolCallId) => {
1779
+ const answer = (0, import_react5.useCallback)((toolCallId) => {
1714
1780
  const current = latest.current;
1715
1781
  const onAnswer = current.options.onAnswer;
1716
1782
  if (!onAnswer) {
@@ -1720,7 +1786,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
1720
1786
  }, [
1721
1787
  settle
1722
1788
  ]);
1723
- const skip = (0, import_react4.useCallback)((toolCallId) => {
1789
+ const skip = (0, import_react5.useCallback)((toolCallId) => {
1724
1790
  const onSkip = latest.current.options.onSkip;
1725
1791
  if (onSkip) {
1726
1792
  settle(toolCallId, "skip", () => onSkip(toolCallId));
@@ -1728,7 +1794,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
1728
1794
  }, [
1729
1795
  settle
1730
1796
  ]);
1731
- const approve = (0, import_react4.useCallback)((toolCallId) => {
1797
+ const approve = (0, import_react5.useCallback)((toolCallId) => {
1732
1798
  const onApprove = latest.current.options.onApprove;
1733
1799
  if (onApprove) {
1734
1800
  settle(toolCallId, "approve", () => onApprove(toolCallId));
@@ -1736,7 +1802,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
1736
1802
  }, [
1737
1803
  settle
1738
1804
  ]);
1739
- const reject = (0, import_react4.useCallback)((toolCallId) => {
1805
+ const reject = (0, import_react5.useCallback)((toolCallId) => {
1740
1806
  const onReject = latest.current.options.onReject;
1741
1807
  if (onReject) {
1742
1808
  settle(toolCallId, "reject", () => onReject(toolCallId));
@@ -1744,7 +1810,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
1744
1810
  }, [
1745
1811
  settle
1746
1812
  ]);
1747
- const callbacksFor = (0, import_react4.useCallback)((id) => {
1813
+ const callbacksFor = (0, import_react5.useCallback)((id) => {
1748
1814
  const cached = callbackCache.current.get(id);
1749
1815
  if (cached) {
1750
1816
  return cached;
@@ -1983,7 +2049,7 @@ function MessageItem({ message, isStreaming = false, editable, onEditSubmit, reg
1983
2049
  getCreatedAt: /* @__PURE__ */ __name(() => createdAt, "getCreatedAt")
1984
2050
  } : {}
1985
2051
  });
1986
- return /* @__PURE__ */ import_react5.default.createElement(MessageItemView, {
2052
+ return /* @__PURE__ */ import_react6.default.createElement(MessageItemView, {
1987
2053
  item,
1988
2054
  ...slots
1989
2055
  });
@@ -1991,51 +2057,51 @@ function MessageItem({ message, isStreaming = false, editable, onEditSubmit, reg
1991
2057
  __name(MessageItem, "MessageItem");
1992
2058
  function MessageItemView({ item, ...slots }) {
1993
2059
  const { classNames } = slots;
1994
- const actionRow = /* @__PURE__ */ import_react5.default.createElement("div", {
2060
+ const actionRow = /* @__PURE__ */ import_react6.default.createElement("div", {
1995
2061
  className: classNames?.actions,
1996
2062
  "data-actions-for": item.role
1997
- }, item.copy.available ? /* @__PURE__ */ import_react5.default.createElement("button", {
2063
+ }, item.copy.available ? /* @__PURE__ */ import_react6.default.createElement("button", {
1998
2064
  type: "button",
1999
2065
  onClick: item.copy.copy,
2000
2066
  className: classNames?.actionButton,
2001
2067
  title: item.copy.copied ? "Copied!" : "Copy message text"
2002
- }, item.copy.copied ? "Copied" : "Copy") : null, item.fork.available ? /* @__PURE__ */ import_react5.default.createElement("button", {
2068
+ }, item.copy.copied ? "Copied" : "Copy") : null, item.fork.available ? /* @__PURE__ */ import_react6.default.createElement("button", {
2003
2069
  type: "button",
2004
2070
  onClick: item.fork.run,
2005
2071
  className: classNames?.actionButton,
2006
2072
  title: "Fork thread from this message"
2007
- }, "Fork") : null, item.regenerate.available ? /* @__PURE__ */ import_react5.default.createElement("button", {
2073
+ }, "Fork") : null, item.regenerate.available ? /* @__PURE__ */ import_react6.default.createElement("button", {
2008
2074
  type: "button",
2009
2075
  onClick: item.regenerate.run,
2010
2076
  className: classNames?.actionButton,
2011
2077
  title: "Regenerate response"
2012
- }, "Regenerate") : null, slots.meta, !item.isUser && item.usage ? /* @__PURE__ */ import_react5.default.createElement("span", {
2078
+ }, "Regenerate") : null, slots.meta, !item.isUser && item.usage ? /* @__PURE__ */ import_react6.default.createElement("span", {
2013
2079
  className: classNames?.usage,
2014
2080
  title: `Input ${item.usage.inputTokens.toLocaleString()} \xB7 Output ${item.usage.outputTokens.toLocaleString()} tokens`
2015
- }, item.usage.costLabel, " \xB7 ", item.usage.tokensLabel) : null, item.timestamp ? /* @__PURE__ */ import_react5.default.createElement("span", {
2081
+ }, item.usage.costLabel, " \xB7 ", item.usage.tokensLabel) : null, item.timestamp ? /* @__PURE__ */ import_react6.default.createElement("span", {
2016
2082
  className: classNames?.timestamp,
2017
2083
  title: item.timestamp.absolute
2018
2084
  }, item.timestamp.relative) : null);
2019
2085
  if (item.edit.available) {
2020
- return /* @__PURE__ */ import_react5.default.createElement(EditableUserBubble, {
2086
+ return /* @__PURE__ */ import_react6.default.createElement(EditableUserBubble, {
2021
2087
  item,
2022
2088
  slots,
2023
2089
  actions: actionRow
2024
2090
  });
2025
2091
  }
2026
2092
  const className = joinClasses(classNames?.root, classNames?.byRole?.[item.role]);
2027
- return /* @__PURE__ */ import_react5.default.createElement("div", {
2093
+ return /* @__PURE__ */ import_react6.default.createElement("div", {
2028
2094
  className,
2029
2095
  "data-role": item.role,
2030
2096
  "data-streaming": item.isStreaming ? "true" : void 0
2031
- }, /* @__PURE__ */ import_react5.default.createElement("div", null, renderBlocks(item, slots)), actionRow);
2097
+ }, /* @__PURE__ */ import_react6.default.createElement("div", null, renderBlocks(item, slots)), actionRow);
2032
2098
  }
2033
2099
  __name(MessageItemView, "MessageItemView");
2034
2100
  function renderBlocks(item, slots) {
2035
2101
  const nodes = [];
2036
2102
  for (const block of item.blocks) {
2037
2103
  if (block.kind === "text") {
2038
- nodes.push(/* @__PURE__ */ import_react5.default.createElement("div", {
2104
+ nodes.push(/* @__PURE__ */ import_react6.default.createElement("div", {
2039
2105
  key: block.key,
2040
2106
  className: slots.classNames?.text
2041
2107
  }, slots.renderText ? slots.renderText(block.text, {
@@ -2056,7 +2122,7 @@ function renderBlocks(item, slots) {
2056
2122
  continue;
2057
2123
  }
2058
2124
  if (block.kind === "ui" && slots.renderUi) {
2059
- nodes.push(/* @__PURE__ */ import_react5.default.createElement("div", {
2125
+ nodes.push(/* @__PURE__ */ import_react6.default.createElement("div", {
2060
2126
  key: block.key,
2061
2127
  "data-slot": "ui",
2062
2128
  "data-component": block.component
@@ -2067,18 +2133,18 @@ function renderBlocks(item, slots) {
2067
2133
  }
2068
2134
  __name(renderBlocks, "renderBlocks");
2069
2135
  function renderFilesBlock(block, slots) {
2070
- return /* @__PURE__ */ import_react5.default.createElement("div", {
2136
+ return /* @__PURE__ */ import_react6.default.createElement("div", {
2071
2137
  key: block.key,
2072
2138
  className: slots.classNames?.files,
2073
2139
  "data-files": "true"
2074
- }, slots.renderFiles ? slots.renderFiles(block.files) : block.files.map((file) => /* @__PURE__ */ import_react5.default.createElement("a", {
2140
+ }, slots.renderFiles ? slots.renderFiles(block.files) : block.files.map((file) => /* @__PURE__ */ import_react6.default.createElement("a", {
2075
2141
  key: file.url,
2076
2142
  href: file.url,
2077
2143
  target: "_blank",
2078
2144
  rel: "noreferrer",
2079
2145
  className: slots.classNames?.file,
2080
2146
  title: file.filename ?? file.mediaType
2081
- }, file.isImage ? /* @__PURE__ */ import_react5.default.createElement("img", {
2147
+ }, file.isImage ? /* @__PURE__ */ import_react6.default.createElement("img", {
2082
2148
  src: file.url,
2083
2149
  alt: file.filename ?? "image",
2084
2150
  className: slots.classNames?.fileImage
@@ -2086,17 +2152,17 @@ function renderFilesBlock(block, slots) {
2086
2152
  }
2087
2153
  __name(renderFilesBlock, "renderFilesBlock");
2088
2154
  function renderReasoningBlock(block, slots) {
2089
- return /* @__PURE__ */ import_react5.default.createElement("div", {
2155
+ return /* @__PURE__ */ import_react6.default.createElement("div", {
2090
2156
  key: block.key,
2091
2157
  className: slots.classNames?.reasoning,
2092
2158
  "data-reasoning": "true",
2093
2159
  "data-streaming": block.isStreaming ? "true" : void 0
2094
- }, /* @__PURE__ */ import_react5.default.createElement("button", {
2160
+ }, /* @__PURE__ */ import_react6.default.createElement("button", {
2095
2161
  type: "button",
2096
2162
  className: slots.classNames?.reasoningToggle,
2097
2163
  onClick: /* @__PURE__ */ __name(() => block.toggle(), "onClick"),
2098
2164
  "aria-expanded": block.isOpen
2099
- }, slots.reasoningLabel ?? "Reasoning"), block.isOpen ? /* @__PURE__ */ import_react5.default.createElement("div", {
2165
+ }, slots.reasoningLabel ?? "Reasoning"), block.isOpen ? /* @__PURE__ */ import_react6.default.createElement("div", {
2100
2166
  className: slots.classNames?.reasoningText
2101
2167
  }, slots.renderReasoning ? slots.renderReasoning(block.text, {
2102
2168
  isStreaming: block.isStreaming,
@@ -2107,7 +2173,7 @@ __name(renderReasoningBlock, "renderReasoningBlock");
2107
2173
  function renderToolBlock(item, block, slots) {
2108
2174
  if (slots.renderToolGroup) {
2109
2175
  return [
2110
- /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, {
2176
+ /* @__PURE__ */ import_react6.default.createElement(import_react6.default.Fragment, {
2111
2177
  key: block.key
2112
2178
  }, slots.renderToolGroup(block.parts, block.key))
2113
2179
  ];
@@ -2118,7 +2184,7 @@ function renderToolBlock(item, block, slots) {
2118
2184
  const renderToolPart = slots.renderToolPart;
2119
2185
  return block.parts.map((part) => {
2120
2186
  const partKey = `${item.id}-${part.toolCallId}`;
2121
- return /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, {
2187
+ return /* @__PURE__ */ import_react6.default.createElement(import_react6.default.Fragment, {
2122
2188
  key: partKey
2123
2189
  }, renderToolPart(part, partKey));
2124
2190
  });
@@ -2128,14 +2194,14 @@ function EditableUserBubble({ item, slots, actions }) {
2128
2194
  const { classNames } = slots;
2129
2195
  const { edit } = item;
2130
2196
  if (!edit.isEditing) {
2131
- return /* @__PURE__ */ import_react5.default.createElement("div", {
2197
+ return /* @__PURE__ */ import_react6.default.createElement("div", {
2132
2198
  className: joinClasses(classNames?.root, classNames?.byRole?.user),
2133
2199
  "data-role": "user"
2134
- }, /* @__PURE__ */ import_react5.default.createElement("div", {
2200
+ }, /* @__PURE__ */ import_react6.default.createElement("div", {
2135
2201
  className: classNames?.text
2136
2202
  }, slots.renderText ? slots.renderText(item.text, {
2137
2203
  isStreaming: false
2138
- }) : item.text), /* @__PURE__ */ import_react5.default.createElement("button", {
2204
+ }) : item.text), /* @__PURE__ */ import_react6.default.createElement("button", {
2139
2205
  type: "button",
2140
2206
  onClick: edit.start,
2141
2207
  className: classNames?.actionButton,
@@ -2143,18 +2209,18 @@ function EditableUserBubble({ item, slots, actions }) {
2143
2209
  }, "Edit"), actions);
2144
2210
  }
2145
2211
  const textareaProps = edit.getTextareaProps();
2146
- return /* @__PURE__ */ import_react5.default.createElement("div", {
2212
+ return /* @__PURE__ */ import_react6.default.createElement("div", {
2147
2213
  "data-editing": "true"
2148
- }, /* @__PURE__ */ import_react5.default.createElement("textarea", {
2214
+ }, /* @__PURE__ */ import_react6.default.createElement("textarea", {
2149
2215
  ...textareaProps,
2150
2216
  rows: Math.min(8, edit.draft.split("\n").length + 1),
2151
2217
  className: classNames?.editTextarea
2152
- }), /* @__PURE__ */ import_react5.default.createElement("div", {
2218
+ }), /* @__PURE__ */ import_react6.default.createElement("div", {
2153
2219
  className: classNames?.editActions
2154
- }, /* @__PURE__ */ import_react5.default.createElement("button", {
2220
+ }, /* @__PURE__ */ import_react6.default.createElement("button", {
2155
2221
  type: "button",
2156
2222
  onClick: edit.cancel
2157
- }, "Cancel"), /* @__PURE__ */ import_react5.default.createElement("button", {
2223
+ }, "Cancel"), /* @__PURE__ */ import_react6.default.createElement("button", {
2158
2224
  type: "button",
2159
2225
  onClick: edit.save,
2160
2226
  disabled: !edit.canSave
@@ -2394,35 +2460,35 @@ function toItem(entry) {
2394
2460
  __name(toItem, "toItem");
2395
2461
 
2396
2462
  // src/composer/use-composer-autocomplete.ts
2397
- var import_react6 = require("react");
2463
+ var import_react7 = require("react");
2398
2464
  function useComposerAutocomplete(options) {
2399
2465
  const { value, sources, acceptOnTab = true } = options;
2400
- const latest = (0, import_react6.useRef)(options);
2466
+ const latest = (0, import_react7.useRef)(options);
2401
2467
  latest.current = options;
2402
- const inputRef = (0, import_react6.useRef)(null);
2403
- const [caret, setCaret] = (0, import_react6.useState)(value.length);
2404
- const [items, setItems] = (0, import_react6.useState)([]);
2405
- const [isLoading, setLoading] = (0, import_react6.useState)(false);
2406
- const [error, setError] = (0, import_react6.useState)(null);
2407
- const [activeIndex, setActiveIndex] = (0, import_react6.useState)(0);
2408
- const [dismissedIndex, setDismissedIndex] = (0, import_react6.useState)(null);
2468
+ const inputRef = (0, import_react7.useRef)(null);
2469
+ const [caret, setCaret] = (0, import_react7.useState)(value.length);
2470
+ const [items, setItems] = (0, import_react7.useState)([]);
2471
+ const [isLoading, setLoading] = (0, import_react7.useState)(false);
2472
+ const [error, setError] = (0, import_react7.useState)(null);
2473
+ const [activeIndex, setActiveIndex] = (0, import_react7.useState)(0);
2474
+ const [dismissedIndex, setDismissedIndex] = (0, import_react7.useState)(null);
2409
2475
  const safeCaret = Math.min(caret, value.length);
2410
- const match = (0, import_react6.useMemo)(() => findActiveTrigger(value, safeCaret, sources), [
2476
+ const match = (0, import_react7.useMemo)(() => findActiveTrigger(value, safeCaret, sources), [
2411
2477
  value,
2412
2478
  safeCaret,
2413
2479
  sources
2414
2480
  ]);
2415
2481
  const isActive = match !== null && dismissedIndex !== match.index;
2416
- const matchRef = (0, import_react6.useRef)(match);
2482
+ const matchRef = (0, import_react7.useRef)(match);
2417
2483
  matchRef.current = match;
2418
- const caretRef = (0, import_react6.useRef)(safeCaret);
2484
+ const caretRef = (0, import_react7.useRef)(safeCaret);
2419
2485
  caretRef.current = safeCaret;
2420
- const itemsRef = (0, import_react6.useRef)(items);
2486
+ const itemsRef = (0, import_react7.useRef)(items);
2421
2487
  itemsRef.current = items;
2422
- const activeIndexRef = (0, import_react6.useRef)(activeIndex);
2488
+ const activeIndexRef = (0, import_react7.useRef)(activeIndex);
2423
2489
  activeIndexRef.current = activeIndex;
2424
2490
  const requestKey = isActive && match ? `${match.source.id}\0${match.query}` : null;
2425
- (0, import_react6.useEffect)(() => {
2491
+ (0, import_react7.useEffect)(() => {
2426
2492
  const current = matchRef.current;
2427
2493
  if (requestKey === null || current === null) {
2428
2494
  setItems([]);
@@ -2470,25 +2536,25 @@ function useComposerAutocomplete(options) {
2470
2536
  ]);
2471
2537
  const isOpen = isActive;
2472
2538
  const activeItem = isOpen ? items[activeIndex] ?? null : null;
2473
- const reactId = (0, import_react6.useId)();
2539
+ const reactId = (0, import_react7.useId)();
2474
2540
  const listboxId = options.id ?? `agent-autocomplete-${reactId}`;
2475
- const optionId = (0, import_react6.useCallback)((index) => `${listboxId}-option-${index}`, [
2541
+ const optionId = (0, import_react7.useCallback)((index) => `${listboxId}-option-${index}`, [
2476
2542
  listboxId
2477
2543
  ]);
2478
- const highlight = (0, import_react6.useCallback)((index) => {
2544
+ const highlight = (0, import_react7.useCallback)((index) => {
2479
2545
  const total = itemsRef.current.length;
2480
2546
  if (total === 0) return;
2481
2547
  setActiveIndex((index % total + total) % total);
2482
2548
  }, []);
2483
- const moveHighlight = (0, import_react6.useCallback)((delta) => highlight(activeIndexRef.current + delta), [
2549
+ const moveHighlight = (0, import_react7.useCallback)((delta) => highlight(activeIndexRef.current + delta), [
2484
2550
  highlight
2485
2551
  ]);
2486
- const dismiss = (0, import_react6.useCallback)(() => {
2552
+ const dismiss = (0, import_react7.useCallback)(() => {
2487
2553
  const current = matchRef.current;
2488
2554
  if (current) setDismissedIndex(current.index);
2489
2555
  }, []);
2490
- const pendingCaret = (0, import_react6.useRef)(null);
2491
- const accept = (0, import_react6.useCallback)((item) => {
2556
+ const pendingCaret = (0, import_react7.useRef)(null);
2557
+ const accept = (0, import_react7.useCallback)((item) => {
2492
2558
  const current = matchRef.current;
2493
2559
  if (!current) return;
2494
2560
  const chosen = item ?? itemsRef.current[activeIndexRef.current];
@@ -2499,16 +2565,16 @@ function useComposerAutocomplete(options) {
2499
2565
  setDismissedIndex(null);
2500
2566
  pendingCaret.current = edit.caret;
2501
2567
  }, []);
2502
- (0, import_react6.useLayoutEffect)(() => {
2568
+ (0, import_react7.useLayoutEffect)(() => {
2503
2569
  const position = pendingCaret.current;
2504
2570
  if (position === null) return;
2505
2571
  pendingCaret.current = null;
2506
2572
  inputRef.current?.setSelectionRange(position, position);
2507
2573
  });
2508
- const readCaret = (0, import_react6.useCallback)((element) => {
2574
+ const readCaret = (0, import_react7.useCallback)((element) => {
2509
2575
  setCaret(element.selectionStart ?? element.value.length);
2510
2576
  }, []);
2511
- const onKeyDown = (0, import_react6.useCallback)((event) => {
2577
+ const onKeyDown = (0, import_react7.useCallback)((event) => {
2512
2578
  if (!isOpen) return;
2513
2579
  const hasItems = itemsRef.current.length > 0;
2514
2580
  switch (event.key) {
@@ -2546,23 +2612,23 @@ function useComposerAutocomplete(options) {
2546
2612
  dismiss,
2547
2613
  moveHighlight
2548
2614
  ]);
2549
- const onChange = (0, import_react6.useCallback)((event) => {
2615
+ const onChange = (0, import_react7.useCallback)((event) => {
2550
2616
  readCaret(event.target);
2551
2617
  const next = findActiveTrigger(event.target.value, event.target.selectionStart ?? event.target.value.length, latest.current.sources);
2552
2618
  setDismissedIndex((current) => current !== null && next?.index === current ? current : null);
2553
2619
  }, [
2554
2620
  readCaret
2555
2621
  ]);
2556
- const onSelect = (0, import_react6.useCallback)((event) => readCaret(event.currentTarget), [
2622
+ const onSelect = (0, import_react7.useCallback)((event) => readCaret(event.currentTarget), [
2557
2623
  readCaret
2558
2624
  ]);
2559
- const onClick = (0, import_react6.useCallback)((event) => readCaret(event.currentTarget), [
2625
+ const onClick = (0, import_react7.useCallback)((event) => readCaret(event.currentTarget), [
2560
2626
  readCaret
2561
2627
  ]);
2562
- const setInput = (0, import_react6.useCallback)((element) => {
2628
+ const setInput = (0, import_react7.useCallback)((element) => {
2563
2629
  inputRef.current = element;
2564
2630
  }, []);
2565
- const getInputProps = (0, import_react6.useCallback)(() => ({
2631
+ const getInputProps = (0, import_react7.useCallback)(() => ({
2566
2632
  ref: setInput,
2567
2633
  onChange,
2568
2634
  onKeyDown,
@@ -2590,7 +2656,7 @@ function useComposerAutocomplete(options) {
2590
2656
  activeIndex,
2591
2657
  optionId
2592
2658
  ]);
2593
- const getListboxProps = (0, import_react6.useCallback)(() => ({
2659
+ const getListboxProps = (0, import_react7.useCallback)(() => ({
2594
2660
  id: listboxId,
2595
2661
  role: "listbox",
2596
2662
  "aria-label": match?.source.label ?? match?.source.id ?? "Suggestions"
@@ -2598,7 +2664,7 @@ function useComposerAutocomplete(options) {
2598
2664
  listboxId,
2599
2665
  match
2600
2666
  ]);
2601
- const getOptionProps = (0, import_react6.useCallback)((index) => ({
2667
+ const getOptionProps = (0, import_react7.useCallback)((index) => ({
2602
2668
  id: optionId(index),
2603
2669
  role: "option",
2604
2670
  "aria-selected": index === activeIndex
@@ -2652,6 +2718,16 @@ function refusalReason(result) {
2652
2718
  __name(refusalReason, "refusalReason");
2653
2719
  function storedMessageToUiMessage(message) {
2654
2720
  const parts = [];
2721
+ if (message.reasoning) {
2722
+ parts.push({
2723
+ type: "reasoning",
2724
+ text: message.reasoning,
2725
+ state: "done",
2726
+ ...message.reasoningMs !== void 0 ? {
2727
+ providerMetadata: reasoningDurationMetadata(message.reasoningMs)
2728
+ } : {}
2729
+ });
2730
+ }
2655
2731
  if (message.content) {
2656
2732
  parts.push({
2657
2733
  type: "text",
@@ -2666,6 +2742,20 @@ function storedMessageToUiMessage(message) {
2666
2742
  url: attachment.url
2667
2743
  });
2668
2744
  }
2745
+ for (const component of message.ui ?? []) {
2746
+ parts.push({
2747
+ type: "data-ui",
2748
+ id: component.id,
2749
+ data: {
2750
+ id: component.id,
2751
+ component: component.component,
2752
+ props: component.props,
2753
+ ...component.version !== void 0 ? {
2754
+ version: component.version
2755
+ } : {}
2756
+ }
2757
+ });
2758
+ }
2669
2759
  for (const call of message.toolCalls ?? []) {
2670
2760
  const result = message.toolResults?.find((candidate) => candidate.id === call.id);
2671
2761
  const toolKind2 = call.kind;
@@ -2767,21 +2857,21 @@ function sumUsage(rows) {
2767
2857
  __name(sumUsage, "sumUsage");
2768
2858
 
2769
2859
  // src/use-agent-chat.ts
2770
- var import_react7 = require("@ai-sdk/react");
2771
- var import_react8 = require("react");
2860
+ var import_react8 = require("@ai-sdk/react");
2861
+ var import_react9 = require("react");
2772
2862
  function useAgentChat(options) {
2773
- const latest = (0, import_react8.useRef)(options);
2863
+ const latest = (0, import_react9.useRef)(options);
2774
2864
  latest.current = options;
2775
- const [runId, setRunId] = (0, import_react8.useState)(options.resumeRunId);
2776
- const runIdRef = (0, import_react8.useRef)(runId);
2865
+ const [runId, setRunId] = (0, import_react9.useState)(options.resumeRunId);
2866
+ const runIdRef = (0, import_react9.useRef)(runId);
2777
2867
  runIdRef.current = runId;
2778
- const settlingRunIdRef = (0, import_react8.useRef)(void 0);
2779
- const autoResumeRunIdRef = (0, import_react8.useRef)(void 0);
2780
- const [autoResume, setAutoResume] = (0, import_react8.useState)(false);
2781
- const [activeRunId, setActiveRunId] = (0, import_react8.useState)(null);
2782
- const regenerateNext = (0, import_react8.useRef)(false);
2783
- const createdThreadId = (0, import_react8.useRef)(void 0);
2784
- const client = (0, import_react8.useMemo)(() => {
2868
+ const settlingRunIdRef = (0, import_react9.useRef)(void 0);
2869
+ const autoResumeRunIdRef = (0, import_react9.useRef)(void 0);
2870
+ const [autoResume, setAutoResume] = (0, import_react9.useState)(false);
2871
+ const [activeRunId, setActiveRunId] = (0, import_react9.useState)(null);
2872
+ const regenerateNext = (0, import_react9.useRef)(false);
2873
+ const createdThreadId = (0, import_react9.useRef)(void 0);
2874
+ const client = (0, import_react9.useMemo)(() => {
2785
2875
  if (options.client) return options.client;
2786
2876
  return new AgentClient({
2787
2877
  ...options.baseUrl !== void 0 ? {
@@ -2796,7 +2886,7 @@ function useAgentChat(options) {
2796
2886
  getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders")
2797
2887
  });
2798
2888
  }, []);
2799
- (0, import_react8.useEffect)(() => {
2889
+ (0, import_react9.useEffect)(() => {
2800
2890
  if (!options.resume || options.threadId === void 0) return;
2801
2891
  let cancelled = false;
2802
2892
  const threadId = options.threadId;
@@ -2816,7 +2906,7 @@ function useAgentChat(options) {
2816
2906
  options.resume,
2817
2907
  options.threadId
2818
2908
  ]);
2819
- const transport = (0, import_react8.useMemo)(() => {
2909
+ const transport = (0, import_react9.useMemo)(() => {
2820
2910
  function onMeta(meta) {
2821
2911
  settlingRunIdRef.current = meta.runId;
2822
2912
  setRunId(meta.runId);
@@ -2865,8 +2955,8 @@ function useAgentChat(options) {
2865
2955
  }, "onAttemptStart")
2866
2956
  });
2867
2957
  }, []);
2868
- const backgroundRef = (0, import_react8.useRef)(() => Promise.resolve());
2869
- const chat = (0, import_react7.useChat)({
2958
+ const backgroundRef = (0, import_react9.useRef)(() => Promise.resolve());
2959
+ const chat = (0, import_react8.useChat)({
2870
2960
  transport,
2871
2961
  resume: options.resumeRunId !== void 0 || autoResume,
2872
2962
  ...options.threadId !== void 0 ? {
@@ -2896,13 +2986,13 @@ function useAgentChat(options) {
2896
2986
  }
2897
2987
  }, "onFinish")
2898
2988
  });
2899
- const chatRef = (0, import_react8.useRef)(chat);
2989
+ const chatRef = (0, import_react9.useRef)(chat);
2900
2990
  chatRef.current = chat;
2901
- const turnInFlight = (0, import_react8.useRef)(false);
2902
- const isTurnInFlight = (0, import_react8.useCallback)(() => turnInFlight.current || transport.isAttemptLive, [
2991
+ const turnInFlight = (0, import_react9.useRef)(false);
2992
+ const isTurnInFlight = (0, import_react9.useCallback)(() => turnInFlight.current || transport.isAttemptLive, [
2903
2993
  transport
2904
2994
  ]);
2905
- const sendMessage = (0, import_react8.useCallback)(async (...args) => {
2995
+ const sendMessage = (0, import_react9.useCallback)(async (...args) => {
2906
2996
  if (isTurnInFlight()) return;
2907
2997
  turnInFlight.current = true;
2908
2998
  try {
@@ -2913,15 +3003,15 @@ function useAgentChat(options) {
2913
3003
  }, [
2914
3004
  isTurnInFlight
2915
3005
  ]);
2916
- const addToolResult = (0, import_react8.useCallback)(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
3006
+ const addToolResult = (0, import_react9.useCallback)(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
2917
3007
  tool,
2918
3008
  toolCallId,
2919
3009
  output
2920
3010
  }), []);
2921
- const [threads, setThreads] = (0, import_react8.useState)([]);
2922
- const [quota, setQuota] = (0, import_react8.useState)(null);
2923
- const [backgroundRuns, setBackgroundRuns] = (0, import_react8.useState)([]);
2924
- const refreshBackground = (0, import_react8.useCallback)(async () => {
3011
+ const [threads, setThreads] = (0, import_react9.useState)([]);
3012
+ const [quota, setQuota] = (0, import_react9.useState)(null);
3013
+ const [backgroundRuns, setBackgroundRuns] = (0, import_react9.useState)([]);
3014
+ const refreshBackground = (0, import_react9.useCallback)(async () => {
2925
3015
  const threadId = latest.current.threadId ?? createdThreadId.current;
2926
3016
  if (threadId === void 0) {
2927
3017
  return;
@@ -2946,7 +3036,7 @@ function useAgentChat(options) {
2946
3036
  ]);
2947
3037
  backgroundRef.current = refreshBackground;
2948
3038
  const isWorking = backgroundRuns.some((run) => run.status === "running");
2949
- (0, import_react8.useEffect)(() => {
3039
+ (0, import_react9.useEffect)(() => {
2950
3040
  if (options.background !== true || options.threadId === void 0) {
2951
3041
  return;
2952
3042
  }
@@ -2956,7 +3046,7 @@ function useAgentChat(options) {
2956
3046
  options.threadId,
2957
3047
  refreshBackground
2958
3048
  ]);
2959
- (0, import_react8.useEffect)(() => {
3049
+ (0, import_react9.useEffect)(() => {
2960
3050
  const every = latest.current.backgroundPollMs ?? 5e3;
2961
3051
  if (latest.current.background !== true || !isWorking || every <= 0) {
2962
3052
  return;
@@ -2967,26 +3057,26 @@ function useAgentChat(options) {
2967
3057
  isWorking,
2968
3058
  refreshBackground
2969
3059
  ]);
2970
- const loadThreads = (0, import_react8.useCallback)(async () => {
3060
+ const loadThreads = (0, import_react9.useCallback)(async () => {
2971
3061
  const list = await client.listThreads();
2972
3062
  setThreads(list);
2973
3063
  return list;
2974
3064
  }, [
2975
3065
  client
2976
3066
  ]);
2977
- const loadThread = (0, import_react8.useCallback)((id) => client.getThread(id), [
3067
+ const loadThread = (0, import_react9.useCallback)((id) => client.getThread(id), [
2978
3068
  client
2979
3069
  ]);
2980
- const deleteThread = (0, import_react8.useCallback)(async (id) => {
3070
+ const deleteThread = (0, import_react9.useCallback)(async (id) => {
2981
3071
  await client.deleteThread(id);
2982
3072
  setThreads((current) => current.filter((thread) => thread.id !== id));
2983
3073
  }, [
2984
3074
  client
2985
3075
  ]);
2986
- const forkThread = (0, import_react8.useCallback)((threadId, messageId) => client.forkFromMessage(threadId, messageId), [
3076
+ const forkThread = (0, import_react9.useCallback)((threadId, messageId) => client.forkFromMessage(threadId, messageId), [
2987
3077
  client
2988
3078
  ]);
2989
- const renameThread = (0, import_react8.useCallback)(async (id, title) => {
3079
+ const renameThread = (0, import_react9.useCallback)(async (id, title) => {
2990
3080
  await client.renameThread(id, title);
2991
3081
  setThreads((current) => current.map((thread) => thread.id === id ? {
2992
3082
  ...thread,
@@ -2995,24 +3085,24 @@ function useAgentChat(options) {
2995
3085
  }, [
2996
3086
  client
2997
3087
  ]);
2998
- const promoteThread = (0, import_react8.useCallback)(async (id) => {
3088
+ const promoteThread = (0, import_react9.useCallback)(async (id) => {
2999
3089
  await client.promoteThread(id);
3000
3090
  await loadThreads();
3001
3091
  }, [
3002
3092
  client,
3003
3093
  loadThreads
3004
3094
  ]);
3005
- const truncateFromMessage = (0, import_react8.useCallback)((threadId, messageId) => client.truncateFromMessage(threadId, messageId).then(() => void 0), [
3095
+ const truncateFromMessage = (0, import_react9.useCallback)((threadId, messageId) => client.truncateFromMessage(threadId, messageId).then(() => void 0), [
3006
3096
  client
3007
3097
  ]);
3008
- const loadQuota = (0, import_react8.useCallback)(async () => {
3098
+ const loadQuota = (0, import_react9.useCallback)(async () => {
3009
3099
  const today = await client.getQuotaToday();
3010
3100
  setQuota(today);
3011
3101
  return today;
3012
3102
  }, [
3013
3103
  client
3014
3104
  ]);
3015
- const cancel = (0, import_react8.useCallback)(async () => {
3105
+ const cancel = (0, import_react9.useCallback)(async () => {
3016
3106
  chatRef.current.stop();
3017
3107
  const activeRunId2 = runIdRef.current;
3018
3108
  if (activeRunId2) {
@@ -3024,14 +3114,14 @@ function useAgentChat(options) {
3024
3114
  }, [
3025
3115
  client
3026
3116
  ]);
3027
- const approve = (0, import_react8.useCallback)(async ({ toolCallId }) => {
3117
+ const approve = (0, import_react9.useCallback)(async ({ toolCallId }) => {
3028
3118
  await client.approveToolCall({
3029
3119
  toolCallId
3030
3120
  });
3031
3121
  }, [
3032
3122
  client
3033
3123
  ]);
3034
- const reject = (0, import_react8.useCallback)(async ({ toolCallId, reason }) => {
3124
+ const reject = (0, import_react9.useCallback)(async ({ toolCallId, reason }) => {
3035
3125
  await client.rejectToolCall({
3036
3126
  toolCallId,
3037
3127
  ...reason !== void 0 ? {
@@ -3041,7 +3131,7 @@ function useAgentChat(options) {
3041
3131
  }, [
3042
3132
  client
3043
3133
  ]);
3044
- const answer = (0, import_react8.useCallback)(async ({ toolCallId, answers }) => {
3134
+ const answer = (0, import_react9.useCallback)(async ({ toolCallId, answers }) => {
3045
3135
  await client.answerToolCall({
3046
3136
  toolCallId,
3047
3137
  ...answers !== void 0 ? {
@@ -3051,14 +3141,14 @@ function useAgentChat(options) {
3051
3141
  }, [
3052
3142
  client
3053
3143
  ]);
3054
- const skip = (0, import_react8.useCallback)(async ({ toolCallId }) => {
3144
+ const skip = (0, import_react9.useCallback)(async ({ toolCallId }) => {
3055
3145
  await client.skipToolCall({
3056
3146
  toolCallId
3057
3147
  });
3058
3148
  }, [
3059
3149
  client
3060
3150
  ]);
3061
- const regenerate = (0, import_react8.useCallback)(() => {
3151
+ const regenerate = (0, import_react9.useCallback)(() => {
3062
3152
  if (isTurnInFlight()) return;
3063
3153
  regenerateNext.current = true;
3064
3154
  turnInFlight.current = true;
@@ -3130,14 +3220,17 @@ __name(mergeHeaders, "mergeHeaders");
3130
3220
  extractMessageText,
3131
3221
  filterAutocompleteItems,
3132
3222
  findActiveTrigger,
3223
+ formatElapsed,
3133
3224
  formatRelativeTime,
3134
3225
  loadStoredSpeechLang,
3135
3226
  persistSpeechLang,
3227
+ readReasoningMs,
3136
3228
  storedMessageToUiMessage,
3137
3229
  storedThreadToUiMessages,
3138
3230
  useAgentChat,
3139
3231
  useChatTranscript,
3140
3232
  useComposerAutocomplete,
3233
+ useElapsed,
3141
3234
  useSpeechRecognition,
3142
3235
  useStickToBottom,
3143
3236
  useTranscriptItem