@assistant-ui/react 0.0.16 → 0.0.17

Sign up to get free protection for your applications and to get access to all the features.
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",