@copilotkit/react-core 1.56.2 → 1.56.4

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.
Files changed (59) hide show
  1. package/dist/{copilotkit-Cj2ZIxVr.mjs → copilotkit-Bd0m5HFp.mjs} +266 -81
  2. package/dist/copilotkit-Bd0m5HFp.mjs.map +1 -0
  3. package/dist/{copilotkit-CCbxm6JM.d.mts → copilotkit-DFaI4j2r.d.mts} +64 -18
  4. package/dist/copilotkit-DFaI4j2r.d.mts.map +1 -0
  5. package/dist/{copilotkit-BtP7w7cT.d.cts → copilotkit-Dg4r4Gi_.d.cts} +64 -18
  6. package/dist/copilotkit-Dg4r4Gi_.d.cts.map +1 -0
  7. package/dist/{copilotkit-CSJw5BG8.cjs → copilotkit-tb4zqaMK.cjs} +265 -80
  8. package/dist/copilotkit-tb4zqaMK.cjs.map +1 -0
  9. package/dist/index.cjs +1 -1
  10. package/dist/index.d.cts +2 -1
  11. package/dist/index.d.cts.map +1 -1
  12. package/dist/index.d.mts +2 -1
  13. package/dist/index.d.mts.map +1 -1
  14. package/dist/index.mjs +1 -1
  15. package/dist/index.umd.js +31 -44
  16. package/dist/index.umd.js.map +1 -1
  17. package/dist/v2/index.cjs +1 -1
  18. package/dist/v2/index.css +1 -1
  19. package/dist/v2/index.d.cts +2 -2
  20. package/dist/v2/index.d.mts +2 -2
  21. package/dist/v2/index.mjs +1 -1
  22. package/dist/v2/index.umd.js +264 -83
  23. package/dist/v2/index.umd.js.map +1 -1
  24. package/package.json +6 -6
  25. package/src/components/copilot-provider/__tests__/v1-explicit-threadid-bridge.test.tsx +107 -0
  26. package/src/components/copilot-provider/copilotkit.tsx +6 -1
  27. package/src/context/__tests__/threads-context.test.tsx +116 -3
  28. package/src/context/threads-context.tsx +18 -1
  29. package/src/v2/components/chat/CopilotChat.tsx +91 -4
  30. package/src/v2/components/chat/CopilotChatAttachmentQueue.tsx +4 -1
  31. package/src/v2/components/chat/CopilotChatInput.tsx +22 -0
  32. package/src/v2/components/chat/CopilotChatView.tsx +207 -44
  33. package/src/v2/components/chat/__tests__/CopilotChat.absentThreadConnect.test.tsx +66 -0
  34. package/src/v2/components/chat/__tests__/CopilotChat.welcomeGate.test.tsx +186 -0
  35. package/src/v2/components/chat/__tests__/CopilotChatActivityRendering.e2e.test.tsx +438 -4
  36. package/src/v2/components/chat/__tests__/CopilotChatView.connectingGate.test.tsx +56 -0
  37. package/src/v2/components/chat/__tests__/CopilotChatView.inputOverlay.test.tsx +172 -0
  38. package/src/v2/components/chat/__tests__/CopilotChatView.pinToSend.test.tsx +94 -0
  39. package/src/v2/components/chat/__tests__/copilot-chat-throttle.test.tsx +0 -1
  40. package/src/v2/components/chat/__tests__/normalize-auto-scroll.test.ts +37 -0
  41. package/src/v2/components/chat/index.ts +2 -0
  42. package/src/v2/components/chat/last-user-message-context.ts +21 -0
  43. package/src/v2/components/chat/normalize-auto-scroll.ts +17 -0
  44. package/src/v2/components/license-warning-banner.tsx +20 -1
  45. package/src/v2/hooks/__tests__/use-agent-stability.test.tsx +6 -0
  46. package/src/v2/hooks/__tests__/use-agent-thread-isolation.test.tsx +6 -0
  47. package/src/v2/hooks/__tests__/use-agent-throttle.test.tsx +76 -50
  48. package/src/v2/hooks/__tests__/use-pin-to-send.test.tsx +219 -0
  49. package/src/v2/hooks/__tests__/use-threads.test.tsx +68 -0
  50. package/src/v2/hooks/use-agent.tsx +34 -77
  51. package/src/v2/hooks/use-pin-to-send.ts +94 -0
  52. package/src/v2/hooks/use-threads.tsx +55 -12
  53. package/src/v2/providers/CopilotChatConfigurationProvider.tsx +29 -1
  54. package/src/v2/providers/CopilotKitProvider.tsx +2 -11
  55. package/src/v2/providers/__tests__/CopilotChatConfigurationProvider.test.tsx +106 -0
  56. package/dist/copilotkit-BtP7w7cT.d.cts.map +0 -1
  57. package/dist/copilotkit-CCbxm6JM.d.mts.map +0 -1
  58. package/dist/copilotkit-CSJw5BG8.cjs.map +0 -1
  59. package/dist/copilotkit-Cj2ZIxVr.mjs.map +0 -1
@@ -161,7 +161,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
161
161
  welcomeMessageText: "How can I help you today?"
162
162
  };
163
163
  const CopilotChatConfiguration = (0, react.createContext)(null);
164
- const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId, isModalDefaultOpen }) => {
164
+ const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId, hasExplicitThreadId, isModalDefaultOpen }) => {
165
165
  var _ref, _parentConfig$isModal, _parentConfig$setModa;
166
166
  const parentConfig = (0, react.useContext)(CopilotChatConfiguration);
167
167
  const stableLabels = useShallowStableRef(labels);
@@ -176,6 +176,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
176
176
  if (parentConfig === null || parentConfig === void 0 ? void 0 : parentConfig.threadId) return parentConfig.threadId;
177
177
  return (0, _copilotkit_shared.randomUUID)();
178
178
  }, [threadId, parentConfig === null || parentConfig === void 0 ? void 0 : parentConfig.threadId]);
179
+ const resolvedHasExplicitThreadId = (hasExplicitThreadId !== void 0 ? hasExplicitThreadId : !!threadId) || !!(parentConfig === null || parentConfig === void 0 ? void 0 : parentConfig.hasExplicitThreadId);
179
180
  const [internalModalOpen, setInternalModalOpen] = (0, react.useState)(isModalDefaultOpen !== null && isModalDefaultOpen !== void 0 ? isModalDefaultOpen : true);
180
181
  const hasExplicitDefault = isModalDefaultOpen !== void 0;
181
182
  const setAndSync = (0, react.useCallback)((open) => {
@@ -198,12 +199,14 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
198
199
  labels: mergedLabels,
199
200
  agentId: resolvedAgentId,
200
201
  threadId: resolvedThreadId,
202
+ hasExplicitThreadId: resolvedHasExplicitThreadId,
201
203
  isModalOpen: resolvedIsModalOpen,
202
204
  setModalOpen: resolvedSetModalOpen
203
205
  }), [
204
206
  mergedLabels,
205
207
  resolvedAgentId,
206
208
  resolvedThreadId,
209
+ resolvedHasExplicitThreadId,
207
210
  resolvedIsModalOpen,
208
211
  resolvedSetModalOpen
209
212
  ]);
@@ -599,7 +602,7 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
599
602
  //#region src/v2/components/chat/CopilotChatInput.tsx
600
603
  const SLASH_MENU_MAX_VISIBLE_ITEMS = 5;
601
604
  const SLASH_MENU_ITEM_HEIGHT_PX = 40;
602
- function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning = false, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, onAddFile, onChange, value, toolsMenu, autoFocus = false, positioning = "static", keyboardHeight = 0, containerRef, showDisclaimer, textArea, sendButton, startTranscribeButton, cancelTranscribeButton, finishTranscribeButton, addMenuButton, audioRecorder, disclaimer, children, className, ...props }) {
605
+ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning = false, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, onAddFile, onChange, value, toolsMenu, autoFocus = false, positioning = "static", keyboardHeight = 0, containerRef, showDisclaimer, bottomAnchored = false, textArea, sendButton, startTranscribeButton, cancelTranscribeButton, finishTranscribeButton, addMenuButton, audioRecorder, disclaimer, children, className, ...props }) {
603
606
  var _config$labels;
604
607
  const isControlled = value !== void 0;
605
608
  const [internalValue, setInternalValue] = (0, react.useState)(() => value !== null && value !== void 0 ? value : "");
@@ -1134,7 +1137,8 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
1134
1137
  className: cn("cpk:pointer-events-none cpk:relative cpk:z-20", positioning === "absolute" && "cpk:absolute cpk:bottom-0 cpk:left-0 cpk:right-0", className),
1135
1138
  style: {
1136
1139
  transform: keyboardHeight > 0 ? `translateY(-${keyboardHeight}px)` : void 0,
1137
- transition: "transform 0.2s ease-out"
1140
+ transition: "transform 0.2s ease-out",
1141
+ ...positioning === "absolute" || bottomAnchored ? { paddingBottom: "var(--copilotkit-license-banner-offset, 0px)" } : {}
1138
1142
  },
1139
1143
  ...props,
1140
1144
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -1361,6 +1365,8 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
1361
1365
 
1362
1366
  //#endregion
1363
1367
  //#region src/v2/components/license-warning-banner.tsx
1368
+ const LICENSE_BANNER_OFFSET_PX = 52;
1369
+ const LICENSE_BANNER_OFFSET_VAR = "--copilotkit-license-banner-offset";
1364
1370
  const BANNER_STYLES = {
1365
1371
  base: {
1366
1372
  position: "fixed",
@@ -1402,6 +1408,14 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
1402
1408
  }
1403
1409
  }
1404
1410
  function BannerShell({ severity, message, actionLabel, actionUrl, onDismiss }) {
1411
+ (0, react.useEffect)(() => {
1412
+ if (typeof document === "undefined") return;
1413
+ const root = document.documentElement;
1414
+ root.style.setProperty(LICENSE_BANNER_OFFSET_VAR, `${LICENSE_BANNER_OFFSET_PX}px`);
1415
+ return () => {
1416
+ root.style.removeProperty(LICENSE_BANNER_OFFSET_VAR);
1417
+ };
1418
+ }, []);
1405
1419
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1406
1420
  style: {
1407
1421
  ...BANNER_STYLES.base,
@@ -3386,7 +3400,6 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
3386
3400
  didMountRef.current = true;
3387
3401
  }, []);
3388
3402
  (0, react.useEffect)(() => {
3389
- if (defaultThrottleMs !== void 0 && (!Number.isFinite(defaultThrottleMs) || defaultThrottleMs < 0)) console.error(`CopilotKitProvider: defaultThrottleMs must be a non-negative finite number, got ${defaultThrottleMs}. useAgent hooks without an explicit throttleMs will fall back to unthrottled.`);
3390
3403
  copilotkit.setDefaultThrottleMs(defaultThrottleMs);
3391
3404
  }, [copilotkit, defaultThrottleMs]);
3392
3405
  const designSkill = (_openGenerativeUI$des = openGenerativeUI === null || openGenerativeUI === void 0 ? void 0 : openGenerativeUI.designSkill) !== null && _openGenerativeUI$des !== void 0 ? _openGenerativeUI$des : DEFAULT_DESIGN_SKILL;
@@ -3608,16 +3621,6 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
3608
3621
  const providerThrottleMs = copilotkit.defaultThrottleMs;
3609
3622
  const chatConfig = useCopilotChatConfiguration();
3610
3623
  (_threadId = threadId) !== null && _threadId !== void 0 || (threadId = chatConfig === null || chatConfig === void 0 ? void 0 : chatConfig.threadId);
3611
- const effectiveThrottleMs = (0, react.useMemo)(() => {
3612
- var _ref;
3613
- const resolved = (_ref = throttleMs !== null && throttleMs !== void 0 ? throttleMs : providerThrottleMs) !== null && _ref !== void 0 ? _ref : 0;
3614
- if (!Number.isFinite(resolved) || resolved < 0) {
3615
- const source = throttleMs !== void 0 ? "hook-level throttleMs" : "provider-level defaultThrottleMs";
3616
- console.error(`useAgent: ${source} must be a non-negative finite number, got ${resolved}. Falling back to unthrottled.`);
3617
- return 0;
3618
- }
3619
- return resolved;
3620
- }, [throttleMs, providerThrottleMs]);
3621
3624
  const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
3622
3625
  const updateFlags = (0, react.useMemo)(() => updates !== null && updates !== void 0 ? updates : ALL_UPDATES, [JSON.stringify(updates)]);
3623
3626
  const provisionalAgentCache = (0, react.useRef)(/* @__PURE__ */ new Map());
@@ -3681,9 +3684,8 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
3681
3684
  ]);
3682
3685
  (0, react.useEffect)(() => {
3683
3686
  if (updateFlags.length === 0) return;
3684
- const handlers = {};
3685
- let timerId = null;
3686
3687
  let active = true;
3688
+ const handlers = {};
3687
3689
  let batchScheduled = false;
3688
3690
  const batchedForceUpdate = () => {
3689
3691
  if (!active) return;
@@ -3695,46 +3697,24 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
3695
3697
  });
3696
3698
  }
3697
3699
  };
3698
- if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) {
3699
- const ms = effectiveThrottleMs;
3700
- if (ms > 0) {
3701
- let throttleActive = false;
3702
- let pending = false;
3703
- const throttledNotify = () => {
3704
- if (!active) return;
3705
- if (!throttleActive) {
3706
- throttleActive = true;
3707
- pending = false;
3708
- forceUpdate();
3709
- timerId = setTimeout(function trailingEdge() {
3710
- timerId = null;
3711
- if (active && pending) {
3712
- pending = false;
3713
- forceUpdate();
3714
- timerId = setTimeout(trailingEdge, ms);
3715
- } else throttleActive = false;
3716
- }, ms);
3717
- } else pending = true;
3718
- };
3719
- handlers.onMessagesChanged = throttledNotify;
3720
- } else handlers.onMessagesChanged = forceUpdate;
3721
- }
3700
+ if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) handlers.onMessagesChanged = forceUpdate;
3722
3701
  if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) handlers.onStateChanged = batchedForceUpdate;
3723
3702
  if (updateFlags.includes(UseAgentUpdate.OnRunStatusChanged)) {
3724
3703
  handlers.onRunInitialized = batchedForceUpdate;
3725
3704
  handlers.onRunFinalized = batchedForceUpdate;
3726
3705
  handlers.onRunFailed = batchedForceUpdate;
3706
+ handlers.onRunErrorEvent = batchedForceUpdate;
3727
3707
  }
3728
- const subscription = agent.subscribe(handlers);
3708
+ const subscription = copilotkit.subscribeToAgentWithOptions(agent, handlers, { throttleMs });
3729
3709
  return () => {
3730
3710
  active = false;
3731
- if (timerId !== null) clearTimeout(timerId);
3732
3711
  subscription.unsubscribe();
3733
3712
  };
3734
3713
  }, [
3735
3714
  agent,
3736
3715
  forceUpdate,
3737
- effectiveThrottleMs,
3716
+ throttleMs,
3717
+ providerThrottleMs,
3738
3718
  updateFlags
3739
3719
  ]);
3740
3720
  (0, react.useEffect)(() => {
@@ -4705,13 +4685,14 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
4705
4685
  const { copilotkit } = useCopilotKit();
4706
4686
  const [store] = (0, react.useState)(() => (0, _copilotkit_core.ɵcreateThreadStore)({ fetch: globalThis.fetch }));
4707
4687
  const coreThreads = useThreadStoreSelector(store, _copilotkit_core.ɵselectThreads);
4708
- const threads = (0, react.useMemo)(() => coreThreads.map(({ id, agentId, name, archived, createdAt, updatedAt }) => ({
4688
+ const threads = (0, react.useMemo)(() => coreThreads.map(({ id, agentId, name, archived, createdAt, updatedAt, lastRunAt }) => ({
4709
4689
  id,
4710
4690
  agentId,
4711
4691
  name,
4712
4692
  archived,
4713
4693
  createdAt,
4714
- updatedAt
4694
+ updatedAt,
4695
+ ...lastRunAt !== void 0 ? { lastRunAt } : {}
4715
4696
  })), [coreThreads]);
4716
4697
  const storeIsLoading = useThreadStoreSelector(store, _copilotkit_core.ɵselectThreadsIsLoading);
4717
4698
  const storeError = useThreadStoreSelector(store, _copilotkit_core.ɵselectThreadsError);
@@ -4725,7 +4706,9 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
4725
4706
  if (copilotkit.runtimeUrl) return null;
4726
4707
  return /* @__PURE__ */ new Error("Runtime URL is not configured");
4727
4708
  }, [copilotkit.runtimeUrl]);
4728
- const isLoading = runtimeError ? false : storeIsLoading;
4709
+ const [hasDispatchedContext, setHasDispatchedContext] = (0, react.useState)(false);
4710
+ const preConnectLoading = !!copilotkit.runtimeUrl && !hasDispatchedContext;
4711
+ const isLoading = runtimeError ? false : preConnectLoading || storeIsLoading;
4729
4712
  const error = runtimeError !== null && runtimeError !== void 0 ? runtimeError : storeError;
4730
4713
  (0, react.useEffect)(() => {
4731
4714
  store.start();
@@ -4733,20 +4716,28 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
4733
4716
  store.stop();
4734
4717
  };
4735
4718
  }, [store]);
4719
+ const runtimeStatus = copilotkit.runtimeConnectionStatus;
4736
4720
  (0, react.useEffect)(() => {
4737
4721
  var _copilotkit$intellige;
4738
- const context = copilotkit.runtimeUrl ? {
4722
+ if (!copilotkit.runtimeUrl) {
4723
+ store.setContext(null);
4724
+ return;
4725
+ }
4726
+ if (runtimeStatus !== _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Connected) return;
4727
+ const context = {
4739
4728
  runtimeUrl: copilotkit.runtimeUrl,
4740
4729
  headers: { ...copilotkit.headers },
4741
4730
  wsUrl: (_copilotkit$intellige = copilotkit.intelligence) === null || _copilotkit$intellige === void 0 ? void 0 : _copilotkit$intellige.wsUrl,
4742
4731
  agentId,
4743
4732
  includeArchived,
4744
4733
  limit
4745
- } : null;
4734
+ };
4746
4735
  store.setContext(context);
4736
+ setHasDispatchedContext(true);
4747
4737
  }, [
4748
4738
  store,
4749
4739
  copilotkit.runtimeUrl,
4740
+ runtimeStatus,
4750
4741
  headersKey,
4751
4742
  (_copilotkit$intellige2 = copilotkit.intelligence) === null || _copilotkit$intellige2 === void 0 ? void 0 : _copilotkit$intellige2.wsUrl,
4752
4743
  agentId,
@@ -5966,6 +5957,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
5966
5957
  const CopilotChatAttachmentQueue = ({ attachments, onRemoveAttachment, className }) => {
5967
5958
  if (attachments.length === 0) return null;
5968
5959
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5960
+ "data-testid": "copilot-attachment-queue",
5969
5961
  className: cn("cpk:flex cpk:flex-wrap cpk:gap-2 cpk:p-2", className),
5970
5962
  children: attachments.map((attachment) => {
5971
5963
  const isMedia = attachment.type === "image" || attachment.type === "video";
@@ -6308,9 +6300,91 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
6308
6300
  return keyboardState;
6309
6301
  }
6310
6302
 
6303
+ //#endregion
6304
+ //#region src/v2/components/chat/normalize-auto-scroll.ts
6305
+ const VALID = [
6306
+ "pin-to-bottom",
6307
+ "pin-to-send",
6308
+ "none"
6309
+ ];
6310
+ function normalizeAutoScroll(value) {
6311
+ if (value === void 0) return "pin-to-bottom";
6312
+ if (value === true) return "pin-to-bottom";
6313
+ if (value === false) return "none";
6314
+ if (VALID.includes(value)) return value;
6315
+ return "pin-to-bottom";
6316
+ }
6317
+
6318
+ //#endregion
6319
+ //#region src/v2/components/chat/last-user-message-context.ts
6320
+ const LastUserMessageContext = react.default.createContext({
6321
+ id: null,
6322
+ sendNonce: 0
6323
+ });
6324
+
6325
+ //#endregion
6326
+ //#region src/v2/hooks/use-pin-to-send.ts
6327
+ function usePinToSend({ scrollRef, contentRef, spacerRef, topOffset = 16 }) {
6328
+ const { id, sendNonce } = (0, react.useContext)(LastUserMessageContext);
6329
+ const lastNonceRef = (0, react.useRef)(-1);
6330
+ const currentSpacerHeightRef = (0, react.useRef)(0);
6331
+ (0, react.useEffect)(() => {
6332
+ if (sendNonce === lastNonceRef.current) return;
6333
+ lastNonceRef.current = sendNonce;
6334
+ if (!id) return;
6335
+ const scrollEl = scrollRef.current;
6336
+ const contentEl = contentRef.current;
6337
+ const spacerEl = spacerRef.current;
6338
+ if (!scrollEl || !contentEl || !spacerEl) return;
6339
+ const escaped = typeof CSS !== "undefined" && CSS.escape ? CSS.escape(id) : id.replace(/[!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g, "\\$&");
6340
+ const targetEl = contentEl.querySelector(`[data-message-id="${escaped}"]`);
6341
+ if (!targetEl) return;
6342
+ const viewportHeight = scrollEl.clientHeight;
6343
+ const userMessageHeight = targetEl.getBoundingClientRect().height;
6344
+ const paddingTop = parseFloat(getComputedStyle(targetEl).paddingTop) || 0;
6345
+ const bubbleHeight = Math.max(0, userMessageHeight - paddingTop);
6346
+ const spacerHeight = Math.max(0, viewportHeight - bubbleHeight - topOffset);
6347
+ spacerEl.style.height = `${spacerHeight}px`;
6348
+ currentSpacerHeightRef.current = spacerHeight;
6349
+ const raf = requestAnimationFrame(() => {
6350
+ const targetTop = computeOffsetTop(targetEl, scrollEl) + paddingTop - topOffset;
6351
+ scrollEl.scrollTo({
6352
+ top: Math.max(0, targetTop),
6353
+ behavior: "smooth"
6354
+ });
6355
+ });
6356
+ const ro = new ResizeObserver(() => {
6357
+ if (!contentEl || !spacerEl || !scrollEl) return;
6358
+ const consumedBelow = contentEl.getBoundingClientRect().height - computeOffsetTop(targetEl, contentEl) - userMessageHeight;
6359
+ const remaining = Math.max(0, spacerHeight - consumedBelow);
6360
+ if (remaining < currentSpacerHeightRef.current) {
6361
+ spacerEl.style.height = `${remaining}px`;
6362
+ currentSpacerHeightRef.current = remaining;
6363
+ }
6364
+ });
6365
+ ro.observe(contentEl);
6366
+ return () => {
6367
+ cancelAnimationFrame(raf);
6368
+ ro.disconnect();
6369
+ };
6370
+ }, [
6371
+ id,
6372
+ sendNonce,
6373
+ scrollRef,
6374
+ contentRef,
6375
+ spacerRef,
6376
+ topOffset
6377
+ ]);
6378
+ }
6379
+ function computeOffsetTop(el, stopAt) {
6380
+ const elRect = el.getBoundingClientRect();
6381
+ const stopRect = stopAt.getBoundingClientRect();
6382
+ return elRect.top - stopRect.top + stopAt.scrollTop;
6383
+ }
6384
+
6311
6385
  //#endregion
6312
6386
  //#region src/v2/components/chat/CopilotChatView.tsx
6313
- const FEATHER_HEIGHT = 96;
6387
+ const SCROLL_BUTTON_OFFSET = 16;
6314
6388
  function DropOverlay() {
6315
6389
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6316
6390
  className: cn("cpk:absolute cpk:inset-0 cpk:z-50 cpk:pointer-events-none", "cpk:flex cpk:items-center cpk:justify-center", "cpk:bg-primary/5 cpk:backdrop-blur-[2px]", "cpk:border-2 cpk:border-dashed cpk:border-primary/40 cpk:rounded-lg cpk:m-2"),
@@ -6323,7 +6397,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
6323
6397
  })
6324
6398
  });
6325
6399
  }
6326
- function CopilotChatView({ messageView, input, scrollView, suggestionView, welcomeScreen, messages = [], autoScroll = true, isRunning = false, suggestions, suggestionLoadingIndexes, onSelectSuggestion, onSubmitMessage, onStop, inputMode, inputValue, onInputChange, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, attachments, onRemoveAttachment, onAddFile, dragOver, onDragOver, onDragLeave, onDrop, disclaimer, children, className, ...props }) {
6400
+ function CopilotChatView({ messageView, input, scrollView, suggestionView, welcomeScreen, messages = [], autoScroll = true, isRunning = false, suggestions, suggestionLoadingIndexes, onSelectSuggestion, onSubmitMessage, onStop, inputMode, inputValue, onInputChange, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, attachments, onRemoveAttachment, onAddFile, dragOver, onDragOver, onDragLeave, onDrop, isConnecting = false, hasExplicitThreadId = false, disclaimer, children, className, ...props }) {
6327
6401
  const inputContainerRef = (0, react.useRef)(null);
6328
6402
  const [inputContainerHeight, setInputContainerHeight] = (0, react.useState)(0);
6329
6403
  const [isResizing, setIsResizing] = (0, react.useState)(false);
@@ -6373,11 +6447,11 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
6373
6447
  onAddFile,
6374
6448
  positioning: "static",
6375
6449
  keyboardHeight: isKeyboardOpen ? keyboardHeight : 0,
6376
- containerRef: inputContainerRef,
6377
6450
  showDisclaimer: true,
6451
+ bottomAnchored: true,
6378
6452
  ...disclaimer !== void 0 ? { disclaimer } : {}
6379
6453
  });
6380
- const hasSuggestions = Array.isArray(suggestions) && suggestions.length > 0;
6454
+ const hasSuggestions = !isConnecting && !isRunning && Array.isArray(suggestions) && suggestions.length > 0;
6381
6455
  const BoundSuggestionView = hasSuggestions ? renderSlot(suggestionView, CopilotChatSuggestionView, {
6382
6456
  suggestions,
6383
6457
  loadingIndexes: suggestionLoadingIndexes,
@@ -6389,7 +6463,8 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
6389
6463
  inputContainerHeight,
6390
6464
  isResizing,
6391
6465
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6392
- style: { paddingBottom: `${hasSuggestions ? 4 : 32}px` },
6466
+ "data-testid": "copilot-scroll-content",
6467
+ style: { paddingBottom: `${inputContainerHeight + (hasSuggestions ? 4 : 32)}px` },
6393
6468
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6394
6469
  className: "cpk:max-w-3xl cpk:mx-auto",
6395
6470
  children: [BoundMessageView, hasSuggestions ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -6399,7 +6474,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
6399
6474
  })
6400
6475
  })
6401
6476
  });
6402
- if (messages.length === 0 && !(welcomeScreen === false)) {
6477
+ if (messages.length === 0 && !(welcomeScreen === false) && !isConnecting && !hasExplicitThreadId) {
6403
6478
  const BoundInputForWelcome = renderSlot(input, CopilotChatInput_default, {
6404
6479
  onSubmitMessage,
6405
6480
  onStop,
@@ -6463,15 +6538,19 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
6463
6538
  children: [
6464
6539
  dragOver && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropOverlay, {}),
6465
6540
  BoundScrollView,
6466
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6467
- className: "cpk:max-w-3xl cpk:mx-auto cpk:w-full",
6468
- children: attachments && attachments.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatAttachmentQueue, {
6469
- attachments,
6470
- onRemoveAttachment: (id) => onRemoveAttachment === null || onRemoveAttachment === void 0 ? void 0 : onRemoveAttachment(id),
6471
- className: "cpk:px-4"
6472
- })
6473
- }),
6474
- BoundInput
6541
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6542
+ ref: inputContainerRef,
6543
+ "data-testid": "copilot-input-overlay",
6544
+ className: "cpk:absolute cpk:bottom-0 cpk:left-0 cpk:right-0 cpk:z-20 cpk:pointer-events-none",
6545
+ children: [attachments && attachments.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6546
+ className: "cpk:max-w-3xl cpk:mx-auto cpk:w-full cpk:pointer-events-auto",
6547
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatAttachmentQueue, {
6548
+ attachments,
6549
+ onRemoveAttachment: (id) => onRemoveAttachment === null || onRemoveAttachment === void 0 ? void 0 : onRemoveAttachment(id),
6550
+ className: "cpk:px-4"
6551
+ })
6552
+ }), BoundInput]
6553
+ })
6475
6554
  ]
6476
6555
  });
6477
6556
  }
@@ -6501,15 +6580,66 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
6501
6580
  BoundFeather,
6502
6581
  !isAtBottom && !isResizing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6503
6582
  className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
6504
- style: { bottom: `${inputContainerHeight + FEATHER_HEIGHT + 16}px` },
6583
+ style: { bottom: `${inputContainerHeight + SCROLL_BUTTON_OFFSET}px` },
6505
6584
  children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
6506
6585
  })
6507
6586
  ] })
6508
6587
  });
6509
6588
  };
6510
- _CopilotChatView.ScrollView = ({ children, autoScroll = true, scrollToBottomButton, feather, inputContainerHeight = 0, isResizing = false, className, ...props }) => {
6589
+ const PinToSendScrollContainer = ({ children, scrollRef, contentRef, scrollToBottom, scrollToBottomButton, feather, inputContainerHeight, isResizing, nonAutoScrollEl, nonAutoScrollRefCallback, showScrollButton, className, ...props }) => {
6590
+ const spacerRef = (0, react.useRef)(null);
6591
+ usePinToSend({
6592
+ scrollRef,
6593
+ contentRef,
6594
+ spacerRef,
6595
+ topOffset: 16
6596
+ });
6597
+ const BoundFeather = renderSlot(feather, CopilotChatView.Feather, {});
6598
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScrollElementContext.Provider, {
6599
+ value: nonAutoScrollEl,
6600
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6601
+ className: cn("cpk:h-full cpk:max-h-full cpk:flex cpk:flex-col cpk:min-h-0 cpk:relative", className),
6602
+ children: [
6603
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6604
+ ref: nonAutoScrollRefCallback,
6605
+ className: "cpk:flex-1 cpk:min-h-0 cpk:overflow-y-auto cpk:overflow-x-hidden",
6606
+ ...props,
6607
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6608
+ ref: contentRef,
6609
+ className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
6610
+ children
6611
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6612
+ ref: spacerRef,
6613
+ "data-pin-to-send-spacer": true,
6614
+ "aria-hidden": "true",
6615
+ style: {
6616
+ height: 0,
6617
+ flex: "0 0 auto"
6618
+ }
6619
+ })]
6620
+ }),
6621
+ BoundFeather,
6622
+ showScrollButton && !isResizing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6623
+ className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
6624
+ style: { bottom: `${inputContainerHeight + SCROLL_BUTTON_OFFSET}px` },
6625
+ children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
6626
+ })
6627
+ ]
6628
+ })
6629
+ });
6630
+ };
6631
+ _CopilotChatView.ScrollView = ({ children, autoScroll = "pin-to-bottom", scrollToBottomButton, feather, inputContainerHeight = 0, isResizing = false, className, ...props }) => {
6632
+ const mode = normalizeAutoScroll(autoScroll);
6511
6633
  const [hasMounted, setHasMounted] = (0, react.useState)(false);
6512
- const { scrollRef, contentRef, scrollToBottom } = (0, use_stick_to_bottom.useStickToBottom)();
6634
+ const scrollRef = (0, react.useRef)(null);
6635
+ const contentRef = (0, react.useRef)(null);
6636
+ const scrollToBottom = (0, react.useCallback)(() => {
6637
+ const el = scrollRef.current;
6638
+ if (el) el.scrollTo({
6639
+ top: el.scrollHeight,
6640
+ behavior: "smooth"
6641
+ });
6642
+ }, []);
6513
6643
  const [showScrollButton, setShowScrollButton] = (0, react.useState)(false);
6514
6644
  const [nonAutoScrollEl, setNonAutoScrollEl] = (0, react.useState)(null);
6515
6645
  const nonAutoScrollRefCallback = (0, react.useCallback)((el) => {
@@ -6520,7 +6650,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
6520
6650
  setHasMounted(true);
6521
6651
  }, []);
6522
6652
  (0, react.useEffect)(() => {
6523
- if (autoScroll) return;
6653
+ if (mode === "pin-to-bottom") return;
6524
6654
  const scrollElement = scrollRef.current;
6525
6655
  if (!scrollElement) return;
6526
6656
  const checkScroll = () => {
@@ -6534,7 +6664,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
6534
6664
  scrollElement.removeEventListener("scroll", checkScroll);
6535
6665
  resizeObserver.disconnect();
6536
6666
  };
6537
- }, [scrollRef, autoScroll]);
6667
+ }, [scrollRef, mode]);
6538
6668
  if (!hasMounted) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6539
6669
  className: "cpk:h-full cpk:max-h-full cpk:flex cpk:flex-col cpk:min-h-0 cpk:overflow-y-auto cpk:overflow-x-hidden",
6540
6670
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -6542,7 +6672,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
6542
6672
  children
6543
6673
  })
6544
6674
  });
6545
- if (!autoScroll) {
6675
+ if (mode === "none") {
6546
6676
  const BoundFeather = renderSlot(feather, CopilotChatView.Feather, {});
6547
6677
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScrollElementContext.Provider, {
6548
6678
  value: nonAutoScrollEl,
@@ -6559,13 +6689,28 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
6559
6689
  BoundFeather,
6560
6690
  showScrollButton && !isResizing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6561
6691
  className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
6562
- style: { bottom: `${inputContainerHeight + FEATHER_HEIGHT + 16}px` },
6692
+ style: { bottom: `${inputContainerHeight + SCROLL_BUTTON_OFFSET}px` },
6563
6693
  children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
6564
6694
  })
6565
6695
  ]
6566
6696
  })
6567
6697
  });
6568
6698
  }
6699
+ if (mode === "pin-to-send") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PinToSendScrollContainer, {
6700
+ scrollRef,
6701
+ contentRef,
6702
+ scrollToBottom,
6703
+ scrollToBottomButton,
6704
+ feather,
6705
+ inputContainerHeight,
6706
+ isResizing,
6707
+ nonAutoScrollEl,
6708
+ nonAutoScrollRefCallback,
6709
+ showScrollButton,
6710
+ className,
6711
+ ...props,
6712
+ children
6713
+ });
6569
6714
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(use_stick_to_bottom.StickToBottom, {
6570
6715
  className: cn("cpk:flex-1 cpk:max-h-full cpk:flex cpk:flex-col cpk:min-h-0", className),
6571
6716
  resize: "smooth",
@@ -6588,9 +6733,8 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
6588
6733
  ...props,
6589
6734
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronDown, { className: "cpk:w-4 cpk:h-4 cpk:text-gray-600 cpk:dark:text-white" })
6590
6735
  });
6591
- _CopilotChatView.Feather = ({ className, style, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6592
- className: cn("cpk:absolute cpk:bottom-0 cpk:left-0 cpk:right-4 cpk:h-24 cpk:pointer-events-none cpk:z-10 cpk:bg-gradient-to-t", "cpk:from-white cpk:via-white cpk:to-transparent", "cpk:dark:from-[rgb(33,33,33)] cpk:dark:via-[rgb(33,33,33)]", className),
6593
- style,
6736
+ _CopilotChatView.Feather = ({ className, ...props }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6737
+ className,
6594
6738
  ...props
6595
6739
  });
6596
6740
  _CopilotChatView.WelcomeMessage = ({ className, ...props }) => {
@@ -6762,10 +6906,9 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
6762
6906
  var _ref, _attachmentsConfig$ac;
6763
6907
  const existingConfig = useCopilotChatConfiguration();
6764
6908
  const resolvedAgentId = (_ref = agentId !== null && agentId !== void 0 ? agentId : existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.agentId) !== null && _ref !== void 0 ? _ref : _copilotkit_shared.DEFAULT_AGENT_ID;
6765
- const resolvedThreadId = (0, react.useMemo)(() => {
6766
- var _ref2;
6767
- return (_ref2 = threadId !== null && threadId !== void 0 ? threadId : existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.threadId) !== null && _ref2 !== void 0 ? _ref2 : (0, _copilotkit_shared.randomUUID)();
6768
- }, [threadId, existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.threadId]);
6909
+ const providedThreadId = threadId !== null && threadId !== void 0 ? threadId : existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.threadId;
6910
+ const resolvedThreadId = (0, react.useMemo)(() => providedThreadId !== null && providedThreadId !== void 0 ? providedThreadId : (0, _copilotkit_shared.randomUUID)(), [providedThreadId]);
6911
+ const hasExplicitThreadId = !!threadId || !!(existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.hasExplicitThreadId);
6769
6912
  const { agent } = useAgent({
6770
6913
  agentId: resolvedAgentId,
6771
6914
  threadId: resolvedThreadId,
@@ -6807,7 +6950,10 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
6807
6950
  const isTranscriptionEnabled = copilotkit.audioFileTranscriptionEnabled;
6808
6951
  const isMediaRecorderSupported = typeof window !== "undefined" && typeof MediaRecorder !== "undefined";
6809
6952
  const { messageView: providedMessageView, suggestionView: providedSuggestionView, onStop: providedStopHandler, ...restProps } = props;
6953
+ const [lastConnectedThreadId, setLastConnectedThreadId] = (0, react.useState)(null);
6954
+ const isConnecting = hasExplicitThreadId && lastConnectedThreadId !== resolvedThreadId;
6810
6955
  (0, react.useEffect)(() => {
6956
+ if (!hasExplicitThreadId) return;
6811
6957
  let detached = false;
6812
6958
  const connectAbortController = new AbortController();
6813
6959
  if (agent instanceof _ag_ui_client.HttpAgent) agent.abortController = connectAbortController;
@@ -6817,6 +6963,10 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
6817
6963
  } catch (error) {
6818
6964
  if (detached) return;
6819
6965
  console.error("CopilotChat: connectAgent failed", error);
6966
+ } finally {
6967
+ if (!detached) (typeof requestAnimationFrame === "function" ? requestAnimationFrame : (cb) => setTimeout(cb, 16))(() => {
6968
+ if (!detached) setLastConnectedThreadId(resolvedThreadId);
6969
+ });
6820
6970
  }
6821
6971
  };
6822
6972
  connect(agent);
@@ -6828,7 +6978,8 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
6828
6978
  }, [
6829
6979
  resolvedThreadId,
6830
6980
  agent,
6831
- resolvedAgentId
6981
+ resolvedAgentId,
6982
+ hasExplicitThreadId
6832
6983
  ]);
6833
6984
  const onSubmitInput = (0, react.useCallback)(async (value) => {
6834
6985
  if (selectedAttachments.some((a) => a.status === "uploading")) {
@@ -6985,6 +7136,22 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
6985
7136
  }).join(";") : "";
6986
7137
  return `${m.id}:${m.role}:${contentKey}:${toolCallsKey}`;
6987
7138
  }).join(",")]);
7139
+ const lastUserMessageId = (0, react.useMemo)(() => {
7140
+ for (let i = messages.length - 1; i >= 0; i--) if (messages[i].role === "user") return messages[i].id;
7141
+ return null;
7142
+ }, [messages]);
7143
+ const [sendNonce, setSendNonce] = (0, react.useState)(0);
7144
+ const prevLastUserMessageIdRef = (0, react.useRef)(lastUserMessageId);
7145
+ (0, react.useEffect)(() => {
7146
+ if (lastUserMessageId && lastUserMessageId !== prevLastUserMessageIdRef.current) {
7147
+ setSendNonce((n) => n + 1);
7148
+ prevLastUserMessageIdRef.current = lastUserMessageId;
7149
+ }
7150
+ }, [lastUserMessageId]);
7151
+ const lastUserMessageState = (0, react.useMemo)(() => ({
7152
+ id: lastUserMessageId,
7153
+ sendNonce
7154
+ }), [lastUserMessageId, sendNonce]);
6988
7155
  const RenderedChatView = renderSlot(chatView, CopilotChatView, {
6989
7156
  ...mergedProps,
6990
7157
  messages,
@@ -7003,11 +7170,14 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
7003
7170
  dragOver,
7004
7171
  onDragOver: handleDragOver,
7005
7172
  onDragLeave: handleDragLeave,
7006
- onDrop: handleDrop
7173
+ onDrop: handleDrop,
7174
+ isConnecting,
7175
+ hasExplicitThreadId
7007
7176
  });
7008
7177
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatConfigurationProvider, {
7009
7178
  agentId: resolvedAgentId,
7010
7179
  threadId: resolvedThreadId,
7180
+ hasExplicitThreadId,
7011
7181
  labels,
7012
7182
  isModalDefaultOpen,
7013
7183
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -7038,7 +7208,10 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
7038
7208
  },
7039
7209
  children: transcriptionError
7040
7210
  }),
7041
- RenderedChatView
7211
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LastUserMessageContext.Provider, {
7212
+ value: lastUserMessageState,
7213
+ children: RenderedChatView
7214
+ })
7042
7215
  ]
7043
7216
  })
7044
7217
  });
@@ -8663,12 +8836,19 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
8663
8836
  //#region src/context/threads-context.tsx
8664
8837
  const ThreadsContext = (0, react.createContext)(void 0);
8665
8838
  function ThreadsProvider({ children, threadId: explicitThreadId }) {
8666
- const [internalThreadId, setThreadId] = (0, react.useState)(() => (0, _copilotkit_shared.randomUUID)());
8839
+ const [internalThreadId, setInternalThreadId] = (0, react.useState)(() => (0, _copilotkit_shared.randomUUID)());
8840
+ const [internalIsExplicit, setInternalIsExplicit] = (0, react.useState)(false);
8667
8841
  const threadId = explicitThreadId !== null && explicitThreadId !== void 0 ? explicitThreadId : internalThreadId;
8842
+ const isThreadIdExplicit = explicitThreadId != null || internalIsExplicit;
8843
+ const setThreadId = (0, react.useCallback)((value) => {
8844
+ setInternalThreadId(value);
8845
+ setInternalIsExplicit(true);
8846
+ }, []);
8668
8847
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ThreadsContext.Provider, {
8669
8848
  value: {
8670
8849
  threadId,
8671
- setThreadId
8850
+ setThreadId,
8851
+ isThreadIdExplicit
8672
8852
  },
8673
8853
  children
8674
8854
  });
@@ -9388,7 +9568,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
9388
9568
  if (props.agent) setAgentSession({ agentName: props.agent });
9389
9569
  else setAgentSession(null);
9390
9570
  }, [props.agent]);
9391
- const { threadId, setThreadId: setInternalThreadId } = useThreads$1();
9571
+ const { threadId, setThreadId: setInternalThreadId, isThreadIdExplicit } = useThreads$1();
9392
9572
  const setThreadId = (0, react.useCallback)((value) => {
9393
9573
  if (props.threadId) throw new Error("Cannot call setThreadId() when threadId is provided via props.");
9394
9574
  setInternalThreadId(value);
@@ -9594,6 +9774,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
9594
9774
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatConfigurationProvider, {
9595
9775
  agentId: (_props$agent2 = props.agent) !== null && _props$agent2 !== void 0 ? _props$agent2 : "default",
9596
9776
  threadId,
9777
+ hasExplicitThreadId: isThreadIdExplicit,
9597
9778
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CopilotContext.Provider, {
9598
9779
  value: copilotContextValue,
9599
9780
  children: [