@assistant-ui/react 0.0.16 → 0.0.17

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.d.mts CHANGED
@@ -3,6 +3,7 @@ import { FC, ReactNode, PropsWithChildren, ComponentType } from 'react';
3
3
  import { TextareaAutosizeProps } from 'react-textarea-autosize';
4
4
  import { UseChatHelpers, UseAssistantHelpers } from 'ai/react';
5
5
  import * as react_jsx_runtime from 'react/jsx-runtime';
6
+ import { Message } from 'ai';
6
7
  import { UseBoundStore, StoreApi } from 'zustand';
7
8
 
8
9
  declare const ThreadRoot: react.ForwardRefExoticComponent<Pick<Omit<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
@@ -145,6 +146,7 @@ type UserMessage = BaseMessage & {
145
146
  type AssistantMessage = BaseMessage & {
146
147
  role: "assistant";
147
148
  content: AssistantContentPart[];
149
+ status: "in_progress" | "done" | "error";
148
150
  };
149
151
  type AppendMessage = {
150
152
  parentId: string | null;
@@ -199,14 +201,14 @@ type MessageContentProps = {
199
201
  };
200
202
  declare const MessageContent: FC<MessageContentProps>;
201
203
 
202
- declare const MessageLoading: react.ForwardRefExoticComponent<Pick<Omit<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
204
+ declare const MessageInProgress: react.ForwardRefExoticComponent<Pick<Omit<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
203
205
  ref?: ((instance: HTMLDivElement | null) => void) | react.RefObject<HTMLDivElement> | null | undefined;
204
206
  } & {
205
207
  asChild?: boolean;
206
208
  }, "key" | keyof react.HTMLAttributes<HTMLDivElement> | "asChild"> & react.RefAttributes<HTMLDivElement>>;
207
209
 
208
210
  declare namespace index$3 {
209
- export { MessageContent as Content, MessageIf as If, MessageLoading as Loading, MessageProvider as Provider, MessageRoot as Root };
211
+ export { MessageContent as Content, MessageIf as If, MessageInProgress as InProgress, MessageProvider as Provider, MessageRoot as Root };
210
212
  }
211
213
 
212
214
  declare const BranchPickerNext: react.ForwardRefExoticComponent<Pick<Omit<react.DetailedHTMLProps<react.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref"> & {
@@ -274,14 +276,14 @@ declare namespace index$1 {
274
276
 
275
277
  type ContentPartProviderProps = PropsWithChildren<{
276
278
  part: ThreadMessage["content"][number];
277
- isLoading: boolean;
279
+ status: "in_progress" | "done" | "error";
278
280
  }>;
279
281
  declare const ContentPartProvider: FC<ContentPartProviderProps>;
280
282
 
281
- declare const ContentPartLoadingIndicator: FC;
283
+ declare const ContentPartInProgressIndicator: FC;
282
284
 
283
285
  declare namespace index {
284
- export { ContentPartLoadingIndicator as LoadingIndicator, ContentPartProvider as Provider };
286
+ export { ContentPartInProgressIndicator as InProgressIndicator, ContentPartProvider as Provider };
285
287
  }
286
288
 
287
289
  type VercelAIAssistantProviderProps = PropsWithChildren<{
@@ -310,13 +312,16 @@ type RSCMessageConverter<T> = {
310
312
  type VercelRSCAssistantProviderProps<T = VercelRSCMessage> = VercelRSCAssistantProviderBaseProps<T> & (T extends VercelRSCMessage ? object : RSCMessageConverter<T>);
311
313
  declare const VercelRSCAssistantProvider: <T extends WeakKey = VercelRSCMessage>({ children, convertMessage, messages: vercelMessages, append: appendCallback, edit, reload, }: VercelRSCAssistantProviderProps<T>) => react_jsx_runtime.JSX.Element;
312
314
 
315
+ declare const getVercelMessage: (message: ThreadMessage) => Message | undefined;
316
+ declare const getVercelRSCMessage: <T>(message: ThreadMessage) => T | undefined;
317
+
313
318
  type MessageState = {
314
319
  message: ThreadMessage;
315
320
  parentId: string | null;
316
321
  branches: string[];
317
322
  isLast: boolean;
318
- loadingIndicator: ReactNode | null;
319
- setLoadingIndicator: (value: ReactNode | null) => void;
323
+ inProgressIndicator: ReactNode | null;
324
+ setInProgressIndicator: (value: ReactNode | null) => void;
320
325
  isCopied: boolean;
321
326
  setIsCopied: (value: boolean) => void;
322
327
  isHovering: boolean;
@@ -341,4 +346,4 @@ declare const useGoToNextBranch: () => (() => void) | null;
341
346
 
342
347
  declare const useGoToPreviousBranch: () => (() => void) | null;
343
348
 
344
- export { index$1 as ActionBarPrimitive, type AppendContentPart, type AppendMessage, index$2 as BranchPickerPrimitive, index$4 as ComposerPrimitive, index as ContentPartPrimitive, type ImageContentPart, index$3 as MessagePrimitive, type VercelRSCMessage as RSCMessage, type TextContentPart, index$5 as ThreadPrimitive, VercelAIAssistantProvider, type VercelAIAssistantProviderProps, VercelRSCAssistantProvider, type VercelRSCAssistantProviderProps, useMessageContext as unstable_useMessageContext, useBeginMessageEdit, useCopyMessage, useGoToNextBranch, useGoToPreviousBranch, useReloadMessage };
349
+ export { index$1 as ActionBarPrimitive, type AppendContentPart, type AppendMessage, index$2 as BranchPickerPrimitive, index$4 as ComposerPrimitive, index as ContentPartPrimitive, type ImageContentPart, index$3 as MessagePrimitive, type VercelRSCMessage as RSCMessage, type TextContentPart, index$5 as ThreadPrimitive, VercelAIAssistantProvider, type VercelAIAssistantProviderProps, VercelRSCAssistantProvider, type VercelRSCAssistantProviderProps, getVercelMessage as unstable_getVercelMessage, getVercelRSCMessage as unstable_getVercelRSCMessage, useMessageContext as unstable_useMessageContext, useBeginMessageEdit, useCopyMessage, useGoToNextBranch, useGoToPreviousBranch, useReloadMessage };
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@ import { FC, ReactNode, PropsWithChildren, ComponentType } from 'react';
3
3
  import { TextareaAutosizeProps } from 'react-textarea-autosize';
4
4
  import { UseChatHelpers, UseAssistantHelpers } from 'ai/react';
5
5
  import * as react_jsx_runtime from 'react/jsx-runtime';
6
+ import { Message } from 'ai';
6
7
  import { UseBoundStore, StoreApi } from 'zustand';
7
8
 
8
9
  declare const ThreadRoot: react.ForwardRefExoticComponent<Pick<Omit<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
@@ -145,6 +146,7 @@ type UserMessage = BaseMessage & {
145
146
  type AssistantMessage = BaseMessage & {
146
147
  role: "assistant";
147
148
  content: AssistantContentPart[];
149
+ status: "in_progress" | "done" | "error";
148
150
  };
149
151
  type AppendMessage = {
150
152
  parentId: string | null;
@@ -199,14 +201,14 @@ type MessageContentProps = {
199
201
  };
200
202
  declare const MessageContent: FC<MessageContentProps>;
201
203
 
202
- declare const MessageLoading: react.ForwardRefExoticComponent<Pick<Omit<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
204
+ declare const MessageInProgress: react.ForwardRefExoticComponent<Pick<Omit<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
203
205
  ref?: ((instance: HTMLDivElement | null) => void) | react.RefObject<HTMLDivElement> | null | undefined;
204
206
  } & {
205
207
  asChild?: boolean;
206
208
  }, "key" | keyof react.HTMLAttributes<HTMLDivElement> | "asChild"> & react.RefAttributes<HTMLDivElement>>;
207
209
 
208
210
  declare namespace index$3 {
209
- export { MessageContent as Content, MessageIf as If, MessageLoading as Loading, MessageProvider as Provider, MessageRoot as Root };
211
+ export { MessageContent as Content, MessageIf as If, MessageInProgress as InProgress, MessageProvider as Provider, MessageRoot as Root };
210
212
  }
211
213
 
212
214
  declare const BranchPickerNext: react.ForwardRefExoticComponent<Pick<Omit<react.DetailedHTMLProps<react.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref"> & {
@@ -274,14 +276,14 @@ declare namespace index$1 {
274
276
 
275
277
  type ContentPartProviderProps = PropsWithChildren<{
276
278
  part: ThreadMessage["content"][number];
277
- isLoading: boolean;
279
+ status: "in_progress" | "done" | "error";
278
280
  }>;
279
281
  declare const ContentPartProvider: FC<ContentPartProviderProps>;
280
282
 
281
- declare const ContentPartLoadingIndicator: FC;
283
+ declare const ContentPartInProgressIndicator: FC;
282
284
 
283
285
  declare namespace index {
284
- export { ContentPartLoadingIndicator as LoadingIndicator, ContentPartProvider as Provider };
286
+ export { ContentPartInProgressIndicator as InProgressIndicator, ContentPartProvider as Provider };
285
287
  }
286
288
 
287
289
  type VercelAIAssistantProviderProps = PropsWithChildren<{
@@ -310,13 +312,16 @@ type RSCMessageConverter<T> = {
310
312
  type VercelRSCAssistantProviderProps<T = VercelRSCMessage> = VercelRSCAssistantProviderBaseProps<T> & (T extends VercelRSCMessage ? object : RSCMessageConverter<T>);
311
313
  declare const VercelRSCAssistantProvider: <T extends WeakKey = VercelRSCMessage>({ children, convertMessage, messages: vercelMessages, append: appendCallback, edit, reload, }: VercelRSCAssistantProviderProps<T>) => react_jsx_runtime.JSX.Element;
312
314
 
315
+ declare const getVercelMessage: (message: ThreadMessage) => Message | undefined;
316
+ declare const getVercelRSCMessage: <T>(message: ThreadMessage) => T | undefined;
317
+
313
318
  type MessageState = {
314
319
  message: ThreadMessage;
315
320
  parentId: string | null;
316
321
  branches: string[];
317
322
  isLast: boolean;
318
- loadingIndicator: ReactNode | null;
319
- setLoadingIndicator: (value: ReactNode | null) => void;
323
+ inProgressIndicator: ReactNode | null;
324
+ setInProgressIndicator: (value: ReactNode | null) => void;
320
325
  isCopied: boolean;
321
326
  setIsCopied: (value: boolean) => void;
322
327
  isHovering: boolean;
@@ -341,4 +346,4 @@ declare const useGoToNextBranch: () => (() => void) | null;
341
346
 
342
347
  declare const useGoToPreviousBranch: () => (() => void) | null;
343
348
 
344
- export { index$1 as ActionBarPrimitive, type AppendContentPart, type AppendMessage, index$2 as BranchPickerPrimitive, index$4 as ComposerPrimitive, index as ContentPartPrimitive, type ImageContentPart, index$3 as MessagePrimitive, type VercelRSCMessage as RSCMessage, type TextContentPart, index$5 as ThreadPrimitive, VercelAIAssistantProvider, type VercelAIAssistantProviderProps, VercelRSCAssistantProvider, type VercelRSCAssistantProviderProps, useMessageContext as unstable_useMessageContext, useBeginMessageEdit, useCopyMessage, useGoToNextBranch, useGoToPreviousBranch, useReloadMessage };
349
+ export { index$1 as ActionBarPrimitive, type AppendContentPart, type AppendMessage, index$2 as BranchPickerPrimitive, index$4 as ComposerPrimitive, index as ContentPartPrimitive, type ImageContentPart, index$3 as MessagePrimitive, type VercelRSCMessage as RSCMessage, type TextContentPart, index$5 as ThreadPrimitive, VercelAIAssistantProvider, type VercelAIAssistantProviderProps, VercelRSCAssistantProvider, type VercelRSCAssistantProviderProps, getVercelMessage as unstable_getVercelMessage, getVercelRSCMessage as unstable_getVercelRSCMessage, useMessageContext as unstable_useMessageContext, useBeginMessageEdit, useCopyMessage, useGoToNextBranch, useGoToPreviousBranch, useReloadMessage };
package/dist/index.js CHANGED
@@ -38,6 +38,8 @@ __export(src_exports, {
38
38
  ThreadPrimitive: () => thread_exports,
39
39
  VercelAIAssistantProvider: () => VercelAIAssistantProvider,
40
40
  VercelRSCAssistantProvider: () => VercelRSCAssistantProvider,
41
+ unstable_getVercelMessage: () => getVercelMessage,
42
+ unstable_getVercelRSCMessage: () => getVercelRSCMessage,
41
43
  unstable_useMessageContext: () => useMessageContext,
42
44
  useBeginMessageEdit: () => useBeginMessageEdit,
43
45
  useCopyMessage: () => useCopyMessage,
@@ -110,15 +112,15 @@ var import_react_primitive2 = require("@radix-ui/react-primitive");
110
112
  var import_react5 = require("react");
111
113
 
112
114
  // src/utils/hooks/useOnResizeContent.tsx
115
+ var import_react_use_callback_ref = require("@radix-ui/react-use-callback-ref");
113
116
  var import_react3 = require("react");
114
117
  var useOnResizeContent = (ref, callback) => {
115
- const callbackRef = (0, import_react3.useRef)(callback);
116
- callbackRef.current = callback;
118
+ const callbackRef = (0, import_react_use_callback_ref.useCallbackRef)(callback);
117
119
  (0, import_react3.useEffect)(() => {
118
120
  const el = ref.current;
119
121
  if (!el) return;
120
122
  const resizeObserver = new ResizeObserver(() => {
121
- callbackRef.current();
123
+ callbackRef();
122
124
  });
123
125
  const mutationObserver = new MutationObserver((mutations) => {
124
126
  for (const mutation of mutations) {
@@ -133,7 +135,7 @@ var useOnResizeContent = (ref, callback) => {
133
135
  }
134
136
  }
135
137
  }
136
- callbackRef.current();
138
+ callbackRef();
137
139
  });
138
140
  resizeObserver.observe(el);
139
141
  mutationObserver.observe(el, { childList: true });
@@ -144,20 +146,20 @@ var useOnResizeContent = (ref, callback) => {
144
146
  resizeObserver.disconnect();
145
147
  mutationObserver.disconnect();
146
148
  };
147
- }, [ref.current]);
149
+ }, [ref.current, callbackRef]);
148
150
  };
149
151
 
150
152
  // src/utils/hooks/useOnScrollToBottom.tsx
153
+ var import_react_use_callback_ref2 = require("@radix-ui/react-use-callback-ref");
151
154
  var import_react4 = require("react");
152
155
  var useOnScrollToBottom = (callback) => {
153
- const callbackRef = (0, import_react4.useRef)(callback);
154
- callbackRef.current = callback;
156
+ const callbackRef = (0, import_react_use_callback_ref2.useCallbackRef)(callback);
155
157
  const { useViewport } = useAssistantContext();
156
158
  (0, import_react4.useEffect)(() => {
157
159
  return useViewport.getState().onScrollToBottom(() => {
158
- callbackRef.current();
160
+ callbackRef();
159
161
  });
160
- }, [useViewport]);
162
+ }, [useViewport, callbackRef]);
161
163
  };
162
164
 
163
165
  // src/primitives/thread/ThreadViewport.tsx
@@ -253,7 +255,7 @@ var message_exports = {};
253
255
  __export(message_exports, {
254
256
  Content: () => MessageContent,
255
257
  If: () => MessageIf,
256
- Loading: () => MessageLoading,
258
+ InProgress: () => MessageInProgress,
257
259
  Provider: () => MessageProvider,
258
260
  Root: () => MessageRoot
259
261
  });
@@ -333,11 +335,11 @@ var useMessageContext2 = () => {
333
335
  parentId: null,
334
336
  branches: [],
335
337
  isLast: false,
336
- loadingIndicator: null,
338
+ inProgressIndicator: null,
337
339
  isCopied: false,
338
340
  isHovering: false,
339
- setLoadingIndicator: (value) => {
340
- set({ loadingIndicator: value });
341
+ setInProgressIndicator: (value) => {
342
+ set({ inProgressIndicator: value });
341
343
  },
342
344
  setIsCopied: (value) => {
343
345
  set({ isCopied: value });
@@ -478,15 +480,15 @@ var useContentPartContext = () => {
478
480
  return context;
479
481
  };
480
482
 
481
- // src/primitives/contentPart/ContentPartLoadingIndicator.tsx
482
- var ContentPartLoadingIndicator = () => {
483
+ // src/primitives/contentPart/ContentPartInProgressIndicator.tsx
484
+ var ContentPartInProgressIndicator = () => {
483
485
  const { useMessage } = useMessageContext();
484
486
  const { useContentPart } = useContentPartContext();
485
- const loadingIndicator = useCombinedStore(
487
+ const indicator = useCombinedStore(
486
488
  [useMessage, useContentPart],
487
- (m, c) => c.isLoading ? m.loadingIndicator : null
489
+ (m, c) => c.status === "in_progress" ? m.inProgressIndicator : null
488
490
  );
489
- return loadingIndicator;
491
+ return indicator;
490
492
  };
491
493
 
492
494
  // src/primitives/contentPart/ContentPartProvider.tsx
@@ -497,7 +499,7 @@ var useContentPartContext2 = () => {
497
499
  const [context] = (0, import_react13.useState)(() => {
498
500
  const useContentPart = (0, import_zustand3.create)(() => ({
499
501
  part: null,
500
- isLoading: false
502
+ status: "done"
501
503
  }));
502
504
  return { useContentPart };
503
505
  });
@@ -505,7 +507,7 @@ var useContentPartContext2 = () => {
505
507
  };
506
508
  var ContentPartProvider = ({
507
509
  part,
508
- isLoading,
510
+ status,
509
511
  children
510
512
  }) => {
511
513
  const context = useContentPartContext2();
@@ -513,11 +515,11 @@ var ContentPartProvider = ({
513
515
  context.useContentPart.setState(
514
516
  {
515
517
  part,
516
- isLoading
518
+ status
517
519
  },
518
520
  true
519
521
  );
520
- }, [context, part, isLoading]);
522
+ }, [context, part, status]);
521
523
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ContentPartContext.Provider, { value: context, children });
522
524
  };
523
525
 
@@ -526,7 +528,7 @@ var import_jsx_runtime7 = require("react/jsx-runtime");
526
528
  var defaultComponents = {
527
529
  Text: ({ part }) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
528
530
  part.text,
529
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ContentPartLoadingIndicator, {})
531
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ContentPartInProgressIndicator, {})
530
532
  ] }),
531
533
  Image: () => null,
532
534
  UI: ({ part }) => part.display,
@@ -542,13 +544,10 @@ var MessageContent = ({
542
544
  tools: { by_name = {}, Fallback = defaultComponents.tools.Fallback } = {}
543
545
  } = {}
544
546
  }) => {
545
- const { useThread } = useAssistantContext();
546
547
  const { useMessage } = useMessageContext();
547
- const content = useMessage((s) => s.message.content);
548
- const isLoading = useCombinedStore(
549
- [useThread, useMessage],
550
- (t, s) => s.isLast && t.isRunning
551
- );
548
+ const message = useMessage((s) => s.message);
549
+ const content = message.content;
550
+ const status = message.role === "assistant" ? message.status : "done";
552
551
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: content.map((part, i) => {
553
552
  const key = i;
554
553
  const type = part.type;
@@ -575,7 +574,7 @@ var MessageContent = ({
575
574
  ContentPartProvider,
576
575
  {
577
576
  part,
578
- isLoading: i === content.length - 1 && isLoading,
577
+ status: i === content.length - 1 ? status : "done",
579
578
  children: component
580
579
  },
581
580
  key
@@ -583,14 +582,14 @@ var MessageContent = ({
583
582
  }) });
584
583
  };
585
584
 
586
- // src/primitives/message/MessageLoading.tsx
585
+ // src/primitives/message/MessageInProgress.tsx
587
586
  var import_react_primitive4 = require("@radix-ui/react-primitive");
588
587
  var import_react14 = require("react");
589
588
  var import_jsx_runtime8 = require("react/jsx-runtime");
590
- var MessageLoading = (0, import_react14.forwardRef)((props, ref) => {
589
+ var MessageInProgress = (0, import_react14.forwardRef)((props, ref) => {
591
590
  const { useMessage } = useMessageContext();
592
591
  (0, import_react14.useMemo)(() => {
593
- useMessage.getState().setLoadingIndicator(/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_primitive4.Primitive.div, { ...props, ref }));
592
+ useMessage.getState().setInProgressIndicator(/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_primitive4.Primitive.div, { ...props, ref }));
594
593
  }, [useMessage, props, ref]);
595
594
  return null;
596
595
  });
@@ -892,7 +891,6 @@ var useGoToPreviousBranch = () => {
892
891
  const { message, branches } = useMessage.getState();
893
892
  useThread.getState().switchToBranch(
894
893
  branches[branches.indexOf(message.id) - 1]
895
- // TODO probably there's a more elegant way to do this
896
894
  );
897
895
  };
898
896
  };
@@ -1025,7 +1023,7 @@ var ActionBarEdit = createActionButton(useBeginMessageEdit);
1025
1023
  // src/primitives/contentPart/index.ts
1026
1024
  var contentPart_exports = {};
1027
1025
  __export(contentPart_exports, {
1028
- LoadingIndicator: () => ContentPartLoadingIndicator,
1026
+ InProgressIndicator: () => ContentPartInProgressIndicator,
1029
1027
  Provider: () => ContentPartProvider
1030
1028
  });
1031
1029
 
@@ -1089,6 +1087,7 @@ var useDummyAIAssistantContext = () => {
1089
1087
  };
1090
1088
 
1091
1089
  // src/adapters/vercel/useVercelAIThreadState.tsx
1090
+ var import_react_use_callback_ref3 = require("@radix-ui/react-use-callback-ref");
1092
1091
  var import_react25 = require("react");
1093
1092
 
1094
1093
  // src/adapters/MessageRepository.tsx
@@ -1099,7 +1098,6 @@ var generateId = (0, import_non_secure.customAlphabet)(
1099
1098
  );
1100
1099
  var optimisticPrefix = "__optimistic__";
1101
1100
  var generateOptimisticId = () => `${optimisticPrefix}${generateId()}`;
1102
- var isOptimisticId = (id) => id.startsWith(optimisticPrefix);
1103
1101
  var findHead = (message) => {
1104
1102
  if (message.next) return findHead(message.next);
1105
1103
  return message;
@@ -1183,7 +1181,8 @@ var MessageRepository = class {
1183
1181
  this.addOrUpdateMessage(parentId, {
1184
1182
  ...message,
1185
1183
  id: optimisticId,
1186
- createdAt: /* @__PURE__ */ new Date()
1184
+ createdAt: /* @__PURE__ */ new Date(),
1185
+ ...message.role === "assistant" ? { status: "in_progress" } : void 0
1187
1186
  });
1188
1187
  return optimisticId;
1189
1188
  }
@@ -1253,8 +1252,8 @@ var MessageRepository = class {
1253
1252
 
1254
1253
  // src/adapters/ThreadMessageConverter.tsx
1255
1254
  var ThreadMessageConverter = class {
1256
- constructor(converter2) {
1257
- this.converter = converter2;
1255
+ constructor(converter) {
1256
+ this.converter = converter;
1258
1257
  }
1259
1258
  cache = /* @__PURE__ */ new WeakMap();
1260
1259
  convertMessages(messages) {
@@ -1268,33 +1267,55 @@ var ThreadMessageConverter = class {
1268
1267
  }
1269
1268
  };
1270
1269
 
1270
+ // src/adapters/vercel/VercelThreadMessage.tsx
1271
+ var symbolInnerMessage = Symbol("innerMessage");
1272
+ var symbolInnerRSCMessage = Symbol("innerRSCMessage");
1273
+ var getVercelMessage = (message) => {
1274
+ return message[symbolInnerMessage];
1275
+ };
1276
+ var getVercelRSCMessage = (message) => {
1277
+ return message[symbolInnerRSCMessage];
1278
+ };
1279
+
1271
1280
  // src/adapters/vercel/useVercelAIThreadState.tsx
1272
- var vercelToThreadMessage = (message) => {
1273
- if (message.role !== "user" && message.role !== "assistant")
1274
- throw new Error(
1275
- `You have a message with an unsupported role. The role ${message.role} is not supported.`
1276
- );
1277
- return {
1281
+ var vercelToThreadMessage = (message, status) => {
1282
+ const common = {
1278
1283
  id: message.id,
1279
- role: message.role,
1280
- content: [
1281
- ...message.content ? [{ type: "text", text: message.content }] : [],
1282
- ...message.toolInvocations?.map((t) => ({
1283
- type: "tool-call",
1284
- name: t.toolName,
1285
- args: t.args,
1286
- result: "result" in t ? t.result : void 0
1287
- })) ?? []
1288
- ],
1289
- // ignore type mismatch for now
1290
1284
  createdAt: message.createdAt ?? /* @__PURE__ */ new Date(),
1291
- innerMessage: message
1285
+ [symbolInnerMessage]: message
1292
1286
  };
1287
+ switch (message.role) {
1288
+ case "user":
1289
+ return {
1290
+ ...common,
1291
+ role: "user",
1292
+ content: [{ type: "text", text: message.content }]
1293
+ };
1294
+ case "assistant":
1295
+ return {
1296
+ ...common,
1297
+ role: "assistant",
1298
+ content: [
1299
+ ...message.content ? [{ type: "text", text: message.content }] : [],
1300
+ ...message.toolInvocations?.map(
1301
+ (t) => ({
1302
+ type: "tool-call",
1303
+ name: t.toolName,
1304
+ args: t.args,
1305
+ result: "result" in t ? t.result : void 0
1306
+ })
1307
+ ) ?? []
1308
+ ],
1309
+ status
1310
+ };
1311
+ default:
1312
+ throw new Error(
1313
+ `You have a message with an unsupported role. The role ${message.role} is not supported.`
1314
+ );
1315
+ }
1293
1316
  };
1294
- var converter = new ThreadMessageConverter(vercelToThreadMessage);
1295
1317
  var sliceMessagesUntil = (messages, messageId) => {
1296
1318
  if (messageId == null) return [];
1297
- if (isOptimisticId(messageId)) return messages;
1298
1319
  const messageIdx = messages.findIndex((m) => m.id === messageId);
1299
1320
  if (messageIdx === -1)
1300
1321
  throw new Error(
@@ -1311,9 +1332,14 @@ var getIsRunning = (vercel) => {
1311
1332
  };
1312
1333
  var useVercelAIThreadState = (vercel) => {
1313
1334
  const [data] = (0, import_react25.useState)(() => new MessageRepository());
1314
- const vercelRef = (0, import_react25.useRef)(vercel);
1315
- vercelRef.current = vercel;
1316
- const isRunning = getIsRunning(vercelRef.current);
1335
+ const isRunning = getIsRunning(vercel);
1336
+ const convertCallback = (0, import_react_use_callback_ref3.useCallbackRef)((message) => {
1337
+ return vercelToThreadMessage(
1338
+ message,
1339
+ vercel.messages.at(-1) === message && isRunning ? "in_progress" : "done"
1340
+ );
1341
+ });
1342
+ const converter = new ThreadMessageConverter(convertCallback);
1317
1343
  const assistantOptimisticIdRef = (0, import_react25.useRef)(null);
1318
1344
  const messages = (0, import_react25.useMemo)(() => {
1319
1345
  const vm = converter.convertMessages(vercel.messages);
@@ -1337,55 +1363,46 @@ var useVercelAIThreadState = (vercel) => {
1337
1363
  }
1338
1364
  data.resetHead(assistantOptimisticIdRef.current ?? vm.at(-1)?.id ?? null);
1339
1365
  return data.getMessages();
1340
- }, [data, isRunning, vercel.messages]);
1366
+ }, [converter, data, isRunning, vercel.messages]);
1341
1367
  const getBranches2 = (0, import_react25.useCallback)(
1342
1368
  (messageId) => {
1343
1369
  return data.getBranches(messageId);
1344
1370
  },
1345
1371
  [data]
1346
1372
  );
1347
- const switchToBranch2 = (0, import_react25.useCallback)(
1348
- (messageId) => {
1349
- data.switchToBranch(messageId);
1350
- vercelRef.current.setMessages(
1351
- data.getMessages().filter((m) => !isOptimisticId(m.id)).map((m) => m.innerMessage)
1352
- );
1353
- },
1354
- [data]
1355
- );
1356
- const startRun = (0, import_react25.useCallback)(async (parentId) => {
1357
- const reloadMaybe = "reload" in vercelRef.current ? vercelRef.current.reload : void 0;
1373
+ const switchToBranch2 = (0, import_react_use_callback_ref3.useCallbackRef)((messageId) => {
1374
+ data.switchToBranch(messageId);
1375
+ vercel.setMessages(
1376
+ data.getMessages().map(getVercelMessage).filter((m) => m != null)
1377
+ );
1378
+ });
1379
+ const startRun = (0, import_react_use_callback_ref3.useCallbackRef)(async (parentId) => {
1380
+ const reloadMaybe = "reload" in vercel ? vercel.reload : void 0;
1358
1381
  if (!reloadMaybe)
1359
1382
  throw new Error(
1360
1383
  "Reload is not supported by Vercel AI SDK's useAssistant."
1361
1384
  );
1362
- const newMessages = sliceMessagesUntil(
1363
- vercelRef.current.messages,
1364
- parentId
1365
- );
1366
- vercelRef.current.setMessages(newMessages);
1385
+ const newMessages = sliceMessagesUntil(vercel.messages, parentId);
1386
+ vercel.setMessages(newMessages);
1367
1387
  await reloadMaybe();
1368
- }, []);
1369
- const append = (0, import_react25.useCallback)(async (message) => {
1388
+ });
1389
+ const append = (0, import_react_use_callback_ref3.useCallbackRef)(async (message) => {
1370
1390
  if (message.content.length !== 1 || message.content[0]?.type !== "text")
1371
1391
  throw new Error("Only text content is supported by Vercel AI SDK.");
1372
- const newMessages = sliceMessagesUntil(
1373
- vercelRef.current.messages,
1374
- message.parentId
1375
- );
1376
- vercelRef.current.setMessages(newMessages);
1377
- await vercelRef.current.append({
1392
+ const newMessages = sliceMessagesUntil(vercel.messages, message.parentId);
1393
+ vercel.setMessages(newMessages);
1394
+ await vercel.append({
1378
1395
  role: "user",
1379
1396
  content: message.content[0].text
1380
1397
  });
1381
- }, []);
1382
- const cancelRun2 = (0, import_react25.useCallback)(() => {
1383
- const lastMessage = vercelRef.current.messages.at(-1);
1384
- vercelRef.current.stop();
1398
+ });
1399
+ const cancelRun2 = (0, import_react_use_callback_ref3.useCallbackRef)(() => {
1400
+ const lastMessage = vercel.messages.at(-1);
1401
+ vercel.stop();
1385
1402
  if (lastMessage?.role === "user") {
1386
- vercelRef.current.setInput(lastMessage.content);
1403
+ vercel.setInput(lastMessage.content);
1387
1404
  }
1388
- }, []);
1405
+ });
1389
1406
  return (0, import_react25.useMemo)(
1390
1407
  () => ({
1391
1408
  isRunning,
@@ -1432,12 +1449,15 @@ var VercelAIAssistantProvider = ({
1432
1449
  // src/adapters/vercel/VercelRSCAssistantProvider.tsx
1433
1450
  var import_react27 = require("react");
1434
1451
  var import_jsx_runtime22 = require("react/jsx-runtime");
1435
- var vercelToThreadMessage2 = (message) => {
1452
+ var vercelToThreadMessage2 = (converter, rawMessage) => {
1453
+ const message = converter(rawMessage);
1436
1454
  return {
1437
1455
  id: message.id,
1438
1456
  role: message.role,
1439
1457
  content: [{ type: "ui", display: message.display }],
1440
- createdAt: message.createdAt ?? /* @__PURE__ */ new Date()
1458
+ createdAt: message.createdAt ?? /* @__PURE__ */ new Date(),
1459
+ ...{ status: "done" },
1460
+ [symbolInnerRSCMessage]: rawMessage
1441
1461
  };
1442
1462
  };
1443
1463
  var EMPTY_BRANCHES = [];
@@ -1470,15 +1490,15 @@ var VercelRSCAssistantProvider = ({
1470
1490
  setIsRunning(true);
1471
1491
  return callback.finally(() => setIsRunning(false));
1472
1492
  }, []);
1473
- const converter2 = (0, import_react27.useMemo)(() => {
1493
+ const converter = (0, import_react27.useMemo)(() => {
1474
1494
  const rscConverter = convertMessage ?? ((m) => m);
1475
1495
  return new ThreadMessageConverter((m) => {
1476
- return vercelToThreadMessage2(rscConverter(m));
1496
+ return vercelToThreadMessage2(rscConverter, m);
1477
1497
  });
1478
1498
  }, [convertMessage]);
1479
1499
  const messages = (0, import_react27.useMemo)(() => {
1480
- return converter2.convertMessages(vercelMessages);
1481
- }, [converter2, vercelMessages]);
1500
+ return converter.convertMessages(vercelMessages);
1501
+ }, [converter, vercelMessages]);
1482
1502
  const append = (0, import_react27.useCallback)(
1483
1503
  async (message) => {
1484
1504
  if (message.parentId !== (context.useThread.getState().messages.at(-1)?.id ?? null)) {
@@ -1529,6 +1549,8 @@ var VercelRSCAssistantProvider = ({
1529
1549
  ThreadPrimitive,
1530
1550
  VercelAIAssistantProvider,
1531
1551
  VercelRSCAssistantProvider,
1552
+ unstable_getVercelMessage,
1553
+ unstable_getVercelRSCMessage,
1532
1554
  unstable_useMessageContext,
1533
1555
  useBeginMessageEdit,
1534
1556
  useCopyMessage,
package/dist/index.mjs CHANGED
@@ -68,18 +68,18 @@ import { useComposedRefs } from "@radix-ui/react-compose-refs";
68
68
  import {
69
69
  Primitive as Primitive2
70
70
  } from "@radix-ui/react-primitive";
71
- import { forwardRef as forwardRef2, useRef as useRef3 } from "react";
71
+ import { forwardRef as forwardRef2, useRef } from "react";
72
72
 
73
73
  // src/utils/hooks/useOnResizeContent.tsx
74
- import { useEffect, useRef } from "react";
74
+ import { useCallbackRef } from "@radix-ui/react-use-callback-ref";
75
+ import { useEffect } from "react";
75
76
  var useOnResizeContent = (ref, callback) => {
76
- const callbackRef = useRef(callback);
77
- callbackRef.current = callback;
77
+ const callbackRef = useCallbackRef(callback);
78
78
  useEffect(() => {
79
79
  const el = ref.current;
80
80
  if (!el) return;
81
81
  const resizeObserver = new ResizeObserver(() => {
82
- callbackRef.current();
82
+ callbackRef();
83
83
  });
84
84
  const mutationObserver = new MutationObserver((mutations) => {
85
85
  for (const mutation of mutations) {
@@ -94,7 +94,7 @@ var useOnResizeContent = (ref, callback) => {
94
94
  }
95
95
  }
96
96
  }
97
- callbackRef.current();
97
+ callbackRef();
98
98
  });
99
99
  resizeObserver.observe(el);
100
100
  mutationObserver.observe(el, { childList: true });
@@ -105,31 +105,31 @@ var useOnResizeContent = (ref, callback) => {
105
105
  resizeObserver.disconnect();
106
106
  mutationObserver.disconnect();
107
107
  };
108
- }, [ref.current]);
108
+ }, [ref.current, callbackRef]);
109
109
  };
110
110
 
111
111
  // src/utils/hooks/useOnScrollToBottom.tsx
112
- import { useEffect as useEffect2, useRef as useRef2 } from "react";
112
+ import { useCallbackRef as useCallbackRef2 } from "@radix-ui/react-use-callback-ref";
113
+ import { useEffect as useEffect2 } from "react";
113
114
  var useOnScrollToBottom = (callback) => {
114
- const callbackRef = useRef2(callback);
115
- callbackRef.current = callback;
115
+ const callbackRef = useCallbackRef2(callback);
116
116
  const { useViewport } = useAssistantContext();
117
117
  useEffect2(() => {
118
118
  return useViewport.getState().onScrollToBottom(() => {
119
- callbackRef.current();
119
+ callbackRef();
120
120
  });
121
- }, [useViewport]);
121
+ }, [useViewport, callbackRef]);
122
122
  };
123
123
 
124
124
  // src/primitives/thread/ThreadViewport.tsx
125
125
  import { jsx as jsx3, jsxs } from "react/jsx-runtime";
126
126
  var ThreadViewport = forwardRef2(({ autoScroll = true, onScroll, children, ...rest }, forwardedRef) => {
127
- const messagesEndRef = useRef3(null);
128
- const divRef = useRef3(null);
127
+ const messagesEndRef = useRef(null);
128
+ const divRef = useRef(null);
129
129
  const ref = useComposedRefs(forwardedRef, divRef);
130
130
  const { useViewport } = useAssistantContext();
131
- const firstRenderRef = useRef3(true);
132
- const lastScrollTop = useRef3(0);
131
+ const firstRenderRef = useRef(true);
132
+ const lastScrollTop = useRef(0);
133
133
  const scrollToBottom = () => {
134
134
  const div = messagesEndRef.current;
135
135
  if (!div || !autoScroll) return;
@@ -214,7 +214,7 @@ var message_exports = {};
214
214
  __export(message_exports, {
215
215
  Content: () => MessageContent,
216
216
  If: () => MessageIf,
217
- Loading: () => MessageLoading,
217
+ InProgress: () => MessageInProgress,
218
218
  Provider: () => MessageProvider,
219
219
  Root: () => MessageRoot
220
220
  });
@@ -296,11 +296,11 @@ var useMessageContext2 = () => {
296
296
  parentId: null,
297
297
  branches: [],
298
298
  isLast: false,
299
- loadingIndicator: null,
299
+ inProgressIndicator: null,
300
300
  isCopied: false,
301
301
  isHovering: false,
302
- setLoadingIndicator: (value) => {
303
- set({ loadingIndicator: value });
302
+ setInProgressIndicator: (value) => {
303
+ set({ inProgressIndicator: value });
304
304
  },
305
305
  setIsCopied: (value) => {
306
306
  set({ isCopied: value });
@@ -443,15 +443,15 @@ var useContentPartContext = () => {
443
443
  return context;
444
444
  };
445
445
 
446
- // src/primitives/contentPart/ContentPartLoadingIndicator.tsx
447
- var ContentPartLoadingIndicator = () => {
446
+ // src/primitives/contentPart/ContentPartInProgressIndicator.tsx
447
+ var ContentPartInProgressIndicator = () => {
448
448
  const { useMessage } = useMessageContext();
449
449
  const { useContentPart } = useContentPartContext();
450
- const loadingIndicator = useCombinedStore(
450
+ const indicator = useCombinedStore(
451
451
  [useMessage, useContentPart],
452
- (m, c) => c.isLoading ? m.loadingIndicator : null
452
+ (m, c) => c.status === "in_progress" ? m.inProgressIndicator : null
453
453
  );
454
- return loadingIndicator;
454
+ return indicator;
455
455
  };
456
456
 
457
457
  // src/primitives/contentPart/ContentPartProvider.tsx
@@ -462,7 +462,7 @@ var useContentPartContext2 = () => {
462
462
  const [context] = useState2(() => {
463
463
  const useContentPart = create3(() => ({
464
464
  part: null,
465
- isLoading: false
465
+ status: "done"
466
466
  }));
467
467
  return { useContentPart };
468
468
  });
@@ -470,7 +470,7 @@ var useContentPartContext2 = () => {
470
470
  };
471
471
  var ContentPartProvider = ({
472
472
  part,
473
- isLoading,
473
+ status,
474
474
  children
475
475
  }) => {
476
476
  const context = useContentPartContext2();
@@ -478,11 +478,11 @@ var ContentPartProvider = ({
478
478
  context.useContentPart.setState(
479
479
  {
480
480
  part,
481
- isLoading
481
+ status
482
482
  },
483
483
  true
484
484
  );
485
- }, [context, part, isLoading]);
485
+ }, [context, part, status]);
486
486
  return /* @__PURE__ */ jsx6(ContentPartContext.Provider, { value: context, children });
487
487
  };
488
488
 
@@ -491,7 +491,7 @@ import { Fragment, jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
491
491
  var defaultComponents = {
492
492
  Text: ({ part }) => /* @__PURE__ */ jsxs2(Fragment, { children: [
493
493
  part.text,
494
- /* @__PURE__ */ jsx7(ContentPartLoadingIndicator, {})
494
+ /* @__PURE__ */ jsx7(ContentPartInProgressIndicator, {})
495
495
  ] }),
496
496
  Image: () => null,
497
497
  UI: ({ part }) => part.display,
@@ -507,13 +507,10 @@ var MessageContent = ({
507
507
  tools: { by_name = {}, Fallback = defaultComponents.tools.Fallback } = {}
508
508
  } = {}
509
509
  }) => {
510
- const { useThread } = useAssistantContext();
511
510
  const { useMessage } = useMessageContext();
512
- const content = useMessage((s) => s.message.content);
513
- const isLoading = useCombinedStore(
514
- [useThread, useMessage],
515
- (t, s) => s.isLast && t.isRunning
516
- );
511
+ const message = useMessage((s) => s.message);
512
+ const content = message.content;
513
+ const status = message.role === "assistant" ? message.status : "done";
517
514
  return /* @__PURE__ */ jsx7(Fragment, { children: content.map((part, i) => {
518
515
  const key = i;
519
516
  const type = part.type;
@@ -540,7 +537,7 @@ var MessageContent = ({
540
537
  ContentPartProvider,
541
538
  {
542
539
  part,
543
- isLoading: i === content.length - 1 && isLoading,
540
+ status: i === content.length - 1 ? status : "done",
544
541
  children: component
545
542
  },
546
543
  key
@@ -548,16 +545,16 @@ var MessageContent = ({
548
545
  }) });
549
546
  };
550
547
 
551
- // src/primitives/message/MessageLoading.tsx
548
+ // src/primitives/message/MessageInProgress.tsx
552
549
  import {
553
550
  Primitive as Primitive4
554
551
  } from "@radix-ui/react-primitive";
555
552
  import { forwardRef as forwardRef4, useMemo as useMemo4 } from "react";
556
553
  import { jsx as jsx8 } from "react/jsx-runtime";
557
- var MessageLoading = forwardRef4((props, ref) => {
554
+ var MessageInProgress = forwardRef4((props, ref) => {
558
555
  const { useMessage } = useMessageContext();
559
556
  useMemo4(() => {
560
- useMessage.getState().setLoadingIndicator(/* @__PURE__ */ jsx8(Primitive4.div, { ...props, ref }));
557
+ useMessage.getState().setInProgressIndicator(/* @__PURE__ */ jsx8(Primitive4.div, { ...props, ref }));
561
558
  }, [useMessage, props, ref]);
562
559
  return null;
563
560
  });
@@ -666,13 +663,13 @@ import { useComposedRefs as useComposedRefs2 } from "@radix-ui/react-compose-ref
666
663
  import {
667
664
  Primitive as Primitive7
668
665
  } from "@radix-ui/react-primitive";
669
- import { forwardRef as forwardRef7, useRef as useRef4 } from "react";
666
+ import { forwardRef as forwardRef7, useRef as useRef2 } from "react";
670
667
  import { jsx as jsx12 } from "react/jsx-runtime";
671
668
  var ComposerRoot = forwardRef7(
672
669
  ({ onSubmit, ...rest }, forwardedRef) => {
673
670
  const { useViewport } = useAssistantContext();
674
671
  const { useComposer } = useComposerContext();
675
- const formRef = useRef4(null);
672
+ const formRef = useRef2(null);
676
673
  const ref = useComposedRefs2(forwardedRef, formRef);
677
674
  const handleSubmit = (e) => {
678
675
  const composerState = useComposer.getState();
@@ -700,7 +697,7 @@ import {
700
697
  forwardRef as forwardRef8,
701
698
  useCallback,
702
699
  useEffect as useEffect3,
703
- useRef as useRef5
700
+ useRef as useRef3
704
701
  } from "react";
705
702
  import TextareaAutosize from "react-textarea-autosize";
706
703
  import { jsx as jsx13 } from "react/jsx-runtime";
@@ -729,7 +726,7 @@ var ComposerInput = forwardRef8(
729
726
  }
730
727
  }
731
728
  };
732
- const textareaRef = useRef5(null);
729
+ const textareaRef = useRef3(null);
733
730
  const ref = useComposedRefs3(forwardedRef, textareaRef);
734
731
  const autoFocusEnabled = autoFocus && !disabled;
735
732
  const focus = useCallback(() => {
@@ -876,7 +873,6 @@ var useGoToPreviousBranch = () => {
876
873
  const { message, branches } = useMessage.getState();
877
874
  useThread.getState().switchToBranch(
878
875
  branches[branches.indexOf(message.id) - 1]
879
- // TODO probably there's a more elegant way to do this
880
876
  );
881
877
  };
882
878
  };
@@ -1013,7 +1009,7 @@ var ActionBarEdit = createActionButton(useBeginMessageEdit);
1013
1009
  // src/primitives/contentPart/index.ts
1014
1010
  var contentPart_exports = {};
1015
1011
  __export(contentPart_exports, {
1016
- LoadingIndicator: () => ContentPartLoadingIndicator,
1012
+ InProgressIndicator: () => ContentPartInProgressIndicator,
1017
1013
  Provider: () => ContentPartProvider
1018
1014
  });
1019
1015
 
@@ -1077,7 +1073,8 @@ var useDummyAIAssistantContext = () => {
1077
1073
  };
1078
1074
 
1079
1075
  // src/adapters/vercel/useVercelAIThreadState.tsx
1080
- import { useCallback as useCallback2, useMemo as useMemo5, useRef as useRef6, useState as useState4 } from "react";
1076
+ import { useCallbackRef as useCallbackRef3 } from "@radix-ui/react-use-callback-ref";
1077
+ import { useCallback as useCallback2, useMemo as useMemo5, useRef as useRef4, useState as useState4 } from "react";
1081
1078
 
1082
1079
  // src/adapters/MessageRepository.tsx
1083
1080
  import { customAlphabet } from "nanoid/non-secure";
@@ -1087,7 +1084,6 @@ var generateId = customAlphabet(
1087
1084
  );
1088
1085
  var optimisticPrefix = "__optimistic__";
1089
1086
  var generateOptimisticId = () => `${optimisticPrefix}${generateId()}`;
1090
- var isOptimisticId = (id) => id.startsWith(optimisticPrefix);
1091
1087
  var findHead = (message) => {
1092
1088
  if (message.next) return findHead(message.next);
1093
1089
  return message;
@@ -1171,7 +1167,8 @@ var MessageRepository = class {
1171
1167
  this.addOrUpdateMessage(parentId, {
1172
1168
  ...message,
1173
1169
  id: optimisticId,
1174
- createdAt: /* @__PURE__ */ new Date()
1170
+ createdAt: /* @__PURE__ */ new Date(),
1171
+ ...message.role === "assistant" ? { status: "in_progress" } : void 0
1175
1172
  });
1176
1173
  return optimisticId;
1177
1174
  }
@@ -1241,8 +1238,8 @@ var MessageRepository = class {
1241
1238
 
1242
1239
  // src/adapters/ThreadMessageConverter.tsx
1243
1240
  var ThreadMessageConverter = class {
1244
- constructor(converter2) {
1245
- this.converter = converter2;
1241
+ constructor(converter) {
1242
+ this.converter = converter;
1246
1243
  }
1247
1244
  cache = /* @__PURE__ */ new WeakMap();
1248
1245
  convertMessages(messages) {
@@ -1256,33 +1253,55 @@ var ThreadMessageConverter = class {
1256
1253
  }
1257
1254
  };
1258
1255
 
1256
+ // src/adapters/vercel/VercelThreadMessage.tsx
1257
+ var symbolInnerMessage = Symbol("innerMessage");
1258
+ var symbolInnerRSCMessage = Symbol("innerRSCMessage");
1259
+ var getVercelMessage = (message) => {
1260
+ return message[symbolInnerMessage];
1261
+ };
1262
+ var getVercelRSCMessage = (message) => {
1263
+ return message[symbolInnerRSCMessage];
1264
+ };
1265
+
1259
1266
  // src/adapters/vercel/useVercelAIThreadState.tsx
1260
- var vercelToThreadMessage = (message) => {
1261
- if (message.role !== "user" && message.role !== "assistant")
1262
- throw new Error(
1263
- `You have a message with an unsupported role. The role ${message.role} is not supported.`
1264
- );
1265
- return {
1267
+ var vercelToThreadMessage = (message, status) => {
1268
+ const common = {
1266
1269
  id: message.id,
1267
- role: message.role,
1268
- content: [
1269
- ...message.content ? [{ type: "text", text: message.content }] : [],
1270
- ...message.toolInvocations?.map((t) => ({
1271
- type: "tool-call",
1272
- name: t.toolName,
1273
- args: t.args,
1274
- result: "result" in t ? t.result : void 0
1275
- })) ?? []
1276
- ],
1277
- // ignore type mismatch for now
1278
1270
  createdAt: message.createdAt ?? /* @__PURE__ */ new Date(),
1279
- innerMessage: message
1271
+ [symbolInnerMessage]: message
1280
1272
  };
1273
+ switch (message.role) {
1274
+ case "user":
1275
+ return {
1276
+ ...common,
1277
+ role: "user",
1278
+ content: [{ type: "text", text: message.content }]
1279
+ };
1280
+ case "assistant":
1281
+ return {
1282
+ ...common,
1283
+ role: "assistant",
1284
+ content: [
1285
+ ...message.content ? [{ type: "text", text: message.content }] : [],
1286
+ ...message.toolInvocations?.map(
1287
+ (t) => ({
1288
+ type: "tool-call",
1289
+ name: t.toolName,
1290
+ args: t.args,
1291
+ result: "result" in t ? t.result : void 0
1292
+ })
1293
+ ) ?? []
1294
+ ],
1295
+ status
1296
+ };
1297
+ default:
1298
+ throw new Error(
1299
+ `You have a message with an unsupported role. The role ${message.role} is not supported.`
1300
+ );
1301
+ }
1281
1302
  };
1282
- var converter = new ThreadMessageConverter(vercelToThreadMessage);
1283
1303
  var sliceMessagesUntil = (messages, messageId) => {
1284
1304
  if (messageId == null) return [];
1285
- if (isOptimisticId(messageId)) return messages;
1286
1305
  const messageIdx = messages.findIndex((m) => m.id === messageId);
1287
1306
  if (messageIdx === -1)
1288
1307
  throw new Error(
@@ -1299,10 +1318,15 @@ var getIsRunning = (vercel) => {
1299
1318
  };
1300
1319
  var useVercelAIThreadState = (vercel) => {
1301
1320
  const [data] = useState4(() => new MessageRepository());
1302
- const vercelRef = useRef6(vercel);
1303
- vercelRef.current = vercel;
1304
- const isRunning = getIsRunning(vercelRef.current);
1305
- const assistantOptimisticIdRef = useRef6(null);
1321
+ const isRunning = getIsRunning(vercel);
1322
+ const convertCallback = useCallbackRef3((message) => {
1323
+ return vercelToThreadMessage(
1324
+ message,
1325
+ vercel.messages.at(-1) === message && isRunning ? "in_progress" : "done"
1326
+ );
1327
+ });
1328
+ const converter = new ThreadMessageConverter(convertCallback);
1329
+ const assistantOptimisticIdRef = useRef4(null);
1306
1330
  const messages = useMemo5(() => {
1307
1331
  const vm = converter.convertMessages(vercel.messages);
1308
1332
  for (let i = 0; i < vm.length; i++) {
@@ -1325,55 +1349,46 @@ var useVercelAIThreadState = (vercel) => {
1325
1349
  }
1326
1350
  data.resetHead(assistantOptimisticIdRef.current ?? vm.at(-1)?.id ?? null);
1327
1351
  return data.getMessages();
1328
- }, [data, isRunning, vercel.messages]);
1352
+ }, [converter, data, isRunning, vercel.messages]);
1329
1353
  const getBranches2 = useCallback2(
1330
1354
  (messageId) => {
1331
1355
  return data.getBranches(messageId);
1332
1356
  },
1333
1357
  [data]
1334
1358
  );
1335
- const switchToBranch2 = useCallback2(
1336
- (messageId) => {
1337
- data.switchToBranch(messageId);
1338
- vercelRef.current.setMessages(
1339
- data.getMessages().filter((m) => !isOptimisticId(m.id)).map((m) => m.innerMessage)
1340
- );
1341
- },
1342
- [data]
1343
- );
1344
- const startRun = useCallback2(async (parentId) => {
1345
- const reloadMaybe = "reload" in vercelRef.current ? vercelRef.current.reload : void 0;
1359
+ const switchToBranch2 = useCallbackRef3((messageId) => {
1360
+ data.switchToBranch(messageId);
1361
+ vercel.setMessages(
1362
+ data.getMessages().map(getVercelMessage).filter((m) => m != null)
1363
+ );
1364
+ });
1365
+ const startRun = useCallbackRef3(async (parentId) => {
1366
+ const reloadMaybe = "reload" in vercel ? vercel.reload : void 0;
1346
1367
  if (!reloadMaybe)
1347
1368
  throw new Error(
1348
1369
  "Reload is not supported by Vercel AI SDK's useAssistant."
1349
1370
  );
1350
- const newMessages = sliceMessagesUntil(
1351
- vercelRef.current.messages,
1352
- parentId
1353
- );
1354
- vercelRef.current.setMessages(newMessages);
1371
+ const newMessages = sliceMessagesUntil(vercel.messages, parentId);
1372
+ vercel.setMessages(newMessages);
1355
1373
  await reloadMaybe();
1356
- }, []);
1357
- const append = useCallback2(async (message) => {
1374
+ });
1375
+ const append = useCallbackRef3(async (message) => {
1358
1376
  if (message.content.length !== 1 || message.content[0]?.type !== "text")
1359
1377
  throw new Error("Only text content is supported by Vercel AI SDK.");
1360
- const newMessages = sliceMessagesUntil(
1361
- vercelRef.current.messages,
1362
- message.parentId
1363
- );
1364
- vercelRef.current.setMessages(newMessages);
1365
- await vercelRef.current.append({
1378
+ const newMessages = sliceMessagesUntil(vercel.messages, message.parentId);
1379
+ vercel.setMessages(newMessages);
1380
+ await vercel.append({
1366
1381
  role: "user",
1367
1382
  content: message.content[0].text
1368
1383
  });
1369
- }, []);
1370
- const cancelRun2 = useCallback2(() => {
1371
- const lastMessage = vercelRef.current.messages.at(-1);
1372
- vercelRef.current.stop();
1384
+ });
1385
+ const cancelRun2 = useCallbackRef3(() => {
1386
+ const lastMessage = vercel.messages.at(-1);
1387
+ vercel.stop();
1373
1388
  if (lastMessage?.role === "user") {
1374
- vercelRef.current.setInput(lastMessage.content);
1389
+ vercel.setInput(lastMessage.content);
1375
1390
  }
1376
- }, []);
1391
+ });
1377
1392
  return useMemo5(
1378
1393
  () => ({
1379
1394
  isRunning,
@@ -1424,12 +1439,15 @@ import {
1424
1439
  useState as useState5
1425
1440
  } from "react";
1426
1441
  import { jsx as jsx22 } from "react/jsx-runtime";
1427
- var vercelToThreadMessage2 = (message) => {
1442
+ var vercelToThreadMessage2 = (converter, rawMessage) => {
1443
+ const message = converter(rawMessage);
1428
1444
  return {
1429
1445
  id: message.id,
1430
1446
  role: message.role,
1431
1447
  content: [{ type: "ui", display: message.display }],
1432
- createdAt: message.createdAt ?? /* @__PURE__ */ new Date()
1448
+ createdAt: message.createdAt ?? /* @__PURE__ */ new Date(),
1449
+ ...{ status: "done" },
1450
+ [symbolInnerRSCMessage]: rawMessage
1433
1451
  };
1434
1452
  };
1435
1453
  var EMPTY_BRANCHES = [];
@@ -1462,15 +1480,15 @@ var VercelRSCAssistantProvider = ({
1462
1480
  setIsRunning(true);
1463
1481
  return callback.finally(() => setIsRunning(false));
1464
1482
  }, []);
1465
- const converter2 = useMemo7(() => {
1483
+ const converter = useMemo7(() => {
1466
1484
  const rscConverter = convertMessage ?? ((m) => m);
1467
1485
  return new ThreadMessageConverter((m) => {
1468
- return vercelToThreadMessage2(rscConverter(m));
1486
+ return vercelToThreadMessage2(rscConverter, m);
1469
1487
  });
1470
1488
  }, [convertMessage]);
1471
1489
  const messages = useMemo7(() => {
1472
- return converter2.convertMessages(vercelMessages);
1473
- }, [converter2, vercelMessages]);
1490
+ return converter.convertMessages(vercelMessages);
1491
+ }, [converter, vercelMessages]);
1474
1492
  const append = useCallback3(
1475
1493
  async (message) => {
1476
1494
  if (message.parentId !== (context.useThread.getState().messages.at(-1)?.id ?? null)) {
@@ -1520,6 +1538,8 @@ export {
1520
1538
  thread_exports as ThreadPrimitive,
1521
1539
  VercelAIAssistantProvider,
1522
1540
  VercelRSCAssistantProvider,
1541
+ getVercelMessage as unstable_getVercelMessage,
1542
+ getVercelRSCMessage as unstable_getVercelRSCMessage,
1523
1543
  useMessageContext as unstable_useMessageContext,
1524
1544
  useBeginMessageEdit,
1525
1545
  useCopyMessage,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/react",
3
- "version": "0.0.16",
3
+ "version": "0.0.17",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": {
@@ -31,6 +31,7 @@
31
31
  "@radix-ui/react-compose-refs": "^1.0.1",
32
32
  "@radix-ui/react-primitive": "^1.0.3",
33
33
  "@radix-ui/react-slot": "^1.0.2",
34
+ "@radix-ui/react-use-callback-ref": "^1.0.1",
34
35
  "ai": "^3.1.23",
35
36
  "nanoid": "^5.0.7",
36
37
  "react-textarea-autosize": "^8.5.3",