@assistant-ui/react 0.5.77 → 0.5.79

Sign up to get free protection for your applications and to get access to all the features.
package/dist/index.mjs CHANGED
@@ -412,15 +412,11 @@ function useComposerRuntime(options) {
412
412
  import { useCallback } from "react";
413
413
  var useAppendMessage = () => {
414
414
  const threadRuntime = useThreadRuntime();
415
- const threadViewportStore = useThreadViewportStore();
416
- const threadComposerStore = useThreadComposerStore();
417
415
  const append = useCallback(
418
416
  (message) => {
419
417
  threadRuntime.append(message);
420
- threadViewportStore.getState().scrollToBottom();
421
- threadComposerStore.getState().focus();
422
418
  },
423
- [threadRuntime, threadViewportStore, threadComposerStore]
419
+ [threadRuntime]
424
420
  );
425
421
  return append;
426
422
  };
@@ -429,11 +425,9 @@ var useAppendMessage = () => {
429
425
  import { useCallback as useCallback2 } from "react";
430
426
  var useSwitchToNewThread = () => {
431
427
  const assistantRuntime = useAssistantRuntime();
432
- const threadComposerStore = useThreadComposerStore();
433
428
  const switchToNewThread = useCallback2(() => {
434
429
  assistantRuntime.switchToNewThread();
435
- threadComposerStore.getState().focus();
436
- }, [assistantRuntime, threadComposerStore]);
430
+ }, [assistantRuntime]);
437
431
  return switchToNewThread;
438
432
  };
439
433
 
@@ -546,25 +540,25 @@ var getThreadMessageText = (message) => {
546
540
  var useActionBarCopy = ({
547
541
  copiedDuration = 3e3
548
542
  } = {}) => {
549
- const messageStore = useMessageStore();
543
+ const messageRuntime = useMessageRuntime();
544
+ const composerRuntime = useComposerRuntime();
550
545
  const messageUtilsStore = useMessageUtilsStore();
551
- const editComposerStore = useEditComposerStore();
552
546
  const hasCopyableContent = useCombinedStore(
553
- [messageStore, editComposerStore],
547
+ [messageRuntime, composerRuntime],
554
548
  (message, c) => {
555
549
  return !c.isEditing && (message.role !== "assistant" || message.status.type !== "running") && message.content.some((c2) => c2.type === "text" && c2.text.length > 0);
556
550
  }
557
551
  );
558
552
  const callback = useCallback3(() => {
559
- const message = messageStore.getState();
553
+ const message = messageRuntime.getState();
560
554
  const { setIsCopied } = messageUtilsStore.getState();
561
- const { isEditing, text: composerValue } = editComposerStore.getState();
555
+ const { isEditing, text: composerValue } = composerRuntime.getState();
562
556
  const valueToCopy = isEditing ? composerValue : getThreadMessageText(message);
563
557
  navigator.clipboard.writeText(valueToCopy).then(() => {
564
558
  setIsCopied(true);
565
559
  setTimeout(() => setIsCopied(false), copiedDuration);
566
560
  });
567
- }, [messageStore, messageUtilsStore, editComposerStore, copiedDuration]);
561
+ }, [messageRuntime, messageUtilsStore, composerRuntime, copiedDuration]);
568
562
  if (!hasCopyableContent) return null;
569
563
  return callback;
570
564
  };
@@ -584,20 +578,15 @@ var useActionBarEdit = () => {
584
578
  // src/primitive-hooks/actionBar/useActionBarReload.tsx
585
579
  import { useCallback as useCallback5 } from "react";
586
580
  var useActionBarReload = () => {
587
- const messageStore = useMessageStore();
588
- const threadStore = useThreadStore();
589
581
  const messageRuntime = useMessageRuntime();
590
- const threadComposerStore = useThreadComposerStore();
591
- const threadViewportStore = useThreadViewportStore();
582
+ const threadRuntime = useThreadRuntime();
592
583
  const disabled = useCombinedStore(
593
- [threadStore, messageStore],
584
+ [threadRuntime, messageRuntime],
594
585
  (t, m) => t.isRunning || t.isDisabled || m.role !== "assistant"
595
586
  );
596
587
  const callback = useCallback5(() => {
597
588
  messageRuntime.reload();
598
- threadViewportStore.getState().scrollToBottom();
599
- threadComposerStore.getState().focus();
600
- }, [messageRuntime, threadComposerStore, threadViewportStore]);
589
+ }, [messageRuntime]);
601
590
  if (disabled) return null;
602
591
  return callback;
603
592
  };
@@ -687,12 +676,11 @@ var useBranchPickerPrevious = () => {
687
676
  // src/primitive-hooks/composer/useComposerCancel.tsx
688
677
  import { useCallback as useCallback12 } from "react";
689
678
  var useComposerCancel = () => {
690
- const composerStore = useComposerStore();
679
+ const composerRuntime = useComposerRuntime();
691
680
  const disabled = useComposer((c) => !c.canCancel);
692
681
  const callback = useCallback12(() => {
693
- const { cancel } = composerStore.getState();
694
- cancel();
695
- }, [composerStore]);
682
+ composerRuntime.cancel();
683
+ }, [composerRuntime]);
696
684
  if (disabled) return null;
697
685
  return callback;
698
686
  };
@@ -709,21 +697,16 @@ var useComposerIf = (props) => {
709
697
  // src/primitive-hooks/composer/useComposerSend.tsx
710
698
  import { useCallback as useCallback13 } from "react";
711
699
  var useComposerSend = () => {
712
- const threadStore = useThreadStore();
713
- const threadViewportStore = useThreadViewportStore();
714
- const composerStore = useComposerStore();
715
- const threadComposerStore = useThreadComposerStore();
700
+ const composerRuntime = useComposerRuntime();
701
+ const threadRuntime = useThreadRuntime();
716
702
  const disabled = useCombinedStore(
717
- [threadStore, composerStore],
703
+ [threadRuntime, composerRuntime],
718
704
  (t, c) => t.isRunning || !c.isEditing || c.isEmpty
719
705
  );
720
706
  const callback = useCallback13(() => {
721
- const composerState = composerStore.getState();
722
- if (!composerState.isEditing) return;
723
- composerState.send();
724
- threadViewportStore.getState().scrollToBottom();
725
- threadComposerStore.getState().focus();
726
- }, [threadComposerStore, composerStore, threadViewportStore]);
707
+ if (!composerRuntime.getState().isEditing) return;
708
+ composerRuntime.send();
709
+ }, [threadRuntime]);
727
710
  if (disabled) return null;
728
711
  return callback;
729
712
  };
@@ -789,10 +772,10 @@ var useContentPartText = () => {
789
772
 
790
773
  // src/primitive-hooks/message/useMessageIf.tsx
791
774
  var useMessageIf = (props) => {
792
- const messageStore = useMessageStore();
775
+ const messageRuntime = useMessageRuntime();
793
776
  const messageUtilsStore = useMessageUtilsStore();
794
777
  return useCombinedStore(
795
- [messageStore, messageUtilsStore],
778
+ [messageRuntime, messageUtilsStore],
796
779
  ({
797
780
  role,
798
781
  attachments,
@@ -847,11 +830,9 @@ import { useCallback as useCallback15 } from "react";
847
830
  var useThreadScrollToBottom = () => {
848
831
  const isAtBottom = useThreadViewport((s) => s.isAtBottom);
849
832
  const threadViewportStore = useThreadViewportStore();
850
- const threadComposerStore = useThreadComposerStore();
851
833
  const handleScrollToBottom = useCallback15(() => {
852
834
  threadViewportStore.getState().scrollToBottom();
853
- threadComposerStore.getState().focus();
854
- }, [threadViewportStore, threadComposerStore]);
835
+ }, [threadViewportStore]);
855
836
  if (isAtBottom) return null;
856
837
  return handleScrollToBottom;
857
838
  };
@@ -862,20 +843,16 @@ var useThreadSuggestion = ({
862
843
  prompt,
863
844
  autoSend
864
845
  }) => {
865
- const threadStore = useThreadStore();
866
- const composerStore = useThreadComposerStore();
867
- const append = useAppendMessage();
846
+ const threadRuntime = useThreadRuntime();
868
847
  const disabled = useThread((t) => t.isDisabled);
869
848
  const callback = useCallback16(() => {
870
- const thread = threadStore.getState();
871
- const composer = composerStore.getState();
872
- if (autoSend && !thread.isRunning) {
873
- append(prompt);
874
- composer.setText("");
849
+ if (autoSend && !threadRuntime.getState().isRunning) {
850
+ threadRuntime.append(prompt);
851
+ threadRuntime.composer.setText("");
875
852
  } else {
876
- composer.setText(prompt);
853
+ threadRuntime.composer.setText(prompt);
877
854
  }
878
- }, [threadStore, composerStore, autoSend, append, prompt]);
855
+ }, [threadRuntime, autoSend, prompt]);
879
856
  if (disabled) return null;
880
857
  return callback;
881
858
  };
@@ -903,11 +880,11 @@ var useActionBarFloatStatus = ({
903
880
  autohide,
904
881
  autohideFloat
905
882
  }) => {
906
- const threadStore = useThreadStore();
907
- const messageStore = useMessageStore();
883
+ const threadRuntime = useThreadRuntime();
884
+ const messageRuntime = useMessageRuntime();
908
885
  const messageUtilsStore = useMessageUtilsStore();
909
886
  return useCombinedStore(
910
- [threadStore, messageStore, messageUtilsStore],
887
+ [threadRuntime, messageRuntime, messageUtilsStore],
911
888
  (t, m, mu) => {
912
889
  if (hideWhenRunning && t.isRunning) return "hidden" /* Hidden */;
913
890
  const autohideEnabled = autohide === "always" || autohide === "not-last" && !m.isLast;
@@ -1107,46 +1084,44 @@ __export(assistantModal_exports, {
1107
1084
  });
1108
1085
 
1109
1086
  // src/primitives/assistantModal/AssistantModalRoot.tsx
1110
- import { useState as useState4 } from "react";
1087
+ import { useEffect as useEffect7, useState as useState4 } from "react";
1111
1088
  import * as PopoverPrimitive2 from "@radix-ui/react-popover";
1112
1089
  import { composeEventHandlers as composeEventHandlers6 } from "@radix-ui/primitive";
1113
1090
 
1114
- // src/utils/hooks/useOnComposerFocus.tsx
1115
- import { useCallbackRef } from "@radix-ui/react-use-callback-ref";
1116
- import { useEffect as useEffect7 } from "react";
1117
- var useOnComposerFocus = (callback) => {
1118
- const callbackRef = useCallbackRef(callback);
1119
- const threadComposerStore = useThreadComposerStore();
1120
- useEffect7(() => {
1121
- return threadComposerStore.getState().onFocus(() => {
1122
- callbackRef();
1123
- });
1124
- }, [threadComposerStore, callbackRef]);
1125
- };
1126
-
1127
1091
  // src/primitives/assistantModal/scope.tsx
1128
1092
  import * as PopoverPrimitive from "@radix-ui/react-popover";
1129
1093
  var usePopoverScope = PopoverPrimitive.createPopoverScope();
1130
1094
 
1131
1095
  // src/primitives/assistantModal/AssistantModalRoot.tsx
1132
1096
  import { jsx as jsx10 } from "react/jsx-runtime";
1133
- var useAssistantModalOpenState = (defaultOpen = false) => {
1097
+ var useAssistantModalOpenState = ({
1098
+ defaultOpen = false,
1099
+ unstable_openOnRunStart = true
1100
+ }) => {
1134
1101
  const state = useState4(defaultOpen);
1135
1102
  const [, setOpen] = state;
1136
- useOnComposerFocus(() => {
1137
- setOpen(true);
1138
- });
1103
+ const threadRuntime = useThreadRuntime();
1104
+ useEffect7(() => {
1105
+ if (!unstable_openOnRunStart) return void 0;
1106
+ return threadRuntime.unstable_on("run-start", () => {
1107
+ setOpen(true);
1108
+ });
1109
+ }, [unstable_openOnRunStart]);
1139
1110
  return state;
1140
1111
  };
1141
1112
  var AssistantModalPrimitiveRoot = ({
1142
1113
  __scopeAssistantModal,
1143
1114
  defaultOpen,
1115
+ unstable_openOnRunStart,
1144
1116
  open,
1145
1117
  onOpenChange,
1146
1118
  ...rest
1147
1119
  }) => {
1148
1120
  const scope = usePopoverScope(__scopeAssistantModal);
1149
- const [modalOpen, setOpen] = useAssistantModalOpenState(defaultOpen);
1121
+ const [modalOpen, setOpen] = useAssistantModalOpenState({
1122
+ defaultOpen,
1123
+ unstable_openOnRunStart
1124
+ });
1150
1125
  return /* @__PURE__ */ jsx10(
1151
1126
  PopoverPrimitive2.Root,
1152
1127
  {
@@ -1497,7 +1472,7 @@ import {
1497
1472
 
1498
1473
  // src/utils/smooth/useSmooth.tsx
1499
1474
  import { useEffect as useEffect9, useMemo as useMemo5, useRef as useRef2, useState as useState7 } from "react";
1500
- import { useCallbackRef as useCallbackRef2 } from "@radix-ui/react-use-callback-ref";
1475
+ import { useCallbackRef } from "@radix-ui/react-use-callback-ref";
1501
1476
 
1502
1477
  // src/utils/smooth/SmoothContext.tsx
1503
1478
  import {
@@ -1599,7 +1574,7 @@ var useSmooth = (state, smooth = false) => {
1599
1574
  const idRef = useRef2(id);
1600
1575
  const [displayedText, setDisplayedText] = useState7(text);
1601
1576
  const smoothStatusStore = useSmoothStatusStore({ optional: true });
1602
- const setText = useCallbackRef2((text2) => {
1577
+ const setText = useCallbackRef((text2) => {
1603
1578
  setDisplayedText(text2);
1604
1579
  if (smoothStatusStore) {
1605
1580
  writableStore(smoothStatusStore).setState(
@@ -1837,7 +1812,7 @@ var METHOD_NOT_SUPPORTED = () => {
1837
1812
  throw new Error("Composer is not available");
1838
1813
  };
1839
1814
  var EMPTY_ARRAY = Object.freeze([]);
1840
- var getThreadComposerState = (runtime, focus, onFocus) => {
1815
+ var getThreadComposerState = (runtime) => {
1841
1816
  return Object.freeze({
1842
1817
  type: "thread",
1843
1818
  isEditing: runtime?.isEditing ?? false,
@@ -1851,8 +1826,6 @@ var getThreadComposerState = (runtime, focus, onFocus) => {
1851
1826
  // edit: beginEdit,
1852
1827
  send: runtime?.send.bind(runtime) ?? METHOD_NOT_SUPPORTED,
1853
1828
  cancel: runtime?.cancel.bind(runtime) ?? METHOD_NOT_SUPPORTED,
1854
- focus,
1855
- onFocus,
1856
1829
  reset: runtime?.reset.bind(runtime) ?? METHOD_NOT_SUPPORTED,
1857
1830
  addAttachment: runtime?.addAttachment.bind(runtime) ?? METHOD_NOT_SUPPORTED,
1858
1831
  removeAttachment: runtime?.removeAttachment.bind(runtime) ?? METHOD_NOT_SUPPORTED
@@ -1969,11 +1942,7 @@ var ThreadComposerRuntimeImpl = class extends ComposerRuntimeImpl {
1969
1942
  _getState;
1970
1943
  constructor(core) {
1971
1944
  const stateBinding = new LazyMemoizeSubject({
1972
- getState: () => getThreadComposerState(
1973
- core.getState(),
1974
- this.focus.bind(this),
1975
- this.onFocus.bind(this)
1976
- ),
1945
+ getState: () => getThreadComposerState(core.getState()),
1977
1946
  subscribe: (callback) => core.subscribe(callback)
1978
1947
  });
1979
1948
  super({
@@ -1988,21 +1957,6 @@ var ThreadComposerRuntimeImpl = class extends ComposerRuntimeImpl {
1988
1957
  getState() {
1989
1958
  return this._getState();
1990
1959
  }
1991
- // TODO replace with events
1992
- _focusListeners = /* @__PURE__ */ new Set();
1993
- /**
1994
- * @deprecated This feature is being removed in 0.6.0. Submit feedback if you need it.
1995
- */
1996
- focus() {
1997
- this._focusListeners.forEach((callback) => callback());
1998
- }
1999
- /**
2000
- * @deprecated This feature is being removed in 0.6.0. Submit feedback if you need it.
2001
- */
2002
- onFocus(callback) {
2003
- this._focusListeners.add(callback);
2004
- return () => this._focusListeners.delete(callback);
2005
- }
2006
1960
  getAttachmentByIndex(idx) {
2007
1961
  return new ThreadComposerAttachmentRuntimeImpl(
2008
1962
  new ShallowMemoizeSubject({
@@ -2080,6 +2034,9 @@ var NestedSubscriptionSubject = class extends BaseSubject {
2080
2034
  getState() {
2081
2035
  return this.binding.getState();
2082
2036
  }
2037
+ outerSubscribe(callback) {
2038
+ return this.binding.subscribe(callback);
2039
+ }
2083
2040
  _connect() {
2084
2041
  const callback = () => {
2085
2042
  this.notifySubscribers();
@@ -2094,7 +2051,7 @@ var NestedSubscriptionSubject = class extends BaseSubject {
2094
2051
  innerUnsubscribe = this.binding.getState()?.subscribe(callback);
2095
2052
  callback();
2096
2053
  };
2097
- const outerUnsubscribe = this.binding.subscribe(onRuntimeUpdate);
2054
+ const outerUnsubscribe = this.outerSubscribe(onRuntimeUpdate);
2098
2055
  return () => {
2099
2056
  outerUnsubscribe?.();
2100
2057
  innerUnsubscribe?.();
@@ -2265,8 +2222,7 @@ var MessageContentPartComponent = ({
2265
2222
  tools: { by_name = {}, Fallback: Fallback2 = void 0 } = {}
2266
2223
  } = {}
2267
2224
  }) => {
2268
- const messageStore = useMessageStore();
2269
- const threadRuntime = useThreadRuntime();
2225
+ const contentPartRuntime = useContentPartRuntime();
2270
2226
  const part = useContentPart();
2271
2227
  const type = part.type;
2272
2228
  switch (type) {
@@ -2287,12 +2243,7 @@ var MessageContentPartComponent = ({
2287
2243
  return /* @__PURE__ */ jsx23(UI, { ...part, part });
2288
2244
  case "tool-call": {
2289
2245
  const Tool = by_name[part.toolName] || Fallback2;
2290
- const addResult = (result) => threadRuntime.addToolResult({
2291
- messageId: messageStore.getState().id,
2292
- toolName: part.toolName,
2293
- toolCallId: part.toolCallId,
2294
- result
2295
- });
2246
+ const addResult = (result) => contentPartRuntime.addToolResult(result);
2296
2247
  return /* @__PURE__ */ jsx23(ToolUIDisplay, { ...part, part, UI: Tool, addResult });
2297
2248
  }
2298
2249
  default:
@@ -2475,11 +2426,24 @@ import { Slot } from "@radix-ui/react-slot";
2475
2426
  import {
2476
2427
  forwardRef as forwardRef18,
2477
2428
  useCallback as useCallback20,
2478
- useEffect as useEffect11,
2429
+ useEffect as useEffect12,
2479
2430
  useRef as useRef3
2480
2431
  } from "react";
2481
2432
  import TextareaAutosize from "react-textarea-autosize";
2482
2433
  import { useEscapeKeydown as useEscapeKeydown2 } from "@radix-ui/react-use-escape-keydown";
2434
+
2435
+ // src/utils/hooks/useOnScrollToBottom.tsx
2436
+ import { useCallbackRef as useCallbackRef2 } from "@radix-ui/react-use-callback-ref";
2437
+ import { useEffect as useEffect11 } from "react";
2438
+ var useOnScrollToBottom = (callback) => {
2439
+ const callbackRef = useCallbackRef2(callback);
2440
+ const onScrollToBottom = useThreadViewport((vp) => vp.onScrollToBottom);
2441
+ useEffect11(() => {
2442
+ return onScrollToBottom(callbackRef);
2443
+ }, [onScrollToBottom, callbackRef]);
2444
+ };
2445
+
2446
+ // src/primitives/composer/ComposerInput.tsx
2483
2447
  import { jsx as jsx28 } from "react/jsx-runtime";
2484
2448
  var ComposerPrimitiveInput = forwardRef18(
2485
2449
  ({
@@ -2490,10 +2454,13 @@ var ComposerPrimitiveInput = forwardRef18(
2490
2454
  onKeyDown,
2491
2455
  submitOnEnter = true,
2492
2456
  cancelOnEscape = true,
2457
+ unstable_focusOnRunStart = true,
2458
+ unstable_focusOnScrollToBottom = true,
2459
+ unstable_focusOnThreadSwitched = true,
2493
2460
  ...rest
2494
2461
  }, forwardedRef) => {
2495
- const threadStore = useThreadStore();
2496
- const composerStore = useComposerStore();
2462
+ const threadRuntime = useThreadRuntime();
2463
+ const composerRuntime = useComposerRuntime();
2497
2464
  const value = useComposer((c) => {
2498
2465
  if (!c.isEditing) return "";
2499
2466
  return c.text;
@@ -2504,9 +2471,8 @@ var ComposerPrimitiveInput = forwardRef18(
2504
2471
  const ref = useComposedRefs2(forwardedRef, textareaRef);
2505
2472
  useEscapeKeydown2((e) => {
2506
2473
  if (!cancelOnEscape) return;
2507
- const composer = composerStore.getState();
2508
- if (composer.canCancel) {
2509
- composer.cancel();
2474
+ if (composerRuntime.getState().canCancel) {
2475
+ composerRuntime.cancel();
2510
2476
  e.preventDefault();
2511
2477
  }
2512
2478
  });
@@ -2514,7 +2480,7 @@ var ComposerPrimitiveInput = forwardRef18(
2514
2480
  if (isDisabled || !submitOnEnter) return;
2515
2481
  if (e.nativeEvent.isComposing) return;
2516
2482
  if (e.key === "Enter" && e.shiftKey === false) {
2517
- const { isRunning } = threadStore.getState();
2483
+ const { isRunning } = threadRuntime.getState();
2518
2484
  if (!isRunning) {
2519
2485
  e.preventDefault();
2520
2486
  textareaRef.current?.closest("form")?.requestSubmit();
@@ -2522,7 +2488,7 @@ var ComposerPrimitiveInput = forwardRef18(
2522
2488
  }
2523
2489
  };
2524
2490
  const autoFocusEnabled = autoFocus && !isDisabled;
2525
- const focus = useCallback20(() => {
2491
+ const focus2 = useCallback20(() => {
2526
2492
  const textarea = textareaRef.current;
2527
2493
  if (!textarea || !autoFocusEnabled) return;
2528
2494
  textarea.focus({ preventScroll: true });
@@ -2531,12 +2497,22 @@ var ComposerPrimitiveInput = forwardRef18(
2531
2497
  textareaRef.current.value.length
2532
2498
  );
2533
2499
  }, [autoFocusEnabled]);
2534
- useEffect11(() => focus(), [focus]);
2535
- useOnComposerFocus(() => {
2536
- if (composerStore.getState().type === "thread") {
2537
- focus();
2500
+ useEffect12(() => focus2(), [focus2]);
2501
+ useOnScrollToBottom(() => {
2502
+ if (composerRuntime.type === "thread" && unstable_focusOnScrollToBottom) {
2503
+ focus2();
2538
2504
  }
2539
2505
  });
2506
+ useEffect12(() => {
2507
+ if (composerRuntime.type !== "thread" || !unstable_focusOnRunStart)
2508
+ return void 0;
2509
+ return threadRuntime.unstable_on("run-start", focus2);
2510
+ }, [unstable_focusOnRunStart]);
2511
+ useEffect12(() => {
2512
+ if (composerRuntime.type !== "thread" || !unstable_focusOnThreadSwitched)
2513
+ return void 0;
2514
+ return threadRuntime.unstable_on("switched-to", focus2);
2515
+ }, [unstable_focusOnThreadSwitched]);
2540
2516
  return /* @__PURE__ */ jsx28(
2541
2517
  Component,
2542
2518
  {
@@ -2546,9 +2522,8 @@ var ComposerPrimitiveInput = forwardRef18(
2546
2522
  ref,
2547
2523
  disabled: isDisabled,
2548
2524
  onChange: composeEventHandlers9(onChange, (e) => {
2549
- const composerState = composerStore.getState();
2550
- if (!composerState.isEditing) return;
2551
- return composerState.setText(e.target.value);
2525
+ if (!composerRuntime.getState().isEditing) return;
2526
+ return composerRuntime.setText(e.target.value);
2552
2527
  }),
2553
2528
  onKeyDown: composeEventHandlers9(onKeyDown, handleKeyPress)
2554
2529
  }
@@ -2690,7 +2665,7 @@ import { forwardRef as forwardRef20 } from "react";
2690
2665
 
2691
2666
  // src/primitive-hooks/thread/useThreadViewportAutoScroll.tsx
2692
2667
  import { useComposedRefs as useComposedRefs3 } from "@radix-ui/react-compose-refs";
2693
- import { useRef as useRef4 } from "react";
2668
+ import { useEffect as useEffect13, useRef as useRef4 } from "react";
2694
2669
 
2695
2670
  // src/utils/hooks/useOnResizeContent.tsx
2696
2671
  import { useCallbackRef as useCallbackRef3 } from "@radix-ui/react-use-callback-ref";
@@ -2732,22 +2707,10 @@ var useOnResizeContent = (callback) => {
2732
2707
  return useManagedRef(refCallback);
2733
2708
  };
2734
2709
 
2735
- // src/utils/hooks/useOnScrollToBottom.tsx
2736
- import { useCallbackRef as useCallbackRef4 } from "@radix-ui/react-use-callback-ref";
2737
- import { useEffect as useEffect12 } from "react";
2738
- var useOnScrollToBottom = (callback) => {
2739
- const callbackRef = useCallbackRef4(callback);
2740
- const threadViewportStore = useThreadViewportStore();
2741
- useEffect12(() => {
2742
- return threadViewportStore.getState().onScrollToBottom(() => {
2743
- callbackRef();
2744
- });
2745
- }, [threadViewportStore, callbackRef]);
2746
- };
2747
-
2748
2710
  // src/primitive-hooks/thread/useThreadViewportAutoScroll.tsx
2749
2711
  var useThreadViewportAutoScroll = ({
2750
- autoScroll = true
2712
+ autoScroll = true,
2713
+ unstable_scrollToBottomOnRunStart = true
2751
2714
  }) => {
2752
2715
  const divRef = useRef4(null);
2753
2716
  const threadViewportStore = useThreadViewportStore();
@@ -2793,6 +2756,11 @@ var useThreadViewportAutoScroll = ({
2793
2756
  useOnScrollToBottom(() => {
2794
2757
  scrollToBottom("auto");
2795
2758
  });
2759
+ const threadRuntime = useThreadRuntime();
2760
+ useEffect13(() => {
2761
+ if (!unstable_scrollToBottomOnRunStart) return void 0;
2762
+ return threadRuntime.unstable_on("run-start", focus);
2763
+ }, [unstable_scrollToBottomOnRunStart]);
2796
2764
  return autoScrollRef;
2797
2765
  };
2798
2766
 
@@ -2811,7 +2779,7 @@ ThreadPrimitiveViewport.displayName = "ThreadPrimitive.Viewport";
2811
2779
  import { memo as memo5, useMemo as useMemo10 } from "react";
2812
2780
 
2813
2781
  // src/context/providers/MessageRuntimeProvider.tsx
2814
- import { useEffect as useEffect13, useState as useState9 } from "react";
2782
+ import { useEffect as useEffect14, useState as useState9 } from "react";
2815
2783
  import { create as create10 } from "zustand";
2816
2784
 
2817
2785
  // src/context/stores/MessageUtils.ts
@@ -2833,14 +2801,14 @@ var makeMessageUtilsStore = () => create9((set) => {
2833
2801
  import { jsx as jsx32 } from "react/jsx-runtime";
2834
2802
  var useMessageRuntimeStore = (runtime) => {
2835
2803
  const [store] = useState9(() => create10(() => runtime));
2836
- useEffect13(() => {
2804
+ useEffect14(() => {
2837
2805
  writableStore(store).setState(runtime, true);
2838
2806
  }, [runtime, store]);
2839
2807
  return store;
2840
2808
  };
2841
2809
  var useMessageStore2 = (runtime) => {
2842
2810
  const [store] = useState9(() => create10(() => runtime.getState()));
2843
- useEffect13(() => {
2811
+ useEffect14(() => {
2844
2812
  const updateState = () => writableStore(store).setState(runtime.getState(), true);
2845
2813
  updateState();
2846
2814
  return runtime.subscribe(updateState);
@@ -2854,7 +2822,7 @@ var useMessageUtilsStore2 = () => {
2854
2822
  var useEditComposerStore2 = (useMessageRuntime2) => {
2855
2823
  const runtime = useMessageRuntime2.getState().composer;
2856
2824
  const [store] = useState9(() => create10(() => runtime.getState()));
2857
- useEffect13(() => {
2825
+ useEffect14(() => {
2858
2826
  const updateState = () => writableStore(store).setState(runtime.getState());
2859
2827
  updateState();
2860
2828
  return runtime.subscribe(updateState);
@@ -3588,6 +3556,7 @@ var ThreadRuntimeImpl = class {
3588
3556
  this._threadBinding = {
3589
3557
  getState: () => threadBinding.getState(),
3590
3558
  getStateState: () => stateBinding.getState(),
3559
+ outerSubscribe: (callback) => threadBinding.outerSubscribe(callback),
3591
3560
  subscribe: (callback) => threadBinding.subscribe(callback)
3592
3561
  };
3593
3562
  }
@@ -3700,6 +3669,20 @@ var ThreadRuntimeImpl = class {
3700
3669
  this._threadBinding
3701
3670
  );
3702
3671
  }
3672
+ _eventListenerNestedSubscriptions = /* @__PURE__ */ new Map();
3673
+ unstable_on(event, callback) {
3674
+ let subject = this._eventListenerNestedSubscriptions.get(event);
3675
+ if (!subject) {
3676
+ subject = new NestedSubscriptionSubject({
3677
+ getState: () => ({
3678
+ subscribe: (callback2) => this._threadBinding.getState().unstable_on(event, callback2)
3679
+ }),
3680
+ subscribe: (callback2) => this._threadBinding.outerSubscribe(callback2)
3681
+ });
3682
+ this._eventListenerNestedSubscriptions.set(event, subject);
3683
+ }
3684
+ return subject.subscribe(callback);
3685
+ }
3703
3686
  };
3704
3687
 
3705
3688
  // src/api/AssistantRuntime.ts
@@ -3944,17 +3927,13 @@ function assistantDecoderStream() {
3944
3927
  }
3945
3928
  case "c" /* ToolCallDelta */: {
3946
3929
  const { toolCallId, argsTextDelta } = value;
3947
- if (currentToolCall?.id !== toolCallId) {
3948
- throw new Error(
3949
- `Received tool call delta for unknown tool call "${toolCallId}".`
3950
- );
3951
- }
3930
+ const toolName = toolCallNames.get(toolCallId);
3952
3931
  currentToolCall.argsText += argsTextDelta;
3953
3932
  controller.enqueue({
3954
3933
  type: "tool-call-delta",
3955
3934
  toolCallType: "function",
3956
- toolCallId: currentToolCall.id,
3957
- toolName: currentToolCall.name,
3935
+ toolCallId,
3936
+ toolName,
3958
3937
  argsTextDelta
3959
3938
  });
3960
3939
  break;
@@ -4163,18 +4142,23 @@ var BaseThreadRuntimeCore = class {
4163
4142
  this.repository.getMessage(messageId)
4164
4143
  )
4165
4144
  );
4166
- this.notifySubscribers();
4145
+ this._notifySubscribers();
4167
4146
  }
4168
4147
  getBranches(messageId) {
4169
4148
  return this.repository.getBranches(messageId);
4170
4149
  }
4171
4150
  switchToBranch(branchId) {
4172
4151
  this.repository.switchToBranch(branchId);
4173
- this.notifySubscribers();
4152
+ this._notifySubscribers();
4174
4153
  }
4175
- notifySubscribers() {
4154
+ _notifySubscribers() {
4176
4155
  for (const callback of this._subscriptions) callback();
4177
4156
  }
4157
+ _notifyEventSubscribers(event) {
4158
+ const subscribers = this._eventSubscribers.get(event);
4159
+ if (!subscribers) return;
4160
+ for (const callback of subscribers) callback();
4161
+ }
4178
4162
  subscribe(callback) {
4179
4163
  this._subscriptions.add(callback);
4180
4164
  return () => this._subscriptions.delete(callback);
@@ -4189,7 +4173,7 @@ var BaseThreadRuntimeCore = class {
4189
4173
  const { message } = this.repository.getMessage(messageId);
4190
4174
  adapter.submit({ message, type });
4191
4175
  this._submittedFeedback[messageId] = { type };
4192
- this.notifySubscribers();
4176
+ this._notifySubscribers();
4193
4177
  }
4194
4178
  _stopSpeaking;
4195
4179
  speech;
@@ -4206,10 +4190,10 @@ var BaseThreadRuntimeCore = class {
4206
4190
  } else {
4207
4191
  this.speech = { messageId, status: utterance.status };
4208
4192
  }
4209
- this.notifySubscribers();
4193
+ this._notifySubscribers();
4210
4194
  });
4211
4195
  this.speech = { messageId, status: utterance.status };
4212
- this.notifySubscribers();
4196
+ this._notifySubscribers();
4213
4197
  this._stopSpeaking = () => {
4214
4198
  utterance.cancel();
4215
4199
  unsub();
@@ -4220,14 +4204,27 @@ var BaseThreadRuntimeCore = class {
4220
4204
  stopSpeaking() {
4221
4205
  if (!this._stopSpeaking) throw new Error("No message is being spoken");
4222
4206
  this._stopSpeaking();
4223
- this.notifySubscribers();
4207
+ this._notifySubscribers();
4224
4208
  }
4225
4209
  export() {
4226
4210
  return this.repository.export();
4227
4211
  }
4228
4212
  import(data) {
4229
4213
  this.repository.import(data);
4230
- this.notifySubscribers();
4214
+ this._notifySubscribers();
4215
+ }
4216
+ _eventSubscribers = /* @__PURE__ */ new Map();
4217
+ unstable_on(event, callback) {
4218
+ const subscribers = this._eventSubscribers.get(event);
4219
+ if (!subscribers) {
4220
+ this._eventSubscribers.set(event, /* @__PURE__ */ new Set([callback]));
4221
+ } else {
4222
+ subscribers.add(callback);
4223
+ }
4224
+ return () => {
4225
+ const subscribers2 = this._eventSubscribers.get(event);
4226
+ subscribers2.delete(callback);
4227
+ };
4231
4228
  }
4232
4229
  };
4233
4230
 
@@ -4289,7 +4286,7 @@ var LocalThreadRuntimeCore = class extends BaseThreadRuntimeCore {
4289
4286
  this.capabilities.feedback = canFeedback;
4290
4287
  hasUpdates = true;
4291
4288
  }
4292
- if (hasUpdates) this.notifySubscribers();
4289
+ if (hasUpdates) this._notifySubscribers();
4293
4290
  }
4294
4291
  async append(message) {
4295
4292
  const newMessage = fromCoreMessage(message, {
@@ -4300,7 +4297,7 @@ var LocalThreadRuntimeCore = class extends BaseThreadRuntimeCore {
4300
4297
  await this.startRun(newMessage.id);
4301
4298
  } else {
4302
4299
  this.repository.resetHead(newMessage.id);
4303
- this.notifySubscribers();
4300
+ this._notifySubscribers();
4304
4301
  }
4305
4302
  }
4306
4303
  async startRun(parentId) {
@@ -4313,6 +4310,7 @@ var LocalThreadRuntimeCore = class extends BaseThreadRuntimeCore {
4313
4310
  content: [],
4314
4311
  createdAt: /* @__PURE__ */ new Date()
4315
4312
  };
4313
+ this._notifyEventSubscribers("run-start");
4316
4314
  do {
4317
4315
  message = await this.performRoundtrip(parentId, message);
4318
4316
  } while (shouldContinue(message));
@@ -4344,7 +4342,7 @@ var LocalThreadRuntimeCore = class extends BaseThreadRuntimeCore {
4344
4342
  } : void 0
4345
4343
  };
4346
4344
  this.repository.addOrUpdateMessage(parentId, message);
4347
- this.notifySubscribers();
4345
+ this._notifySubscribers();
4348
4346
  };
4349
4347
  const maxSteps = this.options.maxSteps ? this.options.maxSteps : (this.options.maxToolRoundtrips ?? 1) + 1;
4350
4348
  const steps = message.metadata?.steps?.length ?? 0;
@@ -4455,6 +4453,7 @@ var LocalRuntimeCore = class extends BaseAssistantRuntimeCore {
4455
4453
  this.thread.adapter,
4456
4454
  options
4457
4455
  );
4456
+ this.thread._notifyEventSubscribers("switched-to");
4458
4457
  }
4459
4458
  switchToThread(threadId) {
4460
4459
  if (threadId !== null) {
@@ -4503,7 +4502,7 @@ var useLocalRuntime = (adapter, options = {}) => {
4503
4502
  };
4504
4503
 
4505
4504
  // src/runtimes/external-store/useExternalStoreRuntime.tsx
4506
- import { useEffect as useEffect14, useMemo as useMemo12, useState as useState12 } from "react";
4505
+ import { useEffect as useEffect15, useMemo as useMemo12, useState as useState12 } from "react";
4507
4506
 
4508
4507
  // src/runtimes/external-store/getExternalStoreMessage.tsx
4509
4508
  var symbolInnerMessage = Symbol("innerMessage");
@@ -4676,7 +4675,7 @@ var ExternalStoreThreadRuntimeCore = class extends BaseThreadRuntimeCore {
4676
4675
  if (oldStore.convertMessage !== store.convertMessage) {
4677
4676
  this._converter = new ThreadMessageConverter();
4678
4677
  } else if (oldStore.isRunning === store.isRunning && oldStore.messages === store.messages) {
4679
- this.notifySubscribers();
4678
+ this._notifySubscribers();
4680
4679
  return;
4681
4680
  }
4682
4681
  }
@@ -4716,7 +4715,7 @@ var ExternalStoreThreadRuntimeCore = class extends BaseThreadRuntimeCore {
4716
4715
  this.assistantOptimisticId ?? messages2.at(-1)?.id ?? null
4717
4716
  );
4718
4717
  this._messages = this.repository.getMessages();
4719
- this.notifySubscribers();
4718
+ this._notifySubscribers();
4720
4719
  }
4721
4720
  switchToBranch(branchId) {
4722
4721
  if (!this._store.setMessages)
@@ -4755,7 +4754,7 @@ var ExternalStoreThreadRuntimeCore = class extends BaseThreadRuntimeCore {
4755
4754
  }
4756
4755
  messages2 = this.repository.getMessages();
4757
4756
  } else {
4758
- this.notifySubscribers();
4757
+ this._notifySubscribers();
4759
4758
  }
4760
4759
  setTimeout(() => {
4761
4760
  this.updateMessages(messages2);
@@ -4798,6 +4797,7 @@ var ExternalStoreRuntimeCore = class extends BaseAssistantRuntimeCore {
4798
4797
  }
4799
4798
  );
4800
4799
  await this.thread.store.onSwitchToNewThread();
4800
+ this.thread._notifyEventSubscribers("switched-to");
4801
4801
  }
4802
4802
  async switchToThread(threadId) {
4803
4803
  if (threadId !== null) {
@@ -4811,7 +4811,8 @@ var ExternalStoreRuntimeCore = class extends BaseAssistantRuntimeCore {
4811
4811
  // ignore messages until rerender
4812
4812
  }
4813
4813
  );
4814
- this.thread.store.onSwitchToThread(threadId);
4814
+ await this.thread.store.onSwitchToThread(threadId);
4815
+ this.thread._notifyEventSubscribers("switched-to");
4815
4816
  } else {
4816
4817
  this.switchToNewThread();
4817
4818
  }
@@ -4821,7 +4822,7 @@ var ExternalStoreRuntimeCore = class extends BaseAssistantRuntimeCore {
4821
4822
  // src/runtimes/external-store/useExternalStoreRuntime.tsx
4822
4823
  var useExternalStoreRuntime = (store) => {
4823
4824
  const [runtime] = useState12(() => new ExternalStoreRuntimeCore(store));
4824
- useEffect14(() => {
4825
+ useEffect15(() => {
4825
4826
  runtime.thread.store = store;
4826
4827
  });
4827
4828
  return useMemo12(
@@ -5593,7 +5594,7 @@ CircleStopIcon.displayName = "CircleStopIcon";
5593
5594
  // src/ui/attachment.tsx
5594
5595
  import {
5595
5596
  forwardRef as forwardRef28,
5596
- useEffect as useEffect15,
5597
+ useEffect as useEffect16,
5597
5598
  useState as useState14
5598
5599
  } from "react";
5599
5600
  import { CircleXIcon, FileIcon } from "lucide-react";
@@ -5638,7 +5639,7 @@ var AttachmentRoot = withDefaults(attachment_exports.Root, {
5638
5639
  AttachmentRoot.displayName = "AttachmentRoot";
5639
5640
  var useFileSrc = (file) => {
5640
5641
  const [src, setSrc] = useState14(void 0);
5641
- useEffect15(() => {
5642
+ useEffect16(() => {
5642
5643
  if (!file) {
5643
5644
  setSrc(void 0);
5644
5645
  return;