@assistant-ui/react 0.1.7 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
package/dist/index.mjs CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  useContentPartContext,
10
10
  useMessageContext,
11
11
  useThreadContext
12
- } from "./chunk-XVZ2GVQM.mjs";
12
+ } from "./chunk-7O2URLFI.mjs";
13
13
 
14
14
  // src/actions/useCopyMessage.tsx
15
15
  import { useCallback } from "react";
@@ -50,7 +50,7 @@ var getMessageText = (message) => {
50
50
 
51
51
  // src/actions/useCopyMessage.tsx
52
52
  var useCopyMessage = ({ copiedDuration = 3e3 }) => {
53
- const { useMessage, useComposer } = useMessageContext();
53
+ const { useMessage, useMessageUtils, useComposer } = useMessageContext();
54
54
  const hasCopyableContent = useCombinedStore(
55
55
  [useMessage, useComposer],
56
56
  (m, c) => {
@@ -58,13 +58,14 @@ var useCopyMessage = ({ copiedDuration = 3e3 }) => {
58
58
  }
59
59
  );
60
60
  const callback = useCallback(() => {
61
+ const { message } = useMessage.getState();
62
+ const { setIsCopied } = useMessageUtils.getState();
61
63
  const { isEditing, value: composerValue } = useComposer.getState();
62
- const { message, setIsCopied } = useMessage.getState();
63
64
  const valueToCopy = isEditing ? composerValue : getMessageText(message);
64
65
  navigator.clipboard.writeText(valueToCopy);
65
66
  setIsCopied(true);
66
67
  setTimeout(() => setIsCopied(false), copiedDuration);
67
- }, [useComposer, useMessage, copiedDuration]);
68
+ }, [useMessage, useMessageUtils, useComposer, copiedDuration]);
68
69
  if (!hasCopyableContent) return null;
69
70
  return callback;
70
71
  };
@@ -72,7 +73,7 @@ var useCopyMessage = ({ copiedDuration = 3e3 }) => {
72
73
  // src/actions/useReloadMessage.tsx
73
74
  import { useCallback as useCallback2 } from "react";
74
75
  var useReloadMessage = () => {
75
- const { useThread, useViewport } = useThreadContext();
76
+ const { useThread, useThreadActions, useViewport } = useThreadContext();
76
77
  const { useMessage } = useMessageContext();
77
78
  const disabled = useCombinedStore(
78
79
  [useThread, useMessage],
@@ -80,9 +81,9 @@ var useReloadMessage = () => {
80
81
  );
81
82
  const callback = useCallback2(() => {
82
83
  const { parentId } = useMessage.getState();
83
- useThread.getState().startRun(parentId);
84
+ useThreadActions.getState().startRun(parentId);
84
85
  useViewport.getState().scrollToBottom();
85
- }, [useMessage, useThread, useViewport]);
86
+ }, [useThreadActions, useMessage, useViewport]);
86
87
  if (disabled) return null;
87
88
  return callback;
88
89
  };
@@ -106,7 +107,7 @@ var useBeginMessageEdit = () => {
106
107
  // src/actions/useGoToNextBranch.tsx
107
108
  import { useCallback as useCallback4 } from "react";
108
109
  var useGoToNextBranch = () => {
109
- const { useThread } = useThreadContext();
110
+ const { useThreadActions } = useThreadContext();
110
111
  const { useMessage, useComposer } = useMessageContext();
111
112
  const disabled = useCombinedStore(
112
113
  [useMessage, useComposer],
@@ -114,8 +115,8 @@ var useGoToNextBranch = () => {
114
115
  );
115
116
  const callback = useCallback4(() => {
116
117
  const { message, branches } = useMessage.getState();
117
- useThread.getState().switchToBranch(branches[branches.indexOf(message.id) + 1]);
118
- }, [useMessage, useThread]);
118
+ useThreadActions.getState().switchToBranch(branches[branches.indexOf(message.id) + 1]);
119
+ }, [useThreadActions, useMessage]);
119
120
  if (disabled) return null;
120
121
  return callback;
121
122
  };
@@ -123,7 +124,7 @@ var useGoToNextBranch = () => {
123
124
  // src/actions/useGoToPreviousBranch.tsx
124
125
  import { useCallback as useCallback5 } from "react";
125
126
  var useGoToPreviousBranch = () => {
126
- const { useThread } = useThreadContext();
127
+ const { useThreadActions } = useThreadContext();
127
128
  const { useMessage, useComposer } = useMessageContext();
128
129
  const disabled = useCombinedStore(
129
130
  [useMessage, useComposer],
@@ -131,8 +132,8 @@ var useGoToPreviousBranch = () => {
131
132
  );
132
133
  const callback = useCallback5(() => {
133
134
  const { message, branches } = useMessage.getState();
134
- useThread.getState().switchToBranch(branches[branches.indexOf(message.id) - 1]);
135
- }, [useMessage, useThread]);
135
+ useThreadActions.getState().switchToBranch(branches[branches.indexOf(message.id) - 1]);
136
+ }, [useThreadActions, useMessage]);
136
137
  if (disabled) return null;
137
138
  return callback;
138
139
  };
@@ -278,7 +279,9 @@ var ThreadViewport = forwardRef2(({ autoScroll = true, onScroll, children, ...re
278
279
  if (!newIsAtBottom && lastScrollTop.current < div.scrollTop) {
279
280
  } else if (newIsAtBottom !== isAtBottom) {
280
281
  isScrollingToBottomRef.current = false;
281
- useViewport.setState({ isAtBottom: newIsAtBottom });
282
+ useViewport.setState({
283
+ isAtBottom: newIsAtBottom
284
+ });
282
285
  }
283
286
  lastScrollTop.current = div.scrollTop;
284
287
  };
@@ -302,7 +305,7 @@ import { memo } from "react";
302
305
 
303
306
  // src/context/providers/MessageProvider.tsx
304
307
  import { useEffect as useEffect3, useState } from "react";
305
- import { create as create2 } from "zustand";
308
+ import { create as create3 } from "zustand";
306
309
 
307
310
  // src/context/stores/MessageComposer.ts
308
311
  import { create } from "zustand";
@@ -338,17 +341,34 @@ var makeEditComposerStore = ({
338
341
  }
339
342
  }));
340
343
 
344
+ // src/context/stores/MessageUtils.ts
345
+ import { create as create2 } from "zustand";
346
+ var makeMessageUtilsStore = () => create2((set) => ({
347
+ inProgressIndicator: null,
348
+ setInProgressIndicator: (value) => {
349
+ set({ inProgressIndicator: value });
350
+ },
351
+ isCopied: false,
352
+ setIsCopied: (value) => {
353
+ set({ isCopied: value });
354
+ },
355
+ isHovering: false,
356
+ setIsHovering: (value) => {
357
+ set({ isHovering: value });
358
+ }
359
+ }));
360
+
341
361
  // src/context/providers/MessageProvider.tsx
342
362
  import { jsx as jsx4 } from "react/jsx-runtime";
343
363
  var getIsLast = (thread, message) => {
344
364
  return thread.messages[thread.messages.length - 1]?.id === message.id;
345
365
  };
346
- var syncMessage = (thread, useMessage, messageIndex) => {
366
+ var syncMessage = (thread, getBranches, useMessage, messageIndex) => {
347
367
  const parentId = thread.messages[messageIndex - 1]?.id ?? null;
348
368
  const message = thread.messages[messageIndex];
349
369
  if (!message) return;
350
370
  const isLast = getIsLast(thread, message);
351
- const branches = thread.getBranches(message.id);
371
+ const branches = getBranches(message.id);
352
372
  const currentState = useMessage.getState();
353
373
  if (currentState.message === message && currentState.parentId === parentId && currentState.branches === branches && currentState.isLast === isLast)
354
374
  return;
@@ -360,26 +380,10 @@ var syncMessage = (thread, useMessage, messageIndex) => {
360
380
  });
361
381
  };
362
382
  var useMessageContext2 = (messageIndex) => {
363
- const { useThread } = useThreadContext();
383
+ const { useThread, useThreadActions } = useThreadContext();
364
384
  const [context] = useState(() => {
365
- const useMessage = create2((set) => ({
366
- message: null,
367
- parentId: null,
368
- branches: [],
369
- isLast: false,
370
- inProgressIndicator: null,
371
- isCopied: false,
372
- isHovering: false,
373
- setInProgressIndicator: (value) => {
374
- set({ inProgressIndicator: value });
375
- },
376
- setIsCopied: (value) => {
377
- set({ isCopied: value });
378
- },
379
- setIsHovering: (value) => {
380
- set({ isHovering: value });
381
- }
382
- }));
385
+ const useMessage = create3(() => ({}));
386
+ const useMessageUtils = makeMessageUtilsStore();
383
387
  const useComposer = makeEditComposerStore({
384
388
  onEdit: () => {
385
389
  const message = useMessage.getState().message;
@@ -399,20 +403,30 @@ var useMessageContext2 = (messageIndex) => {
399
403
  const nonTextParts = message.content.filter(
400
404
  (part) => part.type !== "text" && part.type !== "ui"
401
405
  );
402
- useThread.getState().append({
406
+ useThreadActions.getState().append({
403
407
  parentId,
404
408
  content: [{ type: "text", text }, ...nonTextParts]
405
409
  });
406
410
  }
407
411
  });
408
- syncMessage(useThread.getState(), useMessage, messageIndex);
409
- return { useMessage, useComposer };
412
+ syncMessage(
413
+ useThread.getState(),
414
+ useThreadActions.getState().getBranches,
415
+ useMessage,
416
+ messageIndex
417
+ );
418
+ return { useMessage, useMessageUtils, useComposer };
410
419
  });
411
420
  useEffect3(() => {
412
421
  return useThread.subscribe((thread) => {
413
- syncMessage(thread, context.useMessage, messageIndex);
422
+ syncMessage(
423
+ thread,
424
+ useThreadActions.getState().getBranches,
425
+ context.useMessage,
426
+ messageIndex
427
+ );
414
428
  });
415
- }, [context, useThread, messageIndex]);
429
+ }, [useThread, useThreadActions, context, messageIndex]);
416
430
  return context;
417
431
  };
418
432
  var MessageProvider = ({
@@ -439,16 +453,19 @@ var ComposerIf = ({ children, ...query }) => {
439
453
 
440
454
  // src/primitives/message/MessageIf.tsx
441
455
  var useMessageIf = (props) => {
442
- const { useMessage } = useMessageContext();
443
- return useMessage(({ message, branches, isLast, isCopied, isHovering }) => {
444
- if (props.hasBranches === true && branches.length < 2) return false;
445
- if (props.user && message.role !== "user") return false;
446
- if (props.assistant && message.role !== "assistant") return false;
447
- if (props.lastOrHover === true && !isHovering && !isLast) return false;
448
- if (props.copied === true && !isCopied) return false;
449
- if (props.copied === false && isCopied) return false;
450
- return true;
451
- });
456
+ const { useMessage, useMessageUtils } = useMessageContext();
457
+ return useCombinedStore(
458
+ [useMessage, useMessageUtils],
459
+ ({ message, branches, isLast }, { isCopied, isHovering }) => {
460
+ if (props.hasBranches === true && branches.length < 2) return false;
461
+ if (props.user && message.role !== "user") return false;
462
+ if (props.assistant && message.role !== "assistant") return false;
463
+ if (props.lastOrHover === true && !isHovering && !isLast) return false;
464
+ if (props.copied === true && !isCopied) return false;
465
+ if (props.copied === false && isCopied) return false;
466
+ return true;
467
+ }
468
+ );
452
469
  };
453
470
  var MessageIf = ({ children, ...query }) => {
454
471
  const result = useMessageIf(query);
@@ -727,8 +744,8 @@ import { forwardRef as forwardRef9 } from "react";
727
744
  import { jsx as jsx12 } from "react/jsx-runtime";
728
745
  var MessageRoot = forwardRef9(
729
746
  ({ onMouseEnter, onMouseLeave, ...rest }, ref) => {
730
- const { useMessage } = useMessageContext();
731
- const setIsHovering = useMessage((s) => s.setIsHovering);
747
+ const { useMessageUtils } = useMessageContext();
748
+ const setIsHovering = useMessageUtils((s) => s.setIsHovering);
732
749
  const handleMouseEnter = () => {
733
750
  setIsHovering(true);
734
751
  };
@@ -753,7 +770,7 @@ import { memo as memo2 } from "react";
753
770
 
754
771
  // src/context/providers/ContentPartProvider.tsx
755
772
  import { useEffect as useEffect5, useState as useState2 } from "react";
756
- import { create as create3 } from "zustand";
773
+ import { create as create4 } from "zustand";
757
774
  import { jsx as jsx13 } from "react/jsx-runtime";
758
775
  var syncContentPart = ({ message }, useContentPart, partIndex) => {
759
776
  const part = message.content[partIndex];
@@ -762,19 +779,24 @@ var syncContentPart = ({ message }, useContentPart, partIndex) => {
762
779
  const status = partIndex === message.content.length - 1 ? messageStatus : "done";
763
780
  const currentState = useContentPart.getState();
764
781
  if (currentState.part === part && currentState.status === status) return;
765
- useContentPart.setState({ part, status });
782
+ useContentPart.setState(
783
+ Object.freeze({
784
+ part,
785
+ status
786
+ })
787
+ );
766
788
  };
767
789
  var useContentPartContext2 = (partIndex) => {
768
790
  const { useMessage } = useMessageContext();
769
791
  const [context] = useState2(() => {
770
- const useContentPart = create3(() => ({
771
- part: { type: "text", text: "" },
772
- status: "done"
773
- }));
792
+ const useContentPart = create4(
793
+ () => ({})
794
+ );
774
795
  syncContentPart(useMessage.getState(), useContentPart, partIndex);
775
796
  return { useContentPart };
776
797
  });
777
798
  useEffect5(() => {
799
+ syncContentPart(useMessage.getState(), context.useContentPart, partIndex);
778
800
  return useMessage.subscribe((message) => {
779
801
  syncContentPart(message, context.useContentPart, partIndex);
780
802
  });
@@ -804,10 +826,10 @@ var ContentPartDisplay = () => {
804
826
 
805
827
  // src/primitives/contentPart/ContentPartInProgressIndicator.tsx
806
828
  var ContentPartInProgressIndicator = () => {
807
- const { useMessage } = useMessageContext();
829
+ const { useMessageUtils } = useMessageContext();
808
830
  const { useContentPart } = useContentPartContext();
809
831
  const indicator = useCombinedStore(
810
- [useMessage, useContentPart],
832
+ [useMessageUtils, useContentPart],
811
833
  (m, c) => c.status === "in_progress" ? m.inProgressIndicator : null
812
834
  );
813
835
  return indicator;
@@ -841,10 +863,8 @@ var defaultComponents = {
841
863
  UI: () => /* @__PURE__ */ jsx15(ContentPartDisplay, {}),
842
864
  tools: {
843
865
  Fallback: (props) => {
844
- const { useToolRenderers } = useAssistantContext();
845
- const Render = useToolRenderers(
846
- (s) => s.getToolRenderer(props.part.toolName)
847
- );
866
+ const { useToolUIs } = useAssistantContext();
867
+ const Render = useToolUIs((s) => s.getToolUI(props.part.toolName));
848
868
  if (!Render) return null;
849
869
  return /* @__PURE__ */ jsx15(Render, { ...props });
850
870
  }
@@ -858,6 +878,8 @@ var MessageContentPartComponent = ({
858
878
  tools: { by_name = {}, Fallback = defaultComponents.tools.Fallback } = {}
859
879
  } = {}
860
880
  }) => {
881
+ const { useThreadActions } = useThreadContext();
882
+ const addToolResult = useThreadActions((t) => t.addToolResult);
861
883
  const { useContentPart } = useContentPartContext();
862
884
  const { part, status } = useContentPart();
863
885
  const type = part.type;
@@ -870,7 +892,8 @@ var MessageContentPartComponent = ({
870
892
  return /* @__PURE__ */ jsx15(UI, { part, status });
871
893
  case "tool-call": {
872
894
  const Tool = by_name[part.toolName] || Fallback;
873
- return /* @__PURE__ */ jsx15(Tool, { part, status });
895
+ const addResult = (result) => addToolResult(part.toolCallId, result);
896
+ return /* @__PURE__ */ jsx15(Tool, { part, status, addResult });
874
897
  }
875
898
  default:
876
899
  throw new Error(`Unknown content part type: ${type}`);
@@ -910,10 +933,10 @@ import {
910
933
  } from "react";
911
934
  import { jsx as jsx16 } from "react/jsx-runtime";
912
935
  var MessageInProgress = forwardRef11((props, ref) => {
913
- const { useMessage } = useMessageContext();
936
+ const { useMessageUtils } = useMessageContext();
914
937
  useMemo2(() => {
915
- useMessage.getState().setInProgressIndicator(/* @__PURE__ */ jsx16(Primitive10.span, { ...props, ref }));
916
- }, [useMessage, props, ref]);
938
+ useMessageUtils.getState().setInProgressIndicator(/* @__PURE__ */ jsx16(Primitive10.span, { ...props, ref }));
939
+ }, [useMessageUtils, props, ref]);
917
940
  return null;
918
941
  });
919
942
  MessageInProgress.displayName = "MessageInProgress";
@@ -997,14 +1020,14 @@ import { forwardRef as forwardRef14 } from "react";
997
1020
  import { jsx as jsx21 } from "react/jsx-runtime";
998
1021
  var ActionBarRoot = forwardRef14(({ hideWhenRunning, autohide, autohideFloat, ...rest }, ref) => {
999
1022
  const { useThread } = useThreadContext();
1000
- const { useMessage } = useMessageContext();
1023
+ const { useMessage, useMessageUtils } = useMessageContext();
1001
1024
  const hideAndfloatStatus = useCombinedStore(
1002
- [useThread, useMessage],
1003
- (t, m) => {
1025
+ [useThread, useMessage, useMessageUtils],
1026
+ (t, m, mu) => {
1004
1027
  if (hideWhenRunning && t.isRunning) return "hidden" /* Hidden */;
1005
1028
  const autohideEnabled = autohide === "always" || autohide === "not-last" && !m.isLast;
1006
1029
  if (!autohideEnabled) return "normal" /* Normal */;
1007
- if (!m.isHovering) return "hidden" /* Hidden */;
1030
+ if (!mu.isHovering) return "hidden" /* Hidden */;
1008
1031
  if (autohideFloat === "always" || autohideFloat === "single-branch" && m.branches.length <= 1)
1009
1032
  return "floating" /* Floating */;
1010
1033
  return "normal" /* Normal */;
@@ -1337,6 +1360,9 @@ var LocalRuntime = class {
1337
1360
  this._configProviders.add(provider);
1338
1361
  return () => this._configProviders.delete(provider);
1339
1362
  }
1363
+ addToolResult() {
1364
+ throw new Error("LocalRuntime does not yet support tool results");
1365
+ }
1340
1366
  };
1341
1367
 
1342
1368
  // src/runtime/local/useLocalRuntime.tsx
@@ -1355,7 +1381,7 @@ import { memo as memo3 } from "react";
1355
1381
  import { useEffect as useEffect7, useInsertionEffect as useInsertionEffect3, useRef as useRef5, useState as useState5 } from "react";
1356
1382
 
1357
1383
  // src/context/stores/AssistantModelConfig.ts
1358
- import { create as create4 } from "zustand";
1384
+ import { create as create5 } from "zustand";
1359
1385
 
1360
1386
  // src/utils/ProxyConfigProvider.ts
1361
1387
  var ProxyConfigProvider = class {
@@ -1372,30 +1398,30 @@ var ProxyConfigProvider = class {
1372
1398
  };
1373
1399
 
1374
1400
  // src/context/stores/AssistantModelConfig.ts
1375
- var makeAssistantModelConfigStore = () => create4(() => {
1401
+ var makeAssistantModelConfigStore = () => create5(() => {
1376
1402
  const proxy = new ProxyConfigProvider();
1377
- return {
1403
+ return Object.freeze({
1378
1404
  getModelConfig: () => {
1379
1405
  return proxy.getModelConfig();
1380
1406
  },
1381
1407
  registerModelConfigProvider: (provider) => {
1382
1408
  return proxy.registerModelConfigProvider(provider);
1383
1409
  }
1384
- };
1410
+ });
1385
1411
  });
1386
1412
 
1387
- // src/context/stores/AssistantToolRenderers.ts
1388
- import { create as create5 } from "zustand";
1389
- var makeAssistantToolRenderersStore = () => create5((set) => {
1413
+ // src/context/stores/AssistantToolUIs.ts
1414
+ import { create as create6 } from "zustand";
1415
+ var makeAssistantToolUIsStore = () => create6((set) => {
1390
1416
  const renderers = /* @__PURE__ */ new Map();
1391
- return {
1392
- getToolRenderer: (name) => {
1417
+ return Object.freeze({
1418
+ getToolUI: (name) => {
1393
1419
  const arr = renderers.get(name);
1394
1420
  const last = arr?.at(-1);
1395
1421
  if (last) return last;
1396
1422
  return null;
1397
1423
  },
1398
- setToolRenderer: (name, render) => {
1424
+ setToolUI: (name, render) => {
1399
1425
  let arr = renderers.get(name);
1400
1426
  if (!arr) {
1401
1427
  arr = [];
@@ -1408,25 +1434,27 @@ var makeAssistantToolRenderersStore = () => create5((set) => {
1408
1434
  if (index !== -1) {
1409
1435
  arr.splice(index, 1);
1410
1436
  }
1411
- set({});
1437
+ if (index === arr.length) {
1438
+ set({});
1439
+ }
1412
1440
  };
1413
1441
  }
1414
- };
1442
+ });
1415
1443
  });
1416
1444
 
1417
1445
  // src/context/providers/ThreadProvider.tsx
1418
1446
  import { useEffect as useEffect6, useInsertionEffect as useInsertionEffect2, useRef as useRef4, useState as useState4 } from "react";
1419
1447
 
1420
1448
  // src/context/stores/Composer.ts
1421
- import { create as create6 } from "zustand";
1422
- var makeComposerStore = (useThread) => create6()((set, get, store) => {
1449
+ import { create as create7 } from "zustand";
1450
+ var makeComposerStore = (useThread, useThreadActions) => create7()((set, get, store) => {
1423
1451
  return {
1424
1452
  ...makeBaseComposer(set, get, store),
1425
1453
  isEditing: true,
1426
1454
  send: () => {
1427
1455
  const { setValue, value } = get();
1428
1456
  setValue("");
1429
- useThread.getState().append({
1457
+ useThreadActions.getState().append({
1430
1458
  parentId: useThread.getState().messages.at(-1)?.id ?? null,
1431
1459
  content: [{ type: "text", text: value }]
1432
1460
  });
@@ -1434,41 +1462,26 @@ var makeComposerStore = (useThread) => create6()((set, get, store) => {
1434
1462
  cancel: () => {
1435
1463
  const thread = useThread.getState();
1436
1464
  if (!thread.isRunning) return false;
1437
- useThread.getState().cancelRun();
1465
+ useThreadActions.getState().cancelRun();
1438
1466
  return true;
1439
1467
  }
1440
1468
  };
1441
1469
  });
1442
1470
 
1443
1471
  // src/context/stores/Thread.ts
1444
- import { create as create7 } from "zustand";
1472
+ import { create as create8 } from "zustand";
1445
1473
  var makeThreadStore = (runtimeRef) => {
1446
- const useThread = create7(() => ({
1474
+ return create8(() => ({
1447
1475
  messages: runtimeRef.current.messages,
1448
- isRunning: runtimeRef.current.isRunning,
1449
- getBranches: (messageId) => runtimeRef.current.getBranches(messageId),
1450
- switchToBranch: (branchId) => runtimeRef.current.switchToBranch(branchId),
1451
- startRun: (parentId) => runtimeRef.current.startRun(parentId),
1452
- append: (message) => runtimeRef.current.append(message),
1453
- cancelRun: () => runtimeRef.current.cancelRun()
1476
+ isRunning: runtimeRef.current.isRunning
1454
1477
  }));
1455
- const onRuntimeUpdate = () => {
1456
- useThread.setState({
1457
- messages: runtimeRef.current.messages,
1458
- isRunning: runtimeRef.current.isRunning
1459
- });
1460
- };
1461
- return {
1462
- useThread,
1463
- onRuntimeUpdate
1464
- };
1465
1478
  };
1466
1479
 
1467
1480
  // src/context/stores/ThreadViewport.tsx
1468
- import { create as create8 } from "zustand";
1481
+ import { create as create9 } from "zustand";
1469
1482
  var makeThreadViewportStore = () => {
1470
1483
  const scrollToBottomListeners = /* @__PURE__ */ new Set();
1471
- return create8(() => ({
1484
+ return create9(() => ({
1472
1485
  isAtBottom: true,
1473
1486
  scrollToBottom: () => {
1474
1487
  for (const listener of scrollToBottomListeners) {
@@ -1484,6 +1497,21 @@ var makeThreadViewportStore = () => {
1484
1497
  }));
1485
1498
  };
1486
1499
 
1500
+ // src/context/stores/ThreadActions.ts
1501
+ import { create as create10 } from "zustand";
1502
+ var makeThreadActionStore = (runtimeRef) => {
1503
+ return create10(
1504
+ () => Object.freeze({
1505
+ getBranches: (messageId) => runtimeRef.current.getBranches(messageId),
1506
+ switchToBranch: (branchId) => runtimeRef.current.switchToBranch(branchId),
1507
+ startRun: (parentId) => runtimeRef.current.startRun(parentId),
1508
+ append: (message) => runtimeRef.current.append(message),
1509
+ cancelRun: () => runtimeRef.current.cancelRun(),
1510
+ addToolResult: (toolCallId, result) => runtimeRef.current.addToolResult(toolCallId, result)
1511
+ })
1512
+ );
1513
+ };
1514
+
1487
1515
  // src/context/providers/ThreadProvider.tsx
1488
1516
  import { jsx as jsx23, jsxs as jsxs4 } from "react/jsx-runtime";
1489
1517
  var ThreadProvider = ({
@@ -1494,23 +1522,31 @@ var ThreadProvider = ({
1494
1522
  useInsertionEffect2(() => {
1495
1523
  runtimeRef.current = runtime;
1496
1524
  });
1497
- const [{ context, onRuntimeUpdate }] = useState4(() => {
1498
- const { useThread, onRuntimeUpdate: onRuntimeUpdate2 } = makeThreadStore(runtimeRef);
1525
+ const [context] = useState4(() => {
1526
+ const useThread = makeThreadStore(runtimeRef);
1527
+ const useThreadActions = makeThreadActionStore(runtimeRef);
1499
1528
  const useViewport = makeThreadViewportStore();
1500
- const useComposer = makeComposerStore(useThread);
1529
+ const useComposer = makeComposerStore(useThread, useThreadActions);
1501
1530
  return {
1502
- context: {
1503
- useViewport,
1504
- useThread,
1505
- useComposer
1506
- },
1507
- onRuntimeUpdate: onRuntimeUpdate2
1531
+ useThread,
1532
+ useThreadActions,
1533
+ useComposer,
1534
+ useViewport
1508
1535
  };
1509
1536
  });
1510
1537
  useEffect6(() => {
1538
+ const onRuntimeUpdate = () => {
1539
+ context.useThread.setState(
1540
+ Object.freeze({
1541
+ messages: runtimeRef.current.messages,
1542
+ isRunning: runtimeRef.current.isRunning
1543
+ }),
1544
+ true
1545
+ );
1546
+ };
1511
1547
  onRuntimeUpdate();
1512
1548
  return runtime.subscribe(onRuntimeUpdate);
1513
- }, [onRuntimeUpdate, runtime]);
1549
+ }, [context, runtime]);
1514
1550
  const RuntimeSynchronizer = runtime.unstable_synchronizer;
1515
1551
  return /* @__PURE__ */ jsxs4(ThreadContext.Provider, { value: context, children: [
1516
1552
  RuntimeSynchronizer && /* @__PURE__ */ jsx23(RuntimeSynchronizer, {}),
@@ -1527,8 +1563,8 @@ var AssistantProvider = ({ children, runtime }) => {
1527
1563
  });
1528
1564
  const [context] = useState5(() => {
1529
1565
  const useModelConfig = makeAssistantModelConfigStore();
1530
- const useToolRenderers = makeAssistantToolRenderersStore();
1531
- return { useModelConfig, useToolRenderers };
1566
+ const useToolUIs = makeAssistantToolUIsStore();
1567
+ return { useModelConfig, useToolUIs };
1532
1568
  });
1533
1569
  const getModelCOnfig = context.useModelConfig((c) => c.getModelConfig);
1534
1570
  useEffect7(() => {