@copilotkit/react-core 1.74.0 → 1.75.0

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.
@@ -1635,7 +1635,7 @@ function useRenderToolCall() {
1635
1635
 
1636
1636
  //#endregion
1637
1637
  //#region package.json
1638
- var version = "1.74.0";
1638
+ var version = "1.75.0";
1639
1639
 
1640
1640
  //#endregion
1641
1641
  //#region src/v2/components/CopilotKitInspector.tsx
@@ -6364,7 +6364,7 @@ function BoundInspectorButton({ inspectorButton, messageId, openInspector, ...pr
6364
6364
  })
6365
6365
  });
6366
6366
  }
6367
- function CopilotChatAssistantMessage({ message, messages, isRunning, onThumbsUp, onThumbsDown, onReadAloud, onRegenerate, additionalToolbarItems, toolbarVisible = true, markdownRenderer, toolbar, copyButton, inspectorButton, thumbsUpButton, thumbsDownButton, readAloudButton, regenerateButton, toolCallsView, children, className, ...props }) {
6367
+ function CopilotChatAssistantMessage({ message, messages, isRunning, isLatest, onThumbsUp, onThumbsDown, onReadAloud, onRegenerate, additionalToolbarItems, toolbarVisible = true, markdownRenderer, toolbar, copyButton, inspectorButton, thumbsUpButton, thumbsDownButton, readAloudButton, regenerateButton, toolCallsView, children, className, ...props }) {
6368
6368
  useKatexStyles();
6369
6369
  const { isInspectorEnabled, openInspector } = useCopilotKitInspector();
6370
6370
  const shortcutsHidden = useInspectorShortcutsHidden();
@@ -6400,7 +6400,7 @@ function CopilotChatAssistantMessage({ message, messages, isRunning, onThumbsUp,
6400
6400
  messages
6401
6401
  });
6402
6402
  const hasContent = !!(message.content && message.content.trim().length > 0);
6403
- const isLatestAssistantMessage = message.role === "assistant" && messages?.[messages.length - 1]?.id === message.id;
6403
+ const isLatestAssistantMessage = message.role === "assistant" && (isLatest ?? messages?.[messages.length - 1]?.id === message.id);
6404
6404
  const shouldShowToolbar = toolbarVisible && (hasContent || showInspectorShortcut) && !(isRunning && isLatestAssistantMessage);
6405
6405
  if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6406
6406
  "data-copilotkit": true,
@@ -6914,8 +6914,8 @@ function formatDuration(seconds) {
6914
6914
  if (secs === 0) return `${mins} minute${mins > 1 ? "s" : ""}`;
6915
6915
  return `${mins}m ${secs}s`;
6916
6916
  }
6917
- function CopilotChatReasoningMessage({ message, messages, isRunning, header, contentView, toggle, children, className, ...props }) {
6918
- const isLatest = messages?.[messages.length - 1]?.id === message.id;
6917
+ function CopilotChatReasoningMessage({ message, messages, isRunning, isLatest: isLatestProp, header, contentView, toggle, children, className, ...props }) {
6918
+ const isLatest = isLatestProp ?? messages?.[messages.length - 1]?.id === message.id;
6919
6919
  const isStreaming = !!(isRunning && isLatest);
6920
6920
  const hasContent = !!(message.content && message.content.length > 0);
6921
6921
  const startTimeRef = (0, react.useRef)(null);
@@ -7310,24 +7310,31 @@ const INTELLIGENCE_TURN_HEAD = "__cpk_turn_head__";
7310
7310
  * emits exactly one `IntelligenceIndicator` per entry, keyed by the turn id and
7311
7311
  * positioned at the anchor; the per-turn key also lets every past turn keep its
7312
7312
  * own indicator in scroll-back.
7313
+ *
7314
+ * `visibleMessages` is what actually renders, when that differs from
7315
+ * `messages` (the chat's `transformMessages`). Turns are still read from the
7316
+ * full list, so hiding a user message does not merge its turn into the one
7317
+ * before it, and anchors are only chosen among visible messages. A message the
7318
+ * transform built itself joins the turn of the visible message before it.
7313
7319
  */
7314
- function getIntelligenceTurnAnchors(messages) {
7315
- const anchors = /* @__PURE__ */ new Map();
7320
+ function getIntelligenceTurnAnchors(messages, visibleMessages = messages) {
7321
+ const turnOfMessage = /* @__PURE__ */ new Map();
7316
7322
  let turnId = INTELLIGENCE_TURN_HEAD;
7317
- let anchorId = null;
7318
- const commit = () => {
7319
- if (anchorId !== null) anchors.set(anchorId, turnId);
7320
- anchorId = null;
7321
- };
7322
7323
  for (const m of messages) {
7323
- if (m.role === "user") {
7324
- commit();
7325
- turnId = m.id;
7326
- continue;
7324
+ if (m.role === "user") turnId = m.id;
7325
+ turnOfMessage.set(m.id, turnId);
7326
+ }
7327
+ const anchors = /* @__PURE__ */ new Map();
7328
+ const anchoredTurns = /* @__PURE__ */ new Set();
7329
+ let currentTurn = INTELLIGENCE_TURN_HEAD;
7330
+ for (const m of visibleMessages) {
7331
+ currentTurn = turnOfMessage.get(m.id) ?? currentTurn;
7332
+ if (m.role === "user") continue;
7333
+ if (!anchoredTurns.has(currentTurn) && messageHasMatchingToolCall(m)) {
7334
+ anchors.set(m.id, currentTurn);
7335
+ anchoredTurns.add(currentTurn);
7327
7336
  }
7328
- if (anchorId === null && messageHasMatchingToolCall(m)) anchorId = m.id;
7329
7337
  }
7330
- commit();
7331
7338
  return anchors;
7332
7339
  }
7333
7340
  /**
@@ -7473,11 +7480,12 @@ function resolveSlotComponent(slot, DefaultComponent) {
7473
7480
  /**
7474
7481
  * Memoized wrapper for assistant messages to prevent re-renders when other messages change.
7475
7482
  */
7476
- const MemoizedAssistantMessage = react.default.memo(function MemoizedAssistantMessage({ message, messages, isRunning, AssistantMessageComponent, slotProps }) {
7483
+ const MemoizedAssistantMessage = react.default.memo(function MemoizedAssistantMessage({ message, messages, isRunning, isLatest, AssistantMessageComponent, slotProps }) {
7477
7484
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AssistantMessageComponent, {
7478
7485
  message,
7479
7486
  messages,
7480
7487
  isRunning,
7488
+ isLatest,
7481
7489
  ...slotProps
7482
7490
  });
7483
7491
  }, (prevProps, nextProps) => {
@@ -7499,7 +7507,7 @@ const MemoizedAssistantMessage = react.default.memo(function MemoizedAssistantMe
7499
7507
  if (prevToolResults.length !== nextToolResults.length) return false;
7500
7508
  for (let i = 0; i < prevToolResults.length; i++) if (prevToolResults[i].content !== nextToolResults[i].content) return false;
7501
7509
  }
7502
- if (nextProps.messages[nextProps.messages.length - 1]?.id === nextProps.message.id && prevProps.isRunning !== nextProps.isRunning) return false;
7510
+ if ((prevProps.isRunning && prevProps.isLatest) !== (nextProps.isRunning && nextProps.isLatest)) return false;
7503
7511
  if (prevProps.AssistantMessageComponent !== nextProps.AssistantMessageComponent) return false;
7504
7512
  if (prevProps.slotProps !== nextProps.slotProps) return false;
7505
7513
  return true;
@@ -7533,20 +7541,19 @@ const MemoizedActivityMessage = react.default.memo(function MemoizedActivityMess
7533
7541
  /**
7534
7542
  * Memoized wrapper for reasoning messages to prevent re-renders when other messages change.
7535
7543
  */
7536
- const MemoizedReasoningMessage = react.default.memo(function MemoizedReasoningMessage({ message, messages, isRunning, ReasoningMessageComponent, slotProps }) {
7544
+ const MemoizedReasoningMessage = react.default.memo(function MemoizedReasoningMessage({ message, messages, isRunning, isLatest, ReasoningMessageComponent, slotProps }) {
7537
7545
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReasoningMessageComponent, {
7538
7546
  message,
7539
7547
  messages,
7540
7548
  isRunning,
7549
+ isLatest,
7541
7550
  ...slotProps
7542
7551
  });
7543
7552
  }, (prevProps, nextProps) => {
7544
7553
  if (prevProps.message.id !== nextProps.message.id) return false;
7545
7554
  if (prevProps.message.content !== nextProps.message.content) return false;
7546
- const prevIsLatest = prevProps.messages[prevProps.messages.length - 1]?.id === prevProps.message.id;
7547
- const nextIsLatest = nextProps.messages[nextProps.messages.length - 1]?.id === nextProps.message.id;
7548
- if (prevIsLatest !== nextIsLatest) return false;
7549
- if (nextIsLatest && prevProps.isRunning !== nextProps.isRunning) return false;
7555
+ if (prevProps.isLatest !== nextProps.isLatest) return false;
7556
+ if (nextProps.isLatest && prevProps.isRunning !== nextProps.isRunning) return false;
7550
7557
  if (prevProps.ReasoningMessageComponent !== nextProps.ReasoningMessageComponent) return false;
7551
7558
  if (prevProps.slotProps !== nextProps.slotProps) return false;
7552
7559
  return true;
@@ -7630,7 +7637,7 @@ function deduplicateMessages(messages) {
7630
7637
  return [...acc.values()];
7631
7638
  }
7632
7639
  const VIRTUALIZE_THRESHOLD = 50;
7633
- function CopilotChatMessageView({ messages = [], assistantMessage, userMessage, reasoningMessage, cursor, intelligenceIndicator, isRunning = false, children, className, ...props }) {
7640
+ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage, reasoningMessage, cursor, intelligenceIndicator, isRunning = false, transformMessages, children, className, ...props }) {
7634
7641
  const isPinnedToBottom = (0, react.useContext)(ScrollPinnedContext);
7635
7642
  const renderCustomMessage = useRenderCustomMessages();
7636
7643
  const { renderActivityMessage } = useRenderActivityMessage();
@@ -7663,13 +7670,27 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
7663
7670
  return copilotkit.getStateByRun(config.agentId, config.threadId, resolvedRunId);
7664
7671
  };
7665
7672
  const deduplicatedMessages = (0, react.useMemo)(() => deduplicateMessages(messages), [messages]);
7673
+ const renderedMessages = (0, react.useMemo)(() => transformMessages ? transformMessages(deduplicatedMessages) : deduplicatedMessages, [deduplicatedMessages, transformMessages]);
7674
+ const latestRenderedId = renderedMessages[renderedMessages.length - 1]?.id;
7675
+ const transformDuplicateId = (0, react.useMemo)(() => {
7676
+ if (process.env.NODE_ENV === "production" || !transformMessages) return;
7677
+ const seen = /* @__PURE__ */ new Set();
7678
+ for (const message of renderedMessages) {
7679
+ if (seen.has(message.id)) return message.id;
7680
+ seen.add(message.id);
7681
+ }
7682
+ }, [renderedMessages, transformMessages]);
7683
+ (0, react.useEffect)(() => {
7684
+ if (transformDuplicateId === void 0) return;
7685
+ console.warn(`[CopilotKit] CopilotChatMessageView: \`transformMessages\` returned more than one message with id "${transformDuplicateId}". Return each id at most once; a message you create needs its own id, stable across renders.`);
7686
+ }, [transformDuplicateId]);
7666
7687
  const rowKeyStoreRef = (0, react.useRef)(null);
7667
7688
  rowKeyStoreRef.current ??= (0, _copilotkit_shared.createRowKeyStore)();
7668
7689
  const rowKeyStore = rowKeyStoreRef.current;
7669
- const rowRenderKeys = (0, react.useMemo)(() => (0, _copilotkit_shared.resolveRowRenderKeysById)(rowKeyStore, deduplicatedMessages), [rowKeyStore, deduplicatedMessages]);
7690
+ const rowRenderKeys = (0, react.useMemo)(() => (0, _copilotkit_shared.resolveRowRenderKeysById)(rowKeyStore, renderedMessages), [rowKeyStore, renderedMessages]);
7670
7691
  (0, react.useLayoutEffect)(() => {
7671
- (0, _copilotkit_shared.commitRowKeyStore)(rowKeyStore, deduplicatedMessages);
7672
- }, [rowKeyStore, deduplicatedMessages]);
7692
+ (0, _copilotkit_shared.commitRowKeyStore)(rowKeyStore, renderedMessages);
7693
+ }, [rowKeyStore, renderedMessages]);
7673
7694
  if (process.env.NODE_ENV === "development" && deduplicatedMessages.length < messages.length) console.warn(`CopilotChatMessageView: Merged ${messages.length - deduplicatedMessages.length} message(s) with duplicate IDs.`);
7674
7695
  const { Component: AssistantComponent, slotProps: assistantSlotProps } = (0, react.useMemo)(() => resolveSlotComponent(assistantMessage, CopilotChatAssistantMessage_default), [assistantMessage]);
7675
7696
  const agentId = config?.agentId;
@@ -7703,7 +7724,18 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
7703
7724
  (0, react.useEffect)(() => {
7704
7725
  if (process.env.NODE_ENV !== "production" && scrollElementFromCtx && scrollElementFromCtx.clientHeight === 0) console.warn("[CopilotKit] Chat scroll container has clientHeight=0 — virtualization disabled. Ensure the chat is rendered in a visible container with a non-zero height.");
7705
7726
  }, [scrollElementFromCtx]);
7706
- const shouldVirtualize = !!scrollElement && !children && deduplicatedMessages.length > VIRTUALIZE_THRESHOLD;
7727
+ const shouldVirtualize = !!scrollElement && !children && renderedMessages.length > VIRTUALIZE_THRESHOLD;
7728
+ const childrenDisabledVirtualization = !!children && !!scrollElement && renderedMessages.length > VIRTUALIZE_THRESHOLD;
7729
+ const warnedChildrenRef = (0, react.useRef)(false);
7730
+ (0, react.useEffect)(() => {
7731
+ if (process.env.NODE_ENV === "production" || !childrenDisabledVirtualization || warnedChildrenRef.current) return;
7732
+ warnedChildrenRef.current = true;
7733
+ console.warn(`[CopilotKit] CopilotChatMessageView: the \`children\` render prop disables virtualization, so all ${renderedMessages.length} messages are mounted. ` + (transformMessages ? "`transformMessages` keeps virtualization on by itself; drop `children` to use it." : "To reshape the list and keep virtualization, use `transformMessages` instead."));
7734
+ }, [
7735
+ childrenDisabledVirtualization,
7736
+ renderedMessages.length,
7737
+ transformMessages
7738
+ ]);
7707
7739
  const measuredRef = react.default.useRef({
7708
7740
  total: 0,
7709
7741
  sizes: /* @__PURE__ */ new Map()
@@ -7738,7 +7770,7 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
7738
7770
  return item.start < (instance.scrollOffset ?? 0) + scrollAdjustments;
7739
7771
  }, []);
7740
7772
  const virtualizer = (0, _tanstack_react_virtual.useVirtualizer)({
7741
- count: shouldVirtualize ? deduplicatedMessages.length : 0,
7773
+ count: shouldVirtualize ? renderedMessages.length : 0,
7742
7774
  getScrollElement: () => scrollElement,
7743
7775
  estimateSize: estimateRowSize,
7744
7776
  overscan: 5,
@@ -7751,10 +7783,10 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
7751
7783
  isPinnedToBottomRef.current = isPinnedToBottom;
7752
7784
  virtualizer.shouldAdjustScrollPositionOnItemSizeChange = shouldAdjustScrollOnResize;
7753
7785
  (0, react.useLayoutEffect)(() => {
7754
- if (!shouldVirtualize || !deduplicatedMessages.length) return;
7755
- virtualizer.scrollToIndex(deduplicatedMessages.length - 1, { align: "end" });
7786
+ if (!shouldVirtualize || !renderedMessages.length) return;
7787
+ virtualizer.scrollToIndex(renderedMessages.length - 1, { align: "end" });
7756
7788
  }, [shouldVirtualize, firstMessageId]);
7757
- const intelligenceTurnAnchors = (0, react.useMemo)(() => getIntelligenceTurnAnchors(deduplicatedMessages), [deduplicatedMessages]);
7789
+ const intelligenceTurnAnchors = (0, react.useMemo)(() => getIntelligenceTurnAnchors(deduplicatedMessages, renderedMessages), [deduplicatedMessages, renderedMessages]);
7758
7790
  const renderMessageBlock = (message) => {
7759
7791
  const elements = [];
7760
7792
  const stateSnapshot = renderCustomMessage ? getStateSnapshotForMessage(message.id) : void 0;
@@ -7769,6 +7801,7 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
7769
7801
  message,
7770
7802
  messages,
7771
7803
  isRunning,
7804
+ isLatest: message.id === latestRenderedId,
7772
7805
  AssistantMessageComponent: AssistantComponent,
7773
7806
  slotProps: assistantSlotPropsWithFeedback
7774
7807
  }, rowKey));
@@ -7785,6 +7818,7 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
7785
7818
  message,
7786
7819
  messages,
7787
7820
  isRunning,
7821
+ isLatest: message.id === latestRenderedId,
7788
7822
  ReasoningMessageComponent: ReasoningComponent,
7789
7823
  slotProps: reasoningSlotProps
7790
7824
  }, rowKey));
@@ -7802,7 +7836,7 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
7802
7836
  }, `intelligence-${intelligenceTurnId}`));
7803
7837
  return elements.filter(Boolean);
7804
7838
  };
7805
- const messageElements = shouldVirtualize ? [] : deduplicatedMessages.flatMap(renderMessageBlock);
7839
+ const messageElements = shouldVirtualize ? [] : renderedMessages.flatMap(renderMessageBlock);
7806
7840
  if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7807
7841
  "data-copilotkit": true,
7808
7842
  style: { display: "contents" },
@@ -7813,7 +7847,7 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
7813
7847
  interruptElement
7814
7848
  })
7815
7849
  });
7816
- const lastMessage = messages[messages.length - 1];
7850
+ const lastMessage = renderedMessages[renderedMessages.length - 1];
7817
7851
  const showCursor = isRunning && lastMessage?.role !== "reasoning";
7818
7852
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7819
7853
  "data-copilotkit": true,
@@ -7827,7 +7861,7 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
7827
7861
  position: "relative"
7828
7862
  },
7829
7863
  children: virtualizer.getVirtualItems().map((virtualItem) => {
7830
- const message = deduplicatedMessages[virtualItem.index];
7864
+ const message = renderedMessages[virtualItem.index];
7831
7865
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7832
7866
  "data-index": virtualItem.index,
7833
7867
  ref: virtualizer.measureElement,
@@ -13081,4 +13115,4 @@ Object.defineProperty(exports, 'useToast', {
13081
13115
  return useToast;
13082
13116
  }
13083
13117
  });
13084
- //# sourceMappingURL=copilotkit-C5FS3iDK.cjs.map
13118
+ //# sourceMappingURL=copilotkit-D2emzYuj.cjs.map