@assistant-ui/react 0.0.9 → 0.0.11

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.js CHANGED
@@ -58,11 +58,12 @@ __export(thread_exports, {
58
58
  });
59
59
 
60
60
  // src/primitives/thread/ThreadRoot.tsx
61
- var import_react = require("react");
62
61
  var import_react_primitive = require("@radix-ui/react-primitive");
62
+ var import_react = require("react");
63
+ var import_jsx_runtime = require("react/jsx-runtime");
63
64
  var ThreadRoot = (0, import_react.forwardRef)(
64
65
  (props, ref) => {
65
- return /* @__PURE__ */ React.createElement(import_react_primitive.Primitive.div, { ...props, ref });
66
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_primitive.Primitive.div, { ...props, ref });
66
67
  }
67
68
  );
68
69
 
@@ -86,9 +87,9 @@ var useThreadIf = (props) => {
86
87
  return false;
87
88
  if (props.empty === false && thread.messages.length === 0)
88
89
  return false;
89
- if (props.busy === true && !thread.isLoading)
90
+ if (props.running === true && !thread.isRunning)
90
91
  return false;
91
- if (props.busy === false && thread.isLoading)
92
+ if (props.running === false && thread.isRunning)
92
93
  return false;
93
94
  return true;
94
95
  });
@@ -99,8 +100,9 @@ var ThreadIf = ({ children, ...query }) => {
99
100
  };
100
101
 
101
102
  // src/primitives/thread/ThreadEmpty.tsx
103
+ var import_jsx_runtime2 = require("react/jsx-runtime");
102
104
  var ThreadEmpty = ({ children }) => {
103
- return /* @__PURE__ */ React.createElement(ThreadIf, { empty: true }, children);
105
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ThreadIf, { empty: true, children });
104
106
  };
105
107
 
106
108
  // src/primitives/thread/ThreadViewport.tsx
@@ -153,20 +155,21 @@ var import_react4 = require("react");
153
155
  var useOnScrollToBottom = (callback) => {
154
156
  const callbackRef = (0, import_react4.useRef)(callback);
155
157
  callbackRef.current = callback;
156
- const { useThread } = useAssistantContext();
158
+ const { useViewport } = useAssistantContext();
157
159
  (0, import_react4.useEffect)(() => {
158
- return useThread.getState().onScrollToBottom(() => {
160
+ return useViewport.getState().onScrollToBottom(() => {
159
161
  callbackRef.current();
160
162
  });
161
- }, [useThread]);
163
+ }, [useViewport]);
162
164
  };
163
165
 
164
166
  // src/primitives/thread/ThreadViewport.tsx
167
+ var import_jsx_runtime3 = require("react/jsx-runtime");
165
168
  var ThreadViewport = (0, import_react5.forwardRef)(({ autoScroll = true, onScroll, children, ...rest }, forwardedRef) => {
166
169
  const messagesEndRef = (0, import_react5.useRef)(null);
167
170
  const divRef = (0, import_react5.useRef)(null);
168
171
  const ref = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, divRef);
169
- const { useThread } = useAssistantContext();
172
+ const { useViewport } = useAssistantContext();
170
173
  const firstRenderRef = (0, import_react5.useRef)(true);
171
174
  const lastScrollTop = (0, import_react5.useRef)(0);
172
175
  const scrollToBottom = () => {
@@ -175,11 +178,11 @@ var ThreadViewport = (0, import_react5.forwardRef)(({ autoScroll = true, onScrol
175
178
  return;
176
179
  const behavior = firstRenderRef.current ? "instant" : "auto";
177
180
  firstRenderRef.current = false;
178
- useThread.setState({ isAtBottom: true });
181
+ useViewport.setState({ isAtBottom: true });
179
182
  div.scrollIntoView({ behavior });
180
183
  };
181
184
  useOnResizeContent(divRef, () => {
182
- if (!useThread.getState().isAtBottom)
185
+ if (!useViewport.getState().isAtBottom)
183
186
  return;
184
187
  scrollToBottom();
185
188
  });
@@ -190,173 +193,36 @@ var ThreadViewport = (0, import_react5.forwardRef)(({ autoScroll = true, onScrol
190
193
  const div = divRef.current;
191
194
  if (!div)
192
195
  return;
193
- const isAtBottom = useThread.getState().isAtBottom;
196
+ const isAtBottom = useViewport.getState().isAtBottom;
194
197
  const newIsAtBottom = div.scrollHeight - div.scrollTop <= div.clientHeight;
195
198
  if (!newIsAtBottom && lastScrollTop.current < div.scrollTop) {
196
199
  } else if (newIsAtBottom !== isAtBottom) {
197
- useThread.setState({ isAtBottom: newIsAtBottom });
200
+ useViewport.setState({ isAtBottom: newIsAtBottom });
198
201
  }
199
202
  lastScrollTop.current = div.scrollTop;
200
203
  };
201
- return /* @__PURE__ */ React.createElement(
204
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
202
205
  import_react_primitive2.Primitive.div,
203
206
  {
204
207
  ...rest,
205
208
  onScroll: (0, import_primitive.composeEventHandlers)(onScroll, handleScroll),
206
- ref
207
- },
208
- children,
209
- /* @__PURE__ */ React.createElement("div", { ref: messagesEndRef })
210
- );
211
- });
212
-
213
- // src/adapters/vercel/useVercelAIBranches.tsx
214
- var import_react6 = require("react");
215
-
216
- // src/utils/context/stores/AssistantTypes.ts
217
- var ROOT_PARENT_ID = "__ROOT_ID__";
218
-
219
- // src/adapters/vercel/useVercelAIBranches.tsx
220
- var UPCOMING_MESSAGE_ID = "__UPCOMING_MESSAGE_ID__";
221
- var updateBranchData = (data, messages) => {
222
- for (let i = 0; i < messages.length; i++) {
223
- const child = messages[i];
224
- const childId = child.id;
225
- const parentId = messages[i - 1]?.id ?? ROOT_PARENT_ID;
226
- data.parentMap.set(childId, parentId);
227
- const parentArray = data.branchMap.get(parentId);
228
- if (!parentArray) {
229
- data.branchMap.set(parentId, [childId]);
230
- } else if (!parentArray.includes(childId)) {
231
- parentArray.push(childId);
209
+ ref,
210
+ children: [
211
+ children,
212
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ref: messagesEndRef })
213
+ ]
232
214
  }
233
- data.snapshots.set(childId, messages);
234
- }
235
- };
236
- var getParentId = (data, messages, messageId) => {
237
- if (messageId === UPCOMING_MESSAGE_ID) {
238
- const parent = messages.at(-1);
239
- if (!parent)
240
- return ROOT_PARENT_ID;
241
- return parent.id;
242
- }
243
- const parentId = data.parentMap.get(messageId);
244
- if (!parentId)
245
- throw new Error("Unexpected: Message parent not found");
246
- return parentId;
247
- };
248
- var getBranchStateImpl = (data, messages, messageId) => {
249
- const parentId = getParentId(data, messages, messageId);
250
- const branches = data.branchMap.get(parentId) ?? [];
251
- const branchId = messageId === UPCOMING_MESSAGE_ID ? branches.length : branches.indexOf(messageId);
252
- if (branchId === -1)
253
- throw new Error("Unexpected: Message not found in parent children");
254
- const upcomingOffset = messageId === UPCOMING_MESSAGE_ID ? 1 : 0;
255
- return {
256
- branchId,
257
- branchCount: branches.length + upcomingOffset
258
- };
259
- };
260
- var switchToBranchImpl = (data, messages, messageId, branchId) => {
261
- const parentId = getParentId(data, messages, messageId);
262
- const branches = data.branchMap.get(parentId);
263
- if (!branches)
264
- throw new Error("Unexpected: Parent children not found");
265
- const newMessageId = branches[branchId];
266
- if (!newMessageId)
267
- throw new Error("Unexpected: Requested branch not found");
268
- if (branchId < 0 || branchId >= branches.length)
269
- throw new Error("Switch to branch called with a branch index out of range");
270
- if (newMessageId === messageId)
271
- return messages;
272
- const snapshot = data.snapshots.get(newMessageId);
273
- if (!snapshot)
274
- throw new Error("Unexpected: Branch snapshot not found");
275
- return snapshot;
276
- };
277
- var sliceMessagesUntil = (messages, messageId) => {
278
- if (messageId === ROOT_PARENT_ID)
279
- return [];
280
- if (messageId === UPCOMING_MESSAGE_ID)
281
- return messages;
282
- const messageIdx = messages.findIndex((m) => m.id === messageId);
283
- if (messageIdx === -1)
284
- throw new Error("Unexpected: Message not found");
285
- return messages.slice(0, messageIdx + 1);
286
- };
287
- var useVercelAIBranches = (chat, context) => {
288
- const data = (0, import_react6.useRef)({
289
- parentMap: /* @__PURE__ */ new Map(),
290
- branchMap: /* @__PURE__ */ new Map(),
291
- snapshots: /* @__PURE__ */ new Map()
292
- }).current;
293
- updateBranchData(data, chat.messages);
294
- const getBranchState = (0, import_react6.useCallback)(
295
- (messageId) => {
296
- return getBranchStateImpl(data, chat.messages, messageId);
297
- },
298
- [data, chat.messages]
299
215
  );
300
- const switchToBranch = (0, import_react6.useCallback)(
301
- (messageId, branchId) => {
302
- const newMessages = switchToBranchImpl(
303
- data,
304
- chat.messages,
305
- messageId,
306
- branchId
307
- );
308
- chat.setMessages(newMessages);
309
- },
310
- [data, chat.messages, chat.setMessages]
311
- );
312
- const reloadMaybe = "reload" in chat ? chat.reload : void 0;
313
- const reload = (0, import_react6.useCallback)(
314
- async (messageId) => {
315
- if (!reloadMaybe)
316
- throw new Error("Reload not supported by Vercel AI SDK's useAssistant");
317
- const newMessages = sliceMessagesUntil(chat.messages, messageId);
318
- chat.setMessages(newMessages);
319
- context.useThread.getState().scrollToBottom();
320
- await reloadMaybe();
321
- },
322
- [context, chat.messages, chat.setMessages, reloadMaybe]
323
- );
324
- const append = (0, import_react6.useCallback)(
325
- async (message) => {
326
- const newMessages = sliceMessagesUntil(chat.messages, message.parentId);
327
- chat.setMessages(newMessages);
328
- if (message.content.length !== 1 || message.content[0]?.type !== "text")
329
- throw new Error("Only text content is currently supported");
330
- context.useThread.getState().scrollToBottom();
331
- await chat.append({
332
- role: "user",
333
- content: message.content[0].text
334
- });
335
- },
336
- [context, chat.messages, chat.setMessages, chat.append]
337
- );
338
- return (0, import_react6.useMemo)(
339
- () => ({
340
- getBranchState,
341
- switchToBranch,
342
- append,
343
- reload
344
- }),
345
- [getBranchState, switchToBranch, append, reload]
346
- );
347
- };
348
- var hasUpcomingMessage = (thread) => {
349
- return thread.isLoading && thread.messages[thread.messages.length - 1]?.role !== "assistant";
350
- };
216
+ });
351
217
 
352
218
  // src/utils/context/useComposerContext.ts
353
- var import_react8 = require("react");
219
+ var import_react7 = require("react");
354
220
 
355
221
  // src/utils/context/useMessageContext.ts
356
- var import_react7 = require("react");
357
- var MessageContext = (0, import_react7.createContext)(null);
222
+ var import_react6 = require("react");
223
+ var MessageContext = (0, import_react6.createContext)(null);
358
224
  var useMessageContext = () => {
359
- const context = (0, import_react7.useContext)(MessageContext);
225
+ const context = (0, import_react6.useContext)(MessageContext);
360
226
  if (!context)
361
227
  throw new Error("useMessageContext must be used within a MessageProvider");
362
228
  return context;
@@ -365,7 +231,7 @@ var useMessageContext = () => {
365
231
  // src/utils/context/useComposerContext.ts
366
232
  var useComposerContext = () => {
367
233
  const { useComposer: useAssisstantComposer } = useAssistantContext();
368
- const { useComposer: useMessageComposer } = (0, import_react8.useContext)(MessageContext) ?? {};
234
+ const { useComposer: useMessageComposer } = (0, import_react7.useContext)(MessageContext) ?? {};
369
235
  return {
370
236
  useComposer: useMessageComposer ?? useAssisstantComposer,
371
237
  type: useMessageComposer ? "message" : "assistant"
@@ -398,17 +264,72 @@ __export(message_exports, {
398
264
  });
399
265
 
400
266
  // src/primitives/message/MessageProvider.tsx
401
- var import_react9 = require("react");
267
+ var import_react8 = require("react");
268
+ var import_zustand2 = require("zustand");
269
+
270
+ // src/utils/context/stores/ComposerStore.ts
402
271
  var import_zustand = require("zustand");
272
+ var makeBaseComposer = (set) => ({
273
+ value: "",
274
+ setValue: (value) => {
275
+ set({ value });
276
+ }
277
+ });
278
+ var makeMessageComposerStore = ({
279
+ onEdit,
280
+ onSend
281
+ }) => (0, import_zustand.create)()((set, get, store) => ({
282
+ ...makeBaseComposer(set, get, store),
283
+ isEditing: false,
284
+ edit: () => {
285
+ const value = onEdit();
286
+ set({ isEditing: true, value });
287
+ },
288
+ send: () => {
289
+ const value = get().value;
290
+ set({ isEditing: false });
291
+ onSend(value);
292
+ },
293
+ cancel: () => {
294
+ if (!get().isEditing)
295
+ return false;
296
+ set({ isEditing: false });
297
+ return true;
298
+ }
299
+ }));
300
+ var makeThreadComposerStore = (useThread) => (0, import_zustand.create)()((set, get, store) => {
301
+ return {
302
+ ...makeBaseComposer(set, get, store),
303
+ isEditing: true,
304
+ send: () => {
305
+ const { value } = get();
306
+ set({ value: "" });
307
+ useThread.getState().append({
308
+ parentId: useThread.getState().messages.at(-1)?.id ?? null,
309
+ content: [{ type: "text", text: value }]
310
+ });
311
+ },
312
+ cancel: () => {
313
+ const thread = useThread.getState();
314
+ if (!thread.isRunning)
315
+ return false;
316
+ useThread.getState().cancelRun();
317
+ return true;
318
+ }
319
+ };
320
+ });
321
+
322
+ // src/primitives/message/MessageProvider.tsx
323
+ var import_jsx_runtime4 = require("react/jsx-runtime");
403
324
  var getIsLast = (thread, message) => {
404
- const hasUpcoming = hasUpcomingMessage(thread);
405
- return hasUpcoming ? message.id === UPCOMING_MESSAGE_ID : thread.messages[thread.messages.length - 1]?.id === message.id;
325
+ return thread.messages[thread.messages.length - 1]?.id === message.id;
406
326
  };
407
327
  var useMessageContext2 = () => {
408
- const [context] = (0, import_react9.useState)(() => {
409
- const { useThread } = useAssistantContext();
410
- const useMessage = (0, import_zustand.create)(() => ({
328
+ const { useThread } = useAssistantContext();
329
+ const [context] = (0, import_react8.useState)(() => {
330
+ const useMessage = (0, import_zustand2.create)(() => ({
411
331
  message: null,
332
+ branches: [],
412
333
  isLast: false,
413
334
  isCopied: false,
414
335
  isHovering: false,
@@ -417,34 +338,23 @@ var useMessageContext2 = () => {
417
338
  setIsHovering: () => {
418
339
  }
419
340
  }));
420
- const useComposer = (0, import_zustand.create)((set, get) => ({
421
- isEditing: false,
422
- canCancel: true,
423
- edit: () => {
341
+ const useComposer = makeMessageComposerStore({
342
+ onEdit: () => {
424
343
  const message = useMessage.getState().message;
425
344
  if (message.role !== "user")
426
345
  throw new Error("Editing is only supported for user messages");
427
346
  if (message.content[0]?.type !== "text")
428
347
  throw new Error("Editing is only supported for text-only messages");
429
- return set({
430
- isEditing: true,
431
- value: message.content[0].text
432
- });
348
+ return message.content[0].text;
433
349
  },
434
- cancel: () => set({ isEditing: false }),
435
- send: () => {
350
+ onSend: (text) => {
436
351
  const message = useMessage.getState().message;
437
- if (message.role !== "user")
438
- throw new Error("Editing is only supported for user messages");
439
352
  useThread.getState().append({
440
353
  parentId: message.parentId,
441
- content: [{ type: "text", text: get().value }]
354
+ content: [{ type: "text", text }]
442
355
  });
443
- set({ isEditing: false });
444
- },
445
- value: "",
446
- setValue: (value) => set({ value })
447
- }));
356
+ }
357
+ });
448
358
  return { useMessage, useComposer };
449
359
  });
450
360
  return context;
@@ -456,12 +366,14 @@ var MessageProvider = ({
456
366
  const { useThread } = useAssistantContext();
457
367
  const context = useMessageContext2();
458
368
  const isLast = useThread((thread) => getIsLast(thread, message));
459
- const [isCopied, setIsCopied] = (0, import_react9.useState)(false);
460
- const [isHovering, setIsHovering] = (0, import_react9.useState)(false);
461
- (0, import_react9.useMemo)(() => {
369
+ const branches = useThread((thread) => thread.getBranches(message.id));
370
+ const [isCopied, setIsCopied] = (0, import_react8.useState)(false);
371
+ const [isHovering, setIsHovering] = (0, import_react8.useState)(false);
372
+ (0, import_react8.useMemo)(() => {
462
373
  context.useMessage.setState(
463
374
  {
464
375
  message,
376
+ branches,
465
377
  isLast,
466
378
  isCopied,
467
379
  isHovering,
@@ -470,15 +382,16 @@ var MessageProvider = ({
470
382
  },
471
383
  true
472
384
  );
473
- }, [context, message, isLast, isCopied, isHovering]);
474
- return /* @__PURE__ */ React.createElement(MessageContext.Provider, { value: context }, children);
385
+ }, [context, message, branches, isLast, isCopied, isHovering]);
386
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MessageContext.Provider, { value: context, children });
475
387
  };
476
388
 
477
389
  // src/primitives/message/MessageRoot.tsx
478
390
  var import_primitive2 = require("@radix-ui/primitive");
479
391
  var import_react_primitive3 = require("@radix-ui/react-primitive");
480
- var import_react10 = require("react");
481
- var MessageRoot = (0, import_react10.forwardRef)(
392
+ var import_react9 = require("react");
393
+ var import_jsx_runtime5 = require("react/jsx-runtime");
394
+ var MessageRoot = (0, import_react9.forwardRef)(
482
395
  ({ onMouseEnter, onMouseLeave, ...rest }, ref) => {
483
396
  const { useMessage } = useMessageContext();
484
397
  const setIsHovering = useMessage((s) => s.setIsHovering);
@@ -488,7 +401,7 @@ var MessageRoot = (0, import_react10.forwardRef)(
488
401
  const handleMouseLeave = () => {
489
402
  setIsHovering(false);
490
403
  };
491
- return /* @__PURE__ */ React.createElement(
404
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
492
405
  import_react_primitive3.Primitive.div,
493
406
  {
494
407
  ...rest,
@@ -503,8 +416,8 @@ var MessageRoot = (0, import_react10.forwardRef)(
503
416
  // src/primitives/message/MessageIf.tsx
504
417
  var useMessageIf = (props) => {
505
418
  const { useMessage } = useMessageContext();
506
- return useMessage(({ message, isLast, isCopied, isHovering }) => {
507
- if (props.hasBranches === true && message.branchCount < 2)
419
+ return useMessage(({ message, branches, isLast, isCopied, isHovering }) => {
420
+ if (props.hasBranches === true && branches.length < 2)
508
421
  return false;
509
422
  if (props.user && message.role !== "user")
510
423
  return false;
@@ -525,8 +438,9 @@ var MessageIf = ({ children, ...query }) => {
525
438
  };
526
439
 
527
440
  // src/primitives/message/MessageContent.tsx
441
+ var import_jsx_runtime6 = require("react/jsx-runtime");
528
442
  var defaultComponents = {
529
- Text: ({ part }) => /* @__PURE__ */ React.createElement(React.Fragment, null, part.text),
443
+ Text: ({ part }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: part.text }),
530
444
  Image: () => null,
531
445
  UI: ({ part }) => part.display,
532
446
  tools: {
@@ -543,26 +457,27 @@ var MessageContent = ({
543
457
  }) => {
544
458
  const { useMessage } = useMessageContext();
545
459
  const content = useMessage((s) => s.message.content);
546
- return /* @__PURE__ */ React.createElement(React.Fragment, null, content.map((part, i) => {
460
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: content.map((part, i) => {
547
461
  const key = i;
548
462
  switch (part.type) {
549
463
  case "text":
550
- return /* @__PURE__ */ React.createElement(Text, { key, part });
464
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { part }, key);
551
465
  case "image":
552
- return /* @__PURE__ */ React.createElement(Image, { key, part });
466
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Image, { part }, key);
553
467
  case "ui":
554
- return /* @__PURE__ */ React.createElement(UI, { key, part });
468
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(UI, { part }, key);
555
469
  case "tool-call": {
556
470
  const Tool = by_name[part.name] || Fallback;
557
- return /* @__PURE__ */ React.createElement(Tool, { key, part });
471
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Tool, { part }, key);
558
472
  }
559
473
  default:
560
474
  return null;
561
475
  }
562
- }));
476
+ }) });
563
477
  };
564
478
 
565
479
  // src/primitives/thread/ThreadMessages.tsx
480
+ var import_jsx_runtime7 = require("react/jsx-runtime");
566
481
  var getComponents = (components) => {
567
482
  return {
568
483
  EditComposer: components.EditComposer ?? components.UserMessage ?? components.Message,
@@ -577,41 +492,32 @@ var ThreadMessages = ({ components }) => {
577
492
  const { UserMessage, EditComposer, AssistantMessage } = getComponents(components);
578
493
  if (messages.length === 0)
579
494
  return null;
580
- return /* @__PURE__ */ React.createElement(React.Fragment, null, messages.map((message, idx) => {
495
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: messages.map((message, idx) => {
581
496
  return (
582
497
  // biome-ignore lint/suspicious/noArrayIndexKey: fixes a11y issues with branch navigation
583
- /* @__PURE__ */ React.createElement(MessageProvider, { key: idx, message }, /* @__PURE__ */ React.createElement(MessageIf, { user: true }, /* @__PURE__ */ React.createElement(ComposerIf, { editing: false }, /* @__PURE__ */ React.createElement(UserMessage, null)), /* @__PURE__ */ React.createElement(ComposerIf, { editing: true }, /* @__PURE__ */ React.createElement(EditComposer, null))), /* @__PURE__ */ React.createElement(MessageIf, { assistant: true }, /* @__PURE__ */ React.createElement(AssistantMessage, null)))
498
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(MessageProvider, { message, children: [
499
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(MessageIf, { user: true, children: [
500
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ComposerIf, { editing: false, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(UserMessage, {}) }),
501
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ComposerIf, { editing: true, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(EditComposer, {}) })
502
+ ] }),
503
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MessageIf, { assistant: true, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AssistantMessage, {}) })
504
+ ] }, idx)
584
505
  );
585
- }), hasUpcomingMessage(thread) && /* @__PURE__ */ React.createElement(
586
- MessageProvider,
587
- {
588
- message: {
589
- id: UPCOMING_MESSAGE_ID,
590
- role: "assistant",
591
- content: [{ type: "text", text: "..." }],
592
- parentId: messages.at(-1)?.id ?? ROOT_PARENT_ID,
593
- // TODO fix these (move upcoming message to AssistantContext)
594
- branchId: 0,
595
- branchCount: 1,
596
- createdAt: /* @__PURE__ */ new Date()
597
- }
598
- },
599
- /* @__PURE__ */ React.createElement(AssistantMessage, null)
600
- ));
506
+ }) });
601
507
  };
602
508
 
603
509
  // src/primitives/thread/ThreadScrollToBottom.tsx
604
510
  var import_primitive3 = require("@radix-ui/primitive");
605
511
  var import_react_primitive4 = require("@radix-ui/react-primitive");
606
- var import_react11 = require("react");
607
- var ThreadScrollToBottom = (0, import_react11.forwardRef)(({ onClick, ...rest }, ref) => {
608
- const { useThread } = useAssistantContext();
609
- const isAtBottom = useThread((s) => s.isAtBottom);
512
+ var import_react10 = require("react");
513
+ var import_jsx_runtime8 = require("react/jsx-runtime");
514
+ var ThreadScrollToBottom = (0, import_react10.forwardRef)(({ onClick, ...rest }, ref) => {
515
+ const { useViewport } = useAssistantContext();
516
+ const isAtBottom = useViewport((s) => s.isAtBottom);
610
517
  const handleScrollToBottom = () => {
611
- const thread = useThread.getState();
612
- thread.scrollToBottom();
518
+ useViewport.getState().scrollToBottom();
613
519
  };
614
- return /* @__PURE__ */ React.createElement(
520
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
615
521
  import_react_primitive4.Primitive.button,
616
522
  {
617
523
  ...rest,
@@ -636,11 +542,13 @@ __export(composer_exports, {
636
542
  var import_primitive4 = require("@radix-ui/primitive");
637
543
  var import_react_compose_refs2 = require("@radix-ui/react-compose-refs");
638
544
  var import_react_primitive5 = require("@radix-ui/react-primitive");
639
- var import_react12 = require("react");
640
- var ComposerRoot = (0, import_react12.forwardRef)(
545
+ var import_react11 = require("react");
546
+ var import_jsx_runtime9 = require("react/jsx-runtime");
547
+ var ComposerRoot = (0, import_react11.forwardRef)(
641
548
  ({ onSubmit, ...rest }, forwardedRef) => {
549
+ const { useViewport } = useAssistantContext();
642
550
  const { useComposer } = useComposerContext();
643
- const formRef = (0, import_react12.useRef)(null);
551
+ const formRef = (0, import_react11.useRef)(null);
644
552
  const ref = (0, import_react_compose_refs2.useComposedRefs)(forwardedRef, formRef);
645
553
  const handleSubmit = (e) => {
646
554
  const composerState = useComposer.getState();
@@ -648,8 +556,9 @@ var ComposerRoot = (0, import_react12.forwardRef)(
648
556
  return;
649
557
  e.preventDefault();
650
558
  composerState.send();
559
+ useViewport.getState().scrollToBottom();
651
560
  };
652
- return /* @__PURE__ */ React.createElement(
561
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
653
562
  import_react_primitive5.Primitive.form,
654
563
  {
655
564
  ...rest,
@@ -664,11 +573,12 @@ var ComposerRoot = (0, import_react12.forwardRef)(
664
573
  var import_primitive5 = require("@radix-ui/primitive");
665
574
  var import_react_compose_refs3 = require("@radix-ui/react-compose-refs");
666
575
  var import_react_slot = require("@radix-ui/react-slot");
667
- var import_react13 = require("react");
576
+ var import_react12 = require("react");
668
577
  var import_react_textarea_autosize = __toESM(require("react-textarea-autosize"));
669
- var ComposerInput = (0, import_react13.forwardRef)(
578
+ var import_jsx_runtime10 = require("react/jsx-runtime");
579
+ var ComposerInput = (0, import_react12.forwardRef)(
670
580
  ({ autoFocus, asChild, disabled, onChange, onKeyDown, ...rest }, forwardedRef) => {
671
- const { useThread } = useAssistantContext();
581
+ const { useThread, useViewport } = useAssistantContext();
672
582
  const { useComposer, type } = useComposerContext();
673
583
  const value = useComposer((c) => {
674
584
  if (!c.isEditing)
@@ -680,22 +590,23 @@ var ComposerInput = (0, import_react13.forwardRef)(
680
590
  if (disabled)
681
591
  return;
682
592
  const composer = useComposer.getState();
683
- if (e.key === "Escape" && composer.canCancel) {
684
- e.preventDefault();
685
- useComposer.getState().cancel();
686
- }
687
- if (e.key === "Enter" && e.shiftKey === false) {
688
- const isLoading = useThread.getState().isLoading;
689
- if (!isLoading) {
593
+ if (e.key === "Escape") {
594
+ if (useComposer.getState().cancel()) {
595
+ e.preventDefault();
596
+ }
597
+ } else if (e.key === "Enter" && e.shiftKey === false) {
598
+ const isRunning = useThread.getState().isRunning;
599
+ if (!isRunning) {
690
600
  e.preventDefault();
691
601
  composer.send();
602
+ useViewport.getState().scrollToBottom();
692
603
  }
693
604
  }
694
605
  };
695
- const textareaRef = (0, import_react13.useRef)(null);
606
+ const textareaRef = (0, import_react12.useRef)(null);
696
607
  const ref = (0, import_react_compose_refs3.useComposedRefs)(forwardedRef, textareaRef);
697
608
  const autoFocusEnabled = autoFocus !== false && !disabled;
698
- const focus = (0, import_react13.useCallback)(() => {
609
+ const focus = (0, import_react12.useCallback)(() => {
699
610
  const textarea = textareaRef.current;
700
611
  if (!textarea || !autoFocusEnabled)
701
612
  return;
@@ -705,13 +616,13 @@ var ComposerInput = (0, import_react13.forwardRef)(
705
616
  textareaRef.current.value.length
706
617
  );
707
618
  }, [autoFocusEnabled]);
708
- (0, import_react13.useEffect)(() => focus(), [focus]);
619
+ (0, import_react12.useEffect)(() => focus(), [focus]);
709
620
  useOnScrollToBottom(() => {
710
621
  if (type === "assistant") {
711
622
  focus();
712
623
  }
713
624
  });
714
- return /* @__PURE__ */ React.createElement(
625
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
715
626
  Component,
716
627
  {
717
628
  value,
@@ -732,12 +643,13 @@ var ComposerInput = (0, import_react13.forwardRef)(
732
643
 
733
644
  // src/primitives/composer/ComposerSend.tsx
734
645
  var import_react_primitive6 = require("@radix-ui/react-primitive");
735
- var import_react14 = require("react");
736
- var ComposerSend = (0, import_react14.forwardRef)(
646
+ var import_react13 = require("react");
647
+ var import_jsx_runtime11 = require("react/jsx-runtime");
648
+ var ComposerSend = (0, import_react13.forwardRef)(
737
649
  ({ disabled, ...rest }, ref) => {
738
650
  const { useComposer } = useComposerContext();
739
651
  const hasValue = useComposer((c) => c.isEditing && c.value.length > 0);
740
- return /* @__PURE__ */ React.createElement(
652
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
741
653
  import_react_primitive6.Primitive.button,
742
654
  {
743
655
  type: "submit",
@@ -752,21 +664,20 @@ var ComposerSend = (0, import_react14.forwardRef)(
752
664
  // src/primitives/composer/ComposerCancel.tsx
753
665
  var import_primitive6 = require("@radix-ui/primitive");
754
666
  var import_react_primitive7 = require("@radix-ui/react-primitive");
755
- var import_react15 = require("react");
756
- var ComposerCancel = (0, import_react15.forwardRef)(({ disabled, onClick, ...rest }, ref) => {
667
+ var import_react14 = require("react");
668
+ var import_jsx_runtime12 = require("react/jsx-runtime");
669
+ var ComposerCancel = (0, import_react14.forwardRef)(({ onClick, ...rest }, ref) => {
757
670
  const { useComposer } = useComposerContext();
758
- const hasValue = useComposer((c) => c.canCancel);
759
- const handleClose = () => {
671
+ const handleCancel = () => {
760
672
  useComposer.getState().cancel();
761
673
  };
762
- return /* @__PURE__ */ React.createElement(
674
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
763
675
  import_react_primitive7.Primitive.button,
764
676
  {
765
677
  type: "button",
766
678
  ...rest,
767
679
  ref,
768
- onClick: (0, import_primitive6.composeEventHandlers)(onClick, handleClose),
769
- disabled: disabled || !hasValue
680
+ onClick: (0, import_primitive6.composeEventHandlers)(onClick, handleCancel)
770
681
  }
771
682
  );
772
683
  });
@@ -782,10 +693,10 @@ __export(branchPicker_exports, {
782
693
  });
783
694
 
784
695
  // src/utils/context/combined/useCombinedStore.ts
785
- var import_react17 = require("react");
696
+ var import_react16 = require("react");
786
697
 
787
698
  // src/utils/context/combined/createCombinedStore.ts
788
- var import_react16 = require("react");
699
+ var import_react15 = require("react");
789
700
  var createCombinedStore = (stores) => {
790
701
  const subscribe = (callback) => {
791
702
  const unsubscribes = stores.map((store) => store.subscribe(callback));
@@ -797,41 +708,42 @@ var createCombinedStore = (stores) => {
797
708
  };
798
709
  return (selector) => {
799
710
  const getSnapshot = () => selector(...stores.map((store) => store.getState()));
800
- return (0, import_react16.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
711
+ return (0, import_react15.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
801
712
  };
802
713
  };
803
714
 
804
715
  // src/utils/context/combined/useCombinedStore.ts
805
716
  var useCombinedStore = (stores, selector) => {
806
- const useCombined = (0, import_react17.useMemo)(() => createCombinedStore(stores), stores);
717
+ const useCombined = (0, import_react16.useMemo)(() => createCombinedStore(stores), stores);
807
718
  return useCombined(selector);
808
719
  };
809
720
 
810
721
  // src/actions/useGoToNextBranch.tsx
811
722
  var useGoToNextBranch = () => {
812
723
  const { useThread } = useAssistantContext();
813
- const { useComposer, useMessage } = useMessageContext();
724
+ const { useMessage, useComposer } = useMessageContext();
814
725
  const disabled = useCombinedStore(
815
- [useThread, useComposer, useMessage],
816
- (t, c, m) => t.isLoading || c.isEditing || m.message.branchId + 1 >= m.message.branchCount
726
+ [useMessage, useComposer],
727
+ (m, c) => c.isEditing || m.branches.indexOf(m.message.id) + 1 >= m.branches.length
817
728
  );
818
729
  if (disabled)
819
730
  return null;
820
731
  return () => {
821
- const { message } = useMessage.getState();
822
- useThread.getState().switchToBranch(message.id, message.branchId + 1);
732
+ const { message, branches } = useMessage.getState();
733
+ useThread.getState().switchToBranch(branches[branches.indexOf(message.id) + 1]);
823
734
  };
824
735
  };
825
736
 
826
737
  // src/utils/createActionButton.tsx
827
- var import_react18 = require("react");
828
- var import_react_primitive8 = require("@radix-ui/react-primitive");
829
738
  var import_primitive7 = require("@radix-ui/primitive");
739
+ var import_react_primitive8 = require("@radix-ui/react-primitive");
740
+ var import_react17 = require("react");
741
+ var import_jsx_runtime13 = require("react/jsx-runtime");
830
742
  var createActionButton = (useActionButton) => {
831
- return (0, import_react18.forwardRef)(
743
+ return (0, import_react17.forwardRef)(
832
744
  (props, forwardedRef) => {
833
745
  const onClick = useActionButton(props);
834
- return /* @__PURE__ */ React.createElement(
746
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
835
747
  import_react_primitive8.Primitive.button,
836
748
  {
837
749
  type: "button",
@@ -851,16 +763,19 @@ var BranchPickerNext = createActionButton(useGoToNextBranch);
851
763
  // src/actions/useGoToPreviousBranch.tsx
852
764
  var useGoToPreviousBranch = () => {
853
765
  const { useThread } = useAssistantContext();
854
- const { useComposer, useMessage } = useMessageContext();
766
+ const { useMessage, useComposer } = useMessageContext();
855
767
  const disabled = useCombinedStore(
856
- [useThread, useComposer, useMessage],
857
- (t, c, m) => t.isLoading || c.isEditing || m.message.branchId <= 0
768
+ [useMessage, useComposer],
769
+ (m, c) => c.isEditing || m.branches.indexOf(m.message.id) <= 0
858
770
  );
859
771
  if (disabled)
860
772
  return null;
861
773
  return () => {
862
- const { message } = useMessage.getState();
863
- useThread.getState().switchToBranch(message.id, message.branchId - 1);
774
+ const { message, branches } = useMessage.getState();
775
+ useThread.getState().switchToBranch(
776
+ branches[branches.indexOf(message.id) - 1]
777
+ // TODO probably there's a more elegant way to do this
778
+ );
864
779
  };
865
780
  };
866
781
 
@@ -868,24 +783,27 @@ var useGoToPreviousBranch = () => {
868
783
  var BranchPickerPrevious = createActionButton(useGoToPreviousBranch);
869
784
 
870
785
  // src/primitives/branchPicker/BranchPickerCount.tsx
786
+ var import_jsx_runtime14 = require("react/jsx-runtime");
871
787
  var BranchPickerCount = () => {
872
788
  const { useMessage } = useMessageContext();
873
- const branchCount = useMessage((s) => s.message.branchCount);
874
- return /* @__PURE__ */ React.createElement(React.Fragment, null, branchCount);
789
+ const branchCount = useMessage((s) => s.branches.length);
790
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_jsx_runtime14.Fragment, { children: branchCount });
875
791
  };
876
792
 
877
793
  // src/primitives/branchPicker/BranchPickerNumber.tsx
794
+ var import_jsx_runtime15 = require("react/jsx-runtime");
878
795
  var BranchPickerNumber = () => {
879
796
  const { useMessage } = useMessageContext();
880
- const branchId = useMessage((s) => s.message.branchId);
881
- return /* @__PURE__ */ React.createElement(React.Fragment, null, branchId + 1);
797
+ const branchIdx = useMessage((s) => s.branches.indexOf(s.message.id));
798
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_jsx_runtime15.Fragment, { children: branchIdx + 1 });
882
799
  };
883
800
 
884
801
  // src/primitives/branchPicker/BranchPickerRoot.tsx
885
802
  var import_react_primitive9 = require("@radix-ui/react-primitive");
886
- var import_react19 = require("react");
887
- var BranchPickerRoot = (0, import_react19.forwardRef)(({ hideWhenSingleBranch, ...rest }, ref) => {
888
- return /* @__PURE__ */ React.createElement(MessageIf, { hasBranches: hideWhenSingleBranch ? true : void 0 }, /* @__PURE__ */ React.createElement(import_react_primitive9.Primitive.div, { ...rest, ref }));
803
+ var import_react18 = require("react");
804
+ var import_jsx_runtime16 = require("react/jsx-runtime");
805
+ var BranchPickerRoot = (0, import_react18.forwardRef)(({ hideWhenSingleBranch, ...rest }, ref) => {
806
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(MessageIf, { hasBranches: hideWhenSingleBranch ? true : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_primitive9.Primitive.div, { ...rest, ref }) });
889
807
  });
890
808
 
891
809
  // src/primitives/actionBar/index.ts
@@ -899,28 +817,29 @@ __export(actionBar_exports, {
899
817
 
900
818
  // src/primitives/actionBar/ActionBarRoot.tsx
901
819
  var import_react_primitive10 = require("@radix-ui/react-primitive");
902
- var import_react20 = require("react");
903
- var ActionBarRoot = (0, import_react20.forwardRef)(({ hideWhenBusy, autohide, autohideFloat, ...rest }, ref) => {
820
+ var import_react19 = require("react");
821
+ var import_jsx_runtime17 = require("react/jsx-runtime");
822
+ var ActionBarRoot = (0, import_react19.forwardRef)(({ hideWhenRunning, autohide, autohideFloat, ...rest }, ref) => {
904
823
  const { useThread } = useAssistantContext();
905
824
  const { useMessage } = useMessageContext();
906
825
  const hideAndfloatStatus = useCombinedStore(
907
826
  [useThread, useMessage],
908
827
  (t, m) => {
909
- if (hideWhenBusy && t.isLoading)
828
+ if (hideWhenRunning && t.isRunning)
910
829
  return "hidden" /* Hidden */;
911
830
  const autohideEnabled = autohide === "always" || autohide === "not-last" && !m.isLast;
912
831
  if (!autohideEnabled)
913
832
  return "normal" /* Normal */;
914
833
  if (!m.isHovering)
915
834
  return "hidden" /* Hidden */;
916
- if (autohideFloat === "always" || autohideFloat === "single-branch" && m.message.branchCount <= 1)
835
+ if (autohideFloat === "always" || autohideFloat === "single-branch" && m.branches.length <= 1)
917
836
  return "floating" /* Floating */;
918
837
  return "normal" /* Normal */;
919
838
  }
920
839
  );
921
840
  if (hideAndfloatStatus === "hidden" /* Hidden */)
922
841
  return null;
923
- return /* @__PURE__ */ React.createElement(
842
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
924
843
  import_react_primitive10.Primitive.div,
925
844
  {
926
845
  "data-floating": hideAndfloatStatus === "floating" /* Floating */,
@@ -951,11 +870,11 @@ var ActionBarCopy = createActionButton(useCopyMessage);
951
870
 
952
871
  // src/actions/useReloadMessage.tsx
953
872
  var useReloadMessage = () => {
954
- const { useThread } = useAssistantContext();
873
+ const { useThread, useViewport } = useAssistantContext();
955
874
  const { useMessage } = useMessageContext();
956
875
  const disabled = useCombinedStore(
957
876
  [useThread, useMessage],
958
- (t, m) => t.isLoading || m.message.role !== "assistant"
877
+ (t, m) => t.isRunning || m.message.role !== "assistant"
959
878
  );
960
879
  if (disabled)
961
880
  return null;
@@ -963,7 +882,8 @@ var useReloadMessage = () => {
963
882
  const message = useMessage.getState().message;
964
883
  if (message.role !== "assistant")
965
884
  throw new Error("Reloading is only supported on assistant messages");
966
- useThread.getState().reload(message.id);
885
+ useThread.getState().startRun(message.parentId);
886
+ useViewport.getState().scrollToBottom();
967
887
  };
968
888
  };
969
889
 
@@ -992,68 +912,309 @@ var ActionBarEdit = createActionButton(useBeginMessageEdit);
992
912
  var import_react22 = require("react");
993
913
 
994
914
  // src/adapters/vercel/useDummyAIAssistantContext.tsx
995
- var import_react21 = require("react");
996
- var import_zustand2 = require("zustand");
997
- var useDummyAIAssistantContext = () => {
998
- const [context] = (0, import_react21.useState)(() => {
999
- const scrollToBottomListeners = /* @__PURE__ */ new Set();
1000
- const useThread = (0, import_zustand2.create)()(() => ({
1001
- messages: [],
1002
- isLoading: false,
1003
- append: async () => {
1004
- throw new Error("Not implemented");
1005
- },
1006
- stop: () => {
1007
- throw new Error("Not implemented");
1008
- },
1009
- switchToBranch: () => {
1010
- throw new Error("Not implemented");
1011
- },
1012
- reload: async () => {
1013
- throw new Error("Not implemented");
1014
- },
1015
- isAtBottom: true,
1016
- scrollToBottom: () => {
1017
- for (const listener of scrollToBottomListeners) {
1018
- listener();
1019
- }
1020
- },
1021
- onScrollToBottom: (callback) => {
1022
- scrollToBottomListeners.add(callback);
1023
- return () => {
1024
- scrollToBottomListeners.delete(callback);
1025
- };
1026
- }
1027
- }));
1028
- const useComposer = (0, import_zustand2.create)()(() => ({
1029
- isEditing: true,
1030
- canCancel: false,
1031
- value: "",
1032
- setValue: (value) => {
1033
- useComposer.setState({ value });
1034
- },
1035
- edit: () => {
1036
- throw new Error("Not implemented");
1037
- },
1038
- send: () => {
1039
- useThread.getState().append({
1040
- parentId: useThread.getState().messages.at(-1)?.id ?? ROOT_PARENT_ID,
1041
- content: [{ type: "text", text: useComposer.getState().value }]
1042
- });
1043
- useComposer.getState().setValue("");
1044
- },
1045
- cancel: () => {
1046
- useThread.getState().stop();
915
+ var import_react20 = require("react");
916
+ var import_zustand4 = require("zustand");
917
+
918
+ // src/utils/context/stores/ViewportStore.tsx
919
+ var import_zustand3 = require("zustand");
920
+ var makeViewportStore = () => {
921
+ const scrollToBottomListeners = /* @__PURE__ */ new Set();
922
+ return (0, import_zustand3.create)(() => ({
923
+ isAtBottom: true,
924
+ scrollToBottom: () => {
925
+ for (const listener of scrollToBottomListeners) {
926
+ listener();
1047
927
  }
1048
- }));
1049
- return { useThread, useComposer };
928
+ },
929
+ onScrollToBottom: (callback) => {
930
+ scrollToBottomListeners.add(callback);
931
+ return () => {
932
+ scrollToBottomListeners.delete(callback);
933
+ };
934
+ }
935
+ }));
936
+ };
937
+
938
+ // src/adapters/vercel/useDummyAIAssistantContext.tsx
939
+ var makeDummyThreadStore = () => {
940
+ return (0, import_zustand4.create)(() => ({
941
+ messages: [],
942
+ isRunning: false,
943
+ getBranches: () => {
944
+ return [];
945
+ },
946
+ switchToBranch: () => {
947
+ throw new Error("Not implemented");
948
+ },
949
+ append: async () => {
950
+ throw new Error("Not implemented");
951
+ },
952
+ cancelRun: () => {
953
+ throw new Error("Not implemented");
954
+ },
955
+ startRun: async () => {
956
+ throw new Error("Not implemented");
957
+ }
958
+ }));
959
+ };
960
+ var useDummyAIAssistantContext = () => {
961
+ const [context] = (0, import_react20.useState)(() => {
962
+ const useThread = makeDummyThreadStore();
963
+ const useViewport = makeViewportStore();
964
+ const useComposer = makeThreadComposerStore(useThread);
965
+ return { useThread, useViewport, useComposer };
1050
966
  });
1051
967
  return context;
1052
968
  };
1053
969
 
970
+ // src/adapters/vercel/useVercelAIBranches.tsx
971
+ var import_react21 = require("react");
972
+
973
+ // ../../node_modules/nanoid/non-secure/index.js
974
+ var customAlphabet = (alphabet, defaultSize = 21) => {
975
+ return (size = defaultSize) => {
976
+ let id = "";
977
+ let i = size;
978
+ while (i--) {
979
+ id += alphabet[Math.random() * alphabet.length | 0];
980
+ }
981
+ return id;
982
+ };
983
+ };
984
+
985
+ // src/adapters/MessageRepository.tsx
986
+ var generateId = customAlphabet(
987
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
988
+ 7
989
+ );
990
+ var optimisticPrefix = "__optimistic__";
991
+ var generateOptimisticId = () => `${optimisticPrefix}${generateId()}`;
992
+ var isOptimisticId = (id) => id.startsWith(optimisticPrefix);
993
+ var findHead = (message) => {
994
+ if (message.next)
995
+ return findHead(message.next);
996
+ return message;
997
+ };
998
+ var MessageRepository = class {
999
+ messages = /* @__PURE__ */ new Map();
1000
+ // message_id -> item
1001
+ head = null;
1002
+ rootChildren = [];
1003
+ getMessages() {
1004
+ const messages = new Array(this.head?.level ?? 0);
1005
+ for (let current = this.head; current; current = current.prev) {
1006
+ messages[current.level] = current.current;
1007
+ }
1008
+ return messages;
1009
+ }
1010
+ addOrUpdateMessage(message) {
1011
+ const item = this.messages.get(message.id);
1012
+ if (item) {
1013
+ if (item.current.parentId !== message.parentId) {
1014
+ this.deleteMessage(message.id);
1015
+ } else {
1016
+ item.current = message;
1017
+ return;
1018
+ }
1019
+ }
1020
+ const prev = message.parentId ? this.messages.get(message.parentId) : null;
1021
+ if (prev === void 0)
1022
+ throw new Error("Unexpected: Parent message not found");
1023
+ const newItem = {
1024
+ prev,
1025
+ current: message,
1026
+ next: null,
1027
+ children: [],
1028
+ level: prev ? prev.level + 1 : 0
1029
+ };
1030
+ this.messages.set(message.id, newItem);
1031
+ if (prev) {
1032
+ prev.children = [...prev.children, message.id];
1033
+ prev.next = newItem;
1034
+ } else {
1035
+ this.rootChildren = [...this.rootChildren, message.id];
1036
+ }
1037
+ if (this.head === prev) {
1038
+ this.head = newItem;
1039
+ }
1040
+ }
1041
+ deleteMessage(messageId) {
1042
+ const message = this.messages.get(messageId);
1043
+ if (!message)
1044
+ throw new Error("Unexpected: Message not found");
1045
+ if (message.children.length > 0) {
1046
+ for (const child of message.children) {
1047
+ this.deleteMessage(child);
1048
+ }
1049
+ }
1050
+ this.messages.delete(messageId);
1051
+ if (message.prev) {
1052
+ message.prev.children = message.prev.children.filter(
1053
+ (m) => m !== messageId
1054
+ );
1055
+ if (message.prev.next === message) {
1056
+ const childId = message.prev.children.at(-1);
1057
+ const child = childId ? this.messages.get(childId) : null;
1058
+ if (child === void 0)
1059
+ throw new Error("Unexpected: Child message not found");
1060
+ message.prev.next = child;
1061
+ }
1062
+ } else {
1063
+ this.rootChildren = this.rootChildren.filter((m) => m !== messageId);
1064
+ }
1065
+ if (this.head === message) {
1066
+ this.head = message.prev ? findHead(message.prev) : null;
1067
+ }
1068
+ }
1069
+ getOptimisticId = () => {
1070
+ let optimisticId;
1071
+ do {
1072
+ optimisticId = generateOptimisticId();
1073
+ } while (this.messages.has(optimisticId));
1074
+ return optimisticId;
1075
+ };
1076
+ commitOptimisticRun(parentId) {
1077
+ const optimisticId = this.getOptimisticId();
1078
+ this.addOrUpdateMessage({
1079
+ id: optimisticId,
1080
+ role: "assistant",
1081
+ content: [
1082
+ {
1083
+ type: "text",
1084
+ text: ""
1085
+ }
1086
+ ],
1087
+ parentId,
1088
+ createdAt: /* @__PURE__ */ new Date()
1089
+ });
1090
+ return optimisticId;
1091
+ }
1092
+ getBranches(messageId) {
1093
+ const message = this.messages.get(messageId);
1094
+ if (!message)
1095
+ throw new Error("Unexpected: Message not found");
1096
+ if (message.prev) {
1097
+ return message.prev.children;
1098
+ }
1099
+ return this.rootChildren;
1100
+ }
1101
+ switchToBranch(messageId) {
1102
+ const message = this.messages.get(messageId);
1103
+ if (!message)
1104
+ throw new Error("Unexpected: Branch not found");
1105
+ if (message.prev) {
1106
+ message.prev.next = message;
1107
+ }
1108
+ this.head = findHead(message);
1109
+ }
1110
+ resetHead(messageId) {
1111
+ if (messageId) {
1112
+ const message = this.messages.get(messageId);
1113
+ if (!message)
1114
+ throw new Error("Unexpected: Branch not found");
1115
+ this.head = message;
1116
+ for (let current = message; current; current = current.prev) {
1117
+ if (current.prev) {
1118
+ current.prev.next = current;
1119
+ }
1120
+ }
1121
+ } else {
1122
+ this.head = null;
1123
+ }
1124
+ }
1125
+ };
1126
+
1127
+ // src/adapters/vercel/useVercelAIBranches.tsx
1128
+ var sliceMessagesUntil = (messages, messageId) => {
1129
+ if (messageId == null)
1130
+ return [];
1131
+ if (isOptimisticId(messageId))
1132
+ return messages;
1133
+ const messageIdx = messages.findIndex((m) => m.id === messageId);
1134
+ if (messageIdx === -1)
1135
+ throw new Error("Unexpected: Message not found");
1136
+ return messages.slice(0, messageIdx + 1);
1137
+ };
1138
+ var hasUpcomingMessage = (isRunning, messages) => {
1139
+ return isRunning && messages[messages.length - 1]?.role !== "assistant";
1140
+ };
1141
+ var useVercelAIBranches = (chat, messages) => {
1142
+ const [data] = (0, import_react21.useState)(() => new MessageRepository());
1143
+ const isRunning = "isLoading" in chat ? chat.isLoading : chat.status === "in_progress";
1144
+ const assistantOptimisticIdRef = (0, import_react21.useRef)(null);
1145
+ const messagesEx = (0, import_react21.useMemo)(() => {
1146
+ for (const message of messages) {
1147
+ data.addOrUpdateMessage(message);
1148
+ }
1149
+ if (assistantOptimisticIdRef.current) {
1150
+ data.deleteMessage(assistantOptimisticIdRef.current);
1151
+ assistantOptimisticIdRef.current = null;
1152
+ }
1153
+ if (hasUpcomingMessage(isRunning, messages)) {
1154
+ assistantOptimisticIdRef.current = data.commitOptimisticRun(
1155
+ messages.at(-1)?.id ?? null
1156
+ );
1157
+ }
1158
+ data.resetHead(
1159
+ assistantOptimisticIdRef.current ?? messages.at(-1)?.id ?? null
1160
+ );
1161
+ return data.getMessages();
1162
+ }, [data, isRunning, messages]);
1163
+ const getBranches = (0, import_react21.useCallback)(
1164
+ (messageId) => {
1165
+ return data.getBranches(messageId);
1166
+ },
1167
+ [data]
1168
+ );
1169
+ const switchToBranch = (0, import_react21.useCallback)(
1170
+ (messageId) => {
1171
+ data.switchToBranch(messageId);
1172
+ chat.setMessages(
1173
+ data.getMessages().filter((m) => !isOptimisticId(m.id)).map((m) => m.innerMessage)
1174
+ );
1175
+ },
1176
+ [data, chat.setMessages]
1177
+ );
1178
+ const reloadMaybe = "reload" in chat ? chat.reload : void 0;
1179
+ const startRun = (0, import_react21.useCallback)(
1180
+ async (parentId) => {
1181
+ if (!reloadMaybe)
1182
+ throw new Error("Reload not supported by Vercel AI SDK's useAssistant");
1183
+ const newMessages = sliceMessagesUntil(chat.messages, parentId);
1184
+ chat.setMessages(newMessages);
1185
+ await reloadMaybe();
1186
+ },
1187
+ [chat.messages, chat.setMessages, reloadMaybe]
1188
+ );
1189
+ const append = (0, import_react21.useCallback)(
1190
+ async (message) => {
1191
+ if (message.content.length !== 1 || message.content[0]?.type !== "text")
1192
+ throw new Error("Only text content is supported by Vercel AI SDK");
1193
+ const newMessages = sliceMessagesUntil(chat.messages, message.parentId);
1194
+ chat.setMessages(newMessages);
1195
+ await chat.append({
1196
+ role: "user",
1197
+ content: message.content[0].text
1198
+ });
1199
+ },
1200
+ [chat.messages, chat.setMessages, chat.append]
1201
+ );
1202
+ return (0, import_react21.useMemo)(
1203
+ () => ({
1204
+ messages: messagesEx,
1205
+ getBranches,
1206
+ switchToBranch,
1207
+ append,
1208
+ startRun
1209
+ }),
1210
+ [messagesEx, getBranches, switchToBranch, append, startRun]
1211
+ );
1212
+ };
1213
+
1054
1214
  // src/adapters/vercel/VercelAIAssistantProvider.tsx
1215
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1055
1216
  var ThreadMessageCache = /* @__PURE__ */ new WeakMap();
1056
- var vercelToThreadMessage = (message, parentId, branchId, branchCount) => {
1217
+ var vercelToThreadMessage = (message, parentId) => {
1057
1218
  if (message.role !== "user" && message.role !== "assistant")
1058
1219
  throw new Error("Unsupported role");
1059
1220
  return {
@@ -1061,24 +1222,17 @@ var vercelToThreadMessage = (message, parentId, branchId, branchCount) => {
1061
1222
  id: message.id,
1062
1223
  role: message.role,
1063
1224
  content: [{ type: "text", text: message.content }],
1064
- branchId,
1065
- branchCount,
1066
- createdAt: message.createdAt ?? /* @__PURE__ */ new Date()
1225
+ createdAt: message.createdAt ?? /* @__PURE__ */ new Date(),
1226
+ innerMessage: message
1067
1227
  };
1068
1228
  };
1069
- var vercelToCachedThreadMessages = (messages, getBranchState) => {
1229
+ var vercelToCachedThreadMessages = (messages) => {
1070
1230
  return messages.map((m, idx) => {
1071
1231
  const cached = ThreadMessageCache.get(m);
1072
- const parentId = messages[idx - 1]?.id ?? ROOT_PARENT_ID;
1073
- const { branchId, branchCount } = getBranchState(m.id);
1074
- if (cached && cached.parentId === parentId && cached.branchId === branchId && cached.branchCount === branchCount)
1232
+ const parentId = messages[idx - 1]?.id ?? null;
1233
+ if (cached && cached.parentId === parentId)
1075
1234
  return cached;
1076
- const newMessage = vercelToThreadMessage(
1077
- m,
1078
- parentId,
1079
- branchId,
1080
- branchCount
1081
- );
1235
+ const newMessage = vercelToThreadMessage(m, parentId);
1082
1236
  ThreadMessageCache.set(m, newMessage);
1083
1237
  return newMessage;
1084
1238
  });
@@ -1089,43 +1243,44 @@ var VercelAIAssistantProvider = ({
1089
1243
  }) => {
1090
1244
  const context = useDummyAIAssistantContext();
1091
1245
  const vercel = "chat" in rest ? rest.chat : rest.assistant;
1092
- const branches = useVercelAIBranches(vercel, context);
1093
1246
  const messages = (0, import_react22.useMemo)(() => {
1094
- return vercelToCachedThreadMessages(
1095
- vercel.messages,
1096
- branches.getBranchState
1097
- );
1098
- }, [vercel.messages, branches.getBranchState]);
1099
- const stop = (0, import_react22.useCallback)(() => {
1247
+ return vercelToCachedThreadMessages(vercel.messages);
1248
+ }, [vercel.messages]);
1249
+ const branches = useVercelAIBranches(vercel, messages);
1250
+ const cancelRun = (0, import_react22.useCallback)(() => {
1100
1251
  const lastMessage = vercel.messages.at(-1);
1101
1252
  vercel.stop();
1102
1253
  if (lastMessage?.role === "user") {
1103
1254
  vercel.setInput(lastMessage.content);
1104
1255
  }
1105
1256
  }, [vercel.messages, vercel.stop, vercel.setInput]);
1106
- const isLoading = "isLoading" in vercel ? vercel.isLoading : vercel.status === "in_progress";
1257
+ const isRunning = "isLoading" in vercel ? vercel.isLoading : vercel.status === "in_progress";
1107
1258
  (0, import_react22.useMemo)(() => {
1108
- context.useThread.setState({
1109
- messages,
1110
- isLoading,
1111
- stop,
1112
- switchToBranch: branches.switchToBranch,
1113
- append: branches.append,
1114
- reload: branches.reload
1115
- });
1116
- }, [context, messages, isLoading, stop, branches]);
1259
+ context.useThread.setState(
1260
+ {
1261
+ messages: branches.messages,
1262
+ isRunning,
1263
+ getBranches: branches.getBranches,
1264
+ switchToBranch: branches.switchToBranch,
1265
+ append: branches.append,
1266
+ startRun: branches.startRun,
1267
+ cancelRun
1268
+ },
1269
+ true
1270
+ );
1271
+ }, [context, isRunning, cancelRun, branches]);
1117
1272
  (0, import_react22.useMemo)(() => {
1118
1273
  context.useComposer.setState({
1119
- canCancel: isLoading,
1120
1274
  value: vercel.input,
1121
1275
  setValue: vercel.setInput
1122
1276
  });
1123
- }, [context, isLoading, vercel.input, vercel.setInput]);
1124
- return /* @__PURE__ */ React.createElement(AssistantContext.Provider, { value: context }, children);
1277
+ }, [context, vercel.input, vercel.setInput]);
1278
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AssistantContext.Provider, { value: context, children });
1125
1279
  };
1126
1280
 
1127
1281
  // src/adapters/vercel/VercelRSCAssistantProvider.tsx
1128
1282
  var import_react23 = require("react");
1283
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1129
1284
  var ThreadMessageCache2 = /* @__PURE__ */ new WeakMap();
1130
1285
  var vercelToThreadMessage2 = (parentId, message) => {
1131
1286
  if (message.role !== "user" && message.role !== "assistant")
@@ -1135,15 +1290,13 @@ var vercelToThreadMessage2 = (parentId, message) => {
1135
1290
  id: message.id,
1136
1291
  role: message.role,
1137
1292
  content: [{ type: "ui", display: message.display }],
1138
- createdAt: message.createdAt ?? /* @__PURE__ */ new Date(),
1139
- branchId: 0,
1140
- branchCount: 1
1293
+ createdAt: message.createdAt ?? /* @__PURE__ */ new Date()
1141
1294
  };
1142
1295
  };
1143
1296
  var vercelToCachedThreadMessages2 = (messages) => {
1144
1297
  return messages.map((m, idx) => {
1145
1298
  const cached = ThreadMessageCache2.get(m);
1146
- const parentId = messages[idx - 1]?.id ?? ROOT_PARENT_ID;
1299
+ const parentId = messages[idx - 1]?.id ?? null;
1147
1300
  if (cached && cached.parentId === parentId)
1148
1301
  return cached;
1149
1302
  const newMessage = vercelToThreadMessage2(parentId, m);
@@ -1162,12 +1315,11 @@ var VercelRSCAssistantProvider = ({
1162
1315
  }, [vercelMessages]);
1163
1316
  const append = (0, import_react23.useCallback)(
1164
1317
  async (message) => {
1165
- if (message.parentId !== (context.useThread.getState().messages.at(-1)?.id ?? ROOT_PARENT_ID))
1318
+ if (message.parentId !== (context.useThread.getState().messages.at(-1)?.id ?? null))
1166
1319
  throw new Error("Unexpected: Message editing is not supported");
1167
1320
  if (message.content[0]?.type !== "text") {
1168
1321
  throw new Error("Only text content is currently supported");
1169
1322
  }
1170
- context.useThread.getState().scrollToBottom();
1171
1323
  await vercelAppend(message);
1172
1324
  },
1173
1325
  [context, vercelAppend]
@@ -1178,7 +1330,7 @@ var VercelRSCAssistantProvider = ({
1178
1330
  append
1179
1331
  });
1180
1332
  }, [context, messages, append]);
1181
- return /* @__PURE__ */ React.createElement(AssistantContext.Provider, { value: context }, children);
1333
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(AssistantContext.Provider, { value: context, children });
1182
1334
  };
1183
1335
  // Annotate the CommonJS export names for ESM import in node:
1184
1336
  0 && (module.exports = {