@assistant-ui/react 0.5.41 → 0.5.45

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.mjs CHANGED
@@ -164,6 +164,7 @@ import { create as create4 } from "zustand";
164
164
  var getThreadStateFromRuntime = (runtime) => {
165
165
  const lastMessage = runtime.messages.at(-1);
166
166
  return Object.freeze({
167
+ threadId: runtime.threadId,
167
168
  capabilities: runtime.capabilities,
168
169
  isDisabled: runtime.isDisabled,
169
170
  isRunning: lastMessage?.role !== "assistant" ? false : lastMessage.status.type === "running"
@@ -636,6 +637,9 @@ var useSmooth = (state, smooth = false) => {
636
637
  setDisplayedText(text2);
637
638
  useSmoothStatus2?.setState(text2 !== state.part.text ? SMOOTH_STATUS : state.status);
638
639
  });
640
+ useEffect(() => {
641
+ useSmoothStatus2?.setState(text !== displayedText ? SMOOTH_STATUS : state.status);
642
+ }, [useSmoothStatus2, text, displayedText, state.status]);
639
643
  const [animatorRef] = useState2(
640
644
  new TextStreamAnimator(text, setText)
641
645
  );
@@ -1072,59 +1076,12 @@ var useEdgeRuntime = ({
1072
1076
  // src/runtimes/local/shouldContinue.tsx
1073
1077
  var shouldContinue = (result) => result.status?.type === "requires-action" && result.status.reason === "tool-calls" && result.content.every((c) => c.type !== "tool-call" || !!c.result);
1074
1078
 
1075
- // src/utils/getThreadMessageText.tsx
1076
- var getThreadMessageText = (message) => {
1077
- const textParts = message.content.filter(
1078
- (part) => part.type === "text"
1079
- );
1080
- return textParts.map((part) => part.text).join("\n\n");
1081
- };
1082
-
1083
- // src/runtimes/speech/WebSpeechSynthesisAdapter.ts
1084
- var WebSpeechSynthesisAdapter = class {
1085
- speak(message) {
1086
- const text = getThreadMessageText(message);
1087
- const utterance = new SpeechSynthesisUtterance(text);
1088
- let ended = false;
1089
- const endHandlers = /* @__PURE__ */ new Set();
1090
- const handleEnd = () => {
1091
- if (ended) return;
1092
- ended = true;
1093
- endHandlers.forEach((handler) => handler());
1094
- };
1095
- utterance.addEventListener("end", handleEnd);
1096
- utterance.addEventListener("error", handleEnd);
1097
- window.speechSynthesis.speak(utterance);
1098
- return {
1099
- stop: () => {
1100
- window.speechSynthesis.cancel();
1101
- handleEnd();
1102
- },
1103
- onEnd: (callback) => {
1104
- if (ended) {
1105
- let cancelled = false;
1106
- queueMicrotask(() => {
1107
- if (!cancelled) callback();
1108
- });
1109
- return () => {
1110
- cancelled = true;
1111
- };
1112
- } else {
1113
- endHandlers.add(callback);
1114
- return () => {
1115
- endHandlers.delete(callback);
1116
- };
1117
- }
1118
- }
1119
- };
1120
- }
1121
- };
1122
-
1123
1079
  // src/runtimes/local/LocalThreadRuntime.tsx
1124
1080
  var LocalThreadRuntime = class {
1125
1081
  constructor(configProvider, adapter, { initialMessages, ...options }) {
1126
1082
  this.configProvider = configProvider;
1127
1083
  this.adapter = adapter;
1084
+ this.threadId = generateId();
1128
1085
  this.options = options;
1129
1086
  if (initialMessages) {
1130
1087
  let parentId = null;
@@ -1146,6 +1103,7 @@ var LocalThreadRuntime = class {
1146
1103
  unstable_copy: true,
1147
1104
  speak: false
1148
1105
  };
1106
+ threadId;
1149
1107
  isDisabled = false;
1150
1108
  get messages() {
1151
1109
  return this.repository.getMessages();
@@ -1158,6 +1116,9 @@ var LocalThreadRuntime = class {
1158
1116
  }
1159
1117
  };
1160
1118
  _options;
1119
+ get options() {
1120
+ return this._options;
1121
+ }
1161
1122
  set options({ initialMessages, ...options }) {
1162
1123
  this._options = options;
1163
1124
  const canSpeak = options.adapters?.speech !== void 0;
@@ -1239,7 +1200,7 @@ var LocalThreadRuntime = class {
1239
1200
  this.repository.addOrUpdateMessage(parentId, message);
1240
1201
  this.notifySubscribers();
1241
1202
  };
1242
- const maxToolRoundtrips = this._options.maxToolRoundtrips ?? 1;
1203
+ const maxToolRoundtrips = this.options.maxToolRoundtrips ?? 1;
1243
1204
  const toolRoundtrips = message.metadata?.roundtrips?.length ?? 0;
1244
1205
  if (toolRoundtrips > maxToolRoundtrips) {
1245
1206
  updateMessage({
@@ -1334,10 +1295,24 @@ var LocalThreadRuntime = class {
1334
1295
  this.performRoundtrip(parentId, message);
1335
1296
  }
1336
1297
  }
1298
+ // TODO lift utterance state to thread runtime
1299
+ _utterance;
1337
1300
  speak(messageId) {
1301
+ const adapter = this.options.adapters?.speech;
1302
+ if (!adapter) throw new Error("Speech adapter not configured");
1338
1303
  const { message } = this.repository.getMessage(messageId);
1339
- const adapter = new WebSpeechSynthesisAdapter();
1340
- return adapter.speak(message);
1304
+ if (this._utterance) {
1305
+ this._utterance.cancel();
1306
+ this._utterance = void 0;
1307
+ }
1308
+ const utterance = adapter.speak(message);
1309
+ utterance.onEnd(() => {
1310
+ if (this._utterance === utterance) {
1311
+ this._utterance = void 0;
1312
+ }
1313
+ });
1314
+ this._utterance = utterance;
1315
+ return this._utterance;
1341
1316
  }
1342
1317
  export() {
1343
1318
  return this.repository.export();
@@ -1431,11 +1406,12 @@ var getAutoStatus = (isLast, isRunning) => isLast && isRunning ? AUTO_STATUS_RUN
1431
1406
 
1432
1407
  // src/runtimes/external-store/ThreadMessageLike.tsx
1433
1408
  var fromThreadMessageLike = (like, fallbackId, fallbackStatus) => {
1434
- const { role, content, id, createdAt, status } = like;
1409
+ const { role, id, createdAt, status } = like;
1435
1410
  const common = {
1436
1411
  id: id ?? fallbackId,
1437
1412
  createdAt: createdAt ?? /* @__PURE__ */ new Date()
1438
1413
  };
1414
+ const content = typeof like.content === "string" ? [{ type: "text", text: like.content }] : like.content;
1439
1415
  switch (role) {
1440
1416
  case "assistant":
1441
1417
  return {
@@ -1445,6 +1421,8 @@ var fromThreadMessageLike = (like, fallbackId, fallbackStatus) => {
1445
1421
  const type = part.type;
1446
1422
  switch (type) {
1447
1423
  case "text":
1424
+ if (part.text.trim().length === 0) return null;
1425
+ return part;
1448
1426
  case "ui":
1449
1427
  return part;
1450
1428
  case "tool-call": {
@@ -1459,7 +1437,7 @@ var fromThreadMessageLike = (like, fallbackId, fallbackStatus) => {
1459
1437
  throw new Error(`Unknown content part type: ${unhandledType}`);
1460
1438
  }
1461
1439
  }
1462
- }),
1440
+ }).filter((c) => !!c),
1463
1441
  status: status ?? fallbackStatus
1464
1442
  };
1465
1443
  case "user":
@@ -1497,6 +1475,14 @@ var fromThreadMessageLike = (like, fallbackId, fallbackStatus) => {
1497
1475
  }
1498
1476
  };
1499
1477
 
1478
+ // src/utils/getThreadMessageText.tsx
1479
+ var getThreadMessageText = (message) => {
1480
+ const textParts = message.content.filter(
1481
+ (part) => part.type === "text"
1482
+ );
1483
+ return textParts.map((part) => part.text).join("\n\n");
1484
+ };
1485
+
1500
1486
  // src/runtimes/external-store/ExternalStoreThreadRuntime.tsx
1501
1487
  var hasUpcomingMessage = (isRunning, messages) => {
1502
1488
  return isRunning && messages[messages.length - 1]?.role !== "assistant";
@@ -1516,8 +1502,9 @@ var ExternalStoreThreadRuntime = class {
1516
1502
  get capabilities() {
1517
1503
  return this._capabilities;
1518
1504
  }
1519
- messages = [];
1520
- isDisabled = false;
1505
+ threadId;
1506
+ messages;
1507
+ isDisabled;
1521
1508
  converter = new ThreadMessageConverter();
1522
1509
  _store;
1523
1510
  composer = {
@@ -1530,8 +1517,12 @@ var ExternalStoreThreadRuntime = class {
1530
1517
  constructor(store) {
1531
1518
  this.store = store;
1532
1519
  }
1520
+ get store() {
1521
+ return this._store;
1522
+ }
1533
1523
  set store(store) {
1534
1524
  if (this._store === store) return;
1525
+ this.threadId = store.threadId ?? this.threadId ?? generateId();
1535
1526
  const isRunning = store.isRunning ?? false;
1536
1527
  this.isDisabled = store.isDisabled ?? false;
1537
1528
  const oldStore = this._store;
@@ -1667,6 +1658,9 @@ var ExternalStoreRuntime = class extends BaseAssistantRuntime {
1667
1658
  constructor(store) {
1668
1659
  super(new ExternalStoreThreadRuntime(store));
1669
1660
  }
1661
+ get store() {
1662
+ return this.thread.store;
1663
+ }
1670
1664
  set store(store) {
1671
1665
  this.thread.store = store;
1672
1666
  }
@@ -1707,6 +1701,153 @@ var useExternalStoreRuntime = (store) => {
1707
1701
  return runtime;
1708
1702
  };
1709
1703
 
1704
+ // src/runtimes/external-store/external-message-converter.tsx
1705
+ import { useMemo as useMemo2 } from "react";
1706
+ var joinExternalMessages = (messages) => {
1707
+ const assistantMessage = {
1708
+ role: "assistant",
1709
+ content: []
1710
+ };
1711
+ for (const output of messages) {
1712
+ if (output.role === "tool") {
1713
+ const toolCallIdx = assistantMessage.content.findIndex(
1714
+ (c) => c.type === "tool-call" && c.toolCallId === output.toolCallId
1715
+ );
1716
+ if (toolCallIdx !== -1) {
1717
+ const toolCall = assistantMessage.content[toolCallIdx];
1718
+ if (output.toolName) {
1719
+ if (toolCall.toolName !== output.toolName)
1720
+ throw new Error(
1721
+ `Tool call name ${output.toolCallId} ${output.toolName} does not match existing tool call ${toolCall.toolName}`
1722
+ );
1723
+ }
1724
+ assistantMessage.content[toolCallIdx] = {
1725
+ ...toolCall,
1726
+ result: output.result
1727
+ };
1728
+ } else {
1729
+ throw new Error(
1730
+ `Tool call ${output.toolCallId} ${output.toolName} not found in assistant message`
1731
+ );
1732
+ }
1733
+ } else {
1734
+ const content = output.content;
1735
+ const role = output.role;
1736
+ switch (role) {
1737
+ case "system":
1738
+ case "user":
1739
+ return { role, content };
1740
+ case "assistant":
1741
+ if (assistantMessage.content.length === 0) {
1742
+ assistantMessage.id = output.id;
1743
+ assistantMessage.createdAt ??= output.createdAt;
1744
+ assistantMessage.status ??= output.status;
1745
+ }
1746
+ assistantMessage.content.push(...content);
1747
+ break;
1748
+ default: {
1749
+ const unsupportedRole = role;
1750
+ throw new Error(`Unknown message role: ${unsupportedRole}`);
1751
+ }
1752
+ }
1753
+ }
1754
+ }
1755
+ return assistantMessage;
1756
+ };
1757
+ var chunkExternalMessages = (callbackResults) => {
1758
+ const results = [];
1759
+ let isAssistant = false;
1760
+ let inputs = [];
1761
+ let outputs = [];
1762
+ const flush = () => {
1763
+ if (outputs.length) {
1764
+ results.push({
1765
+ inputs,
1766
+ outputs
1767
+ });
1768
+ }
1769
+ inputs = [];
1770
+ outputs = [];
1771
+ };
1772
+ for (const callbackResult of callbackResults) {
1773
+ for (const output of callbackResult.outputs) {
1774
+ if (!isAssistant || output.role === "user" || output.role === "system") {
1775
+ flush();
1776
+ }
1777
+ isAssistant = output.role === "assistant" || output.role === "tool";
1778
+ if (inputs.at(-1) !== callbackResult.input) {
1779
+ inputs.push(callbackResult.input);
1780
+ }
1781
+ outputs.push(output);
1782
+ }
1783
+ }
1784
+ flush();
1785
+ return results;
1786
+ };
1787
+ var useExternalMessageConverter = ({
1788
+ callback,
1789
+ messages,
1790
+ isRunning
1791
+ }) => {
1792
+ const state = useMemo2(
1793
+ () => ({
1794
+ callback,
1795
+ callbackCache: /* @__PURE__ */ new WeakMap(),
1796
+ chunkCache: /* @__PURE__ */ new WeakMap(),
1797
+ converterCache: new ThreadMessageConverter()
1798
+ }),
1799
+ [callback]
1800
+ );
1801
+ return useMemo2(() => {
1802
+ const callbackResults = [];
1803
+ for (const message of messages) {
1804
+ let result = state.callbackCache.get(message);
1805
+ if (!result) {
1806
+ const output = state.callback(message);
1807
+ const outputs = Array.isArray(output) ? output : [output];
1808
+ result = { input: message, outputs };
1809
+ state.callbackCache.set(message, result);
1810
+ }
1811
+ callbackResults.push(result);
1812
+ }
1813
+ const chunks = chunkExternalMessages(callbackResults).map((m) => {
1814
+ const key = m.outputs[0];
1815
+ if (!key) return m;
1816
+ const cached = state.chunkCache.get(key);
1817
+ if (cached && shallowArrayEqual(cached.outputs, m.outputs)) return cached;
1818
+ state.chunkCache.set(key, m);
1819
+ return m;
1820
+ });
1821
+ return state.converterCache.convertMessages(
1822
+ chunks,
1823
+ (cache, message, idx) => {
1824
+ const isLast = idx === chunks.length - 1;
1825
+ const autoStatus = getAutoStatus(isLast, isRunning);
1826
+ if (cache && (cache.role !== "assistant" || !isAutoStatus(cache.status) || cache.status === autoStatus)) {
1827
+ const inputs = getExternalStoreMessage(cache);
1828
+ if (shallowArrayEqual(inputs, message.inputs)) {
1829
+ return cache;
1830
+ }
1831
+ }
1832
+ const newMessage = fromThreadMessageLike(
1833
+ joinExternalMessages(message.outputs),
1834
+ idx.toString(),
1835
+ autoStatus
1836
+ );
1837
+ newMessage[symbolInnerMessage] = message.inputs;
1838
+ return newMessage;
1839
+ }
1840
+ );
1841
+ }, [state, messages, isRunning]);
1842
+ };
1843
+ var shallowArrayEqual = (a, b) => {
1844
+ if (a.length !== b.length) return false;
1845
+ for (let i = 0; i < a.length; i++) {
1846
+ if (a[i] !== b[i]) return false;
1847
+ }
1848
+ return true;
1849
+ };
1850
+
1710
1851
  // src/runtimes/dangerous-in-browser/useDangerousInBrowserRuntime.ts
1711
1852
  import { useState as useState6 } from "react";
1712
1853
 
@@ -1743,6 +1884,47 @@ var useDangerousInBrowserRuntime = ({
1743
1884
  return useLocalRuntime(adapter, { initialMessages });
1744
1885
  };
1745
1886
 
1887
+ // src/runtimes/speech/WebSpeechSynthesisAdapter.ts
1888
+ var WebSpeechSynthesisAdapter = class {
1889
+ speak(message) {
1890
+ const text = getThreadMessageText(message);
1891
+ const utterance = new SpeechSynthesisUtterance(text);
1892
+ const endHandlers = /* @__PURE__ */ new Set();
1893
+ const handleEnd = (reason, error) => {
1894
+ if (res.status.type === "ended") return;
1895
+ res.status = { type: "ended", reason, error };
1896
+ endHandlers.forEach((handler) => handler());
1897
+ };
1898
+ utterance.addEventListener("end", () => handleEnd("finished"));
1899
+ utterance.addEventListener("error", (e) => handleEnd("error", e.error));
1900
+ window.speechSynthesis.speak(utterance);
1901
+ const res = {
1902
+ status: { type: "running" },
1903
+ cancel: () => {
1904
+ window.speechSynthesis.cancel();
1905
+ handleEnd("cancelled");
1906
+ },
1907
+ onEnd: (callback) => {
1908
+ if (res.status.type === "ended") {
1909
+ let cancelled = false;
1910
+ queueMicrotask(() => {
1911
+ if (!cancelled) callback();
1912
+ });
1913
+ return () => {
1914
+ cancelled = true;
1915
+ };
1916
+ } else {
1917
+ endHandlers.add(callback);
1918
+ return () => {
1919
+ endHandlers.delete(callback);
1920
+ };
1921
+ }
1922
+ }
1923
+ };
1924
+ return res;
1925
+ }
1926
+ };
1927
+
1746
1928
  // src/context/providers/ThreadProvider.tsx
1747
1929
  import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
1748
1930
  var ThreadProvider = ({
@@ -1770,7 +1952,7 @@ var ThreadProvider = ({
1770
1952
  const thread = provider.thread;
1771
1953
  const oldState = context.useThread.getState();
1772
1954
  const state = getThreadStateFromRuntime(thread);
1773
- if (oldState.isDisabled !== state.isDisabled || oldState.isRunning !== state.isRunning || // TODO ensure capabilities is memoized
1955
+ if (oldState.threadId !== state.threadId || oldState.isDisabled !== state.isDisabled || oldState.isRunning !== state.isRunning || // TODO ensure capabilities is memoized
1774
1956
  oldState.capabilities !== state.capabilities) {
1775
1957
  context.useThread.setState(
1776
1958
  state,
@@ -1859,7 +2041,7 @@ var AssistantRuntimeProviderImpl = ({ children, runtime }) => {
1859
2041
  var AssistantRuntimeProvider = memo(AssistantRuntimeProviderImpl);
1860
2042
 
1861
2043
  // src/context/react/ComposerContext.ts
1862
- import { useMemo as useMemo2 } from "react";
2044
+ import { useMemo as useMemo3 } from "react";
1863
2045
 
1864
2046
  // src/context/react/MessageContext.ts
1865
2047
  import { createContext as createContext4, useContext as useContext4 } from "react";
@@ -1877,7 +2059,7 @@ function useMessageContext(options) {
1877
2059
  var useComposerContext = () => {
1878
2060
  const { useComposer } = useThreadContext();
1879
2061
  const { useEditComposer } = useMessageContext({ optional: true }) ?? {};
1880
- return useMemo2(
2062
+ return useMemo3(
1881
2063
  () => ({
1882
2064
  useComposer: useEditComposer ?? useComposer,
1883
2065
  type: useEditComposer ? "edit" : "new"
@@ -2017,7 +2199,7 @@ var useAssistantInstructions = (instruction) => {
2017
2199
  import { useCallback as useCallback3 } from "react";
2018
2200
 
2019
2201
  // src/utils/combined/useCombinedStore.ts
2020
- import { useMemo as useMemo3 } from "react";
2202
+ import { useMemo as useMemo4 } from "react";
2021
2203
 
2022
2204
  // src/utils/combined/createCombinedStore.ts
2023
2205
  import { useSyncExternalStore } from "react";
@@ -2038,7 +2220,7 @@ var createCombinedStore = (stores) => {
2038
2220
 
2039
2221
  // src/utils/combined/useCombinedStore.ts
2040
2222
  var useCombinedStore = (stores, selector) => {
2041
- const useCombined = useMemo3(() => createCombinedStore(stores), stores);
2223
+ const useCombined = useMemo4(() => createCombinedStore(stores), stores);
2042
2224
  return useCombined(selector);
2043
2225
  };
2044
2226
 
@@ -2900,6 +3082,7 @@ var MessageContentPartComponent = ({
2900
3082
  const Tool = by_name[part.toolName] || Fallback2;
2901
3083
  const addResult = (result) => addToolResult({
2902
3084
  messageId: useMessage.getState().message.id,
3085
+ toolName: part.toolName,
2903
3086
  toolCallId: part.toolCallId,
2904
3087
  result
2905
3088
  });
@@ -3335,7 +3518,7 @@ var makeMessageUtilsStore = () => create14((set) => {
3335
3518
  },
3336
3519
  isSpeaking: false,
3337
3520
  stopSpeaking: () => {
3338
- utterance?.stop();
3521
+ utterance?.cancel();
3339
3522
  },
3340
3523
  addUtterance: (utt) => {
3341
3524
  utterance = utt;
@@ -3537,28 +3720,29 @@ import {
3537
3720
  StopCircleIcon
3538
3721
  } from "lucide-react";
3539
3722
  import { Fragment as Fragment4, jsx as jsx32, jsxs as jsxs5 } from "react/jsx-runtime";
3540
- var useAllowCopy = () => {
3723
+ var useAllowCopy = (ensureCapability = false) => {
3541
3724
  const { assistantMessage: { allowCopy = true } = {} } = useThreadConfig();
3542
3725
  const { useThread } = useThreadContext();
3543
3726
  const copySupported = useThread((t) => t.capabilities.unstable_copy);
3544
- return copySupported && allowCopy;
3727
+ return allowCopy && (!ensureCapability || copySupported);
3545
3728
  };
3546
- var useAllowSpeak = () => {
3729
+ var useAllowSpeak = (ensureCapability = false) => {
3547
3730
  const { assistantMessage: { allowSpeak = true } = {} } = useThreadConfig();
3548
3731
  const { useThread } = useThreadContext();
3549
3732
  const speakSupported = useThread((t) => t.capabilities.speak);
3550
- return speakSupported && allowSpeak;
3733
+ return allowSpeak && (!ensureCapability || speakSupported);
3551
3734
  };
3552
- var useAllowReload = () => {
3735
+ var useAllowReload = (ensureCapability = false) => {
3553
3736
  const { assistantMessage: { allowReload = true } = {} } = useThreadConfig();
3554
3737
  const { useThread } = useThreadContext();
3555
3738
  const reloadSupported = useThread((t) => t.capabilities.reload);
3556
- return reloadSupported && allowReload;
3739
+ return allowReload && (!ensureCapability || reloadSupported);
3557
3740
  };
3558
3741
  var AssistantActionBar = () => {
3559
- const allowCopy = useAllowCopy();
3560
- const allowReload = useAllowReload();
3561
- if (!allowCopy && !allowReload) return null;
3742
+ const allowCopy = useAllowCopy(true);
3743
+ const allowReload = useAllowReload(true);
3744
+ const allowSpeak = useAllowSpeak(true);
3745
+ if (!allowCopy && !allowReload && !allowSpeak) return null;
3562
3746
  return /* @__PURE__ */ jsxs5(
3563
3747
  AssistantActionBarRoot,
3564
3748
  {
@@ -3566,9 +3750,9 @@ var AssistantActionBar = () => {
3566
3750
  autohide: "not-last",
3567
3751
  autohideFloat: "single-branch",
3568
3752
  children: [
3569
- /* @__PURE__ */ jsx32(AssistantActionBarSpeechControl, {}),
3570
- /* @__PURE__ */ jsx32(AssistantActionBarCopy, {}),
3571
- /* @__PURE__ */ jsx32(AssistantActionBarReload, {})
3753
+ allowSpeak && /* @__PURE__ */ jsx32(AssistantActionBarSpeechControl, {}),
3754
+ allowCopy && /* @__PURE__ */ jsx32(AssistantActionBarCopy, {}),
3755
+ allowReload && /* @__PURE__ */ jsx32(AssistantActionBarReload, {})
3572
3756
  ]
3573
3757
  }
3574
3758
  );
@@ -3584,8 +3768,6 @@ var AssistantActionBarCopy = forwardRef20((props, ref) => {
3584
3768
  assistantMessage: { copy: { tooltip = "Copy" } = {} } = {}
3585
3769
  } = {}
3586
3770
  } = useThreadConfig();
3587
- const allowCopy = useAllowCopy();
3588
- if (!allowCopy) return null;
3589
3771
  return /* @__PURE__ */ jsx32(actionBar_exports.Copy, { asChild: true, children: /* @__PURE__ */ jsx32(TooltipIconButton, { tooltip, ...props, ref, children: props.children ?? /* @__PURE__ */ jsxs5(Fragment4, { children: [
3590
3772
  /* @__PURE__ */ jsx32(message_exports.If, { copied: true, children: /* @__PURE__ */ jsx32(CheckIcon, {}) }),
3591
3773
  /* @__PURE__ */ jsx32(message_exports.If, { copied: false, children: /* @__PURE__ */ jsx32(CopyIcon, {}) })
@@ -3979,14 +4161,14 @@ import { forwardRef as forwardRef26 } from "react";
3979
4161
  import { forwardRef as forwardRef25 } from "react";
3980
4162
  import { PencilIcon } from "lucide-react";
3981
4163
  import { jsx as jsx40 } from "react/jsx-runtime";
3982
- var useAllowEdit = () => {
4164
+ var useAllowEdit = (ensureCapability = false) => {
3983
4165
  const { userMessage: { allowEdit = true } = {} } = useThreadConfig();
3984
4166
  const { useThread } = useThreadContext();
3985
4167
  const editSupported = useThread((t) => t.capabilities.edit);
3986
- return editSupported && allowEdit;
4168
+ return allowEdit && (!ensureCapability || editSupported);
3987
4169
  };
3988
4170
  var UserActionBar = () => {
3989
- const allowEdit = useAllowEdit();
4171
+ const allowEdit = useAllowEdit(true);
3990
4172
  if (!allowEdit) return null;
3991
4173
  return /* @__PURE__ */ jsx40(UserActionBarRoot, { hideWhenRunning: true, autohide: "not-last", children: /* @__PURE__ */ jsx40(UserActionBarEdit, {}) });
3992
4174
  };
@@ -4310,6 +4492,7 @@ export {
4310
4492
  useContentPartText,
4311
4493
  useDangerousInBrowserRuntime,
4312
4494
  useEdgeRuntime,
4495
+ useExternalMessageConverter,
4313
4496
  useExternalStoreRuntime,
4314
4497
  useLocalRuntime,
4315
4498
  useMessageContext,