@copilotkit/react-core 1.56.1 → 1.56.2-canary.test-welcome-screen

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 (57) hide show
  1. package/dist/{copilotkit-CSJw5BG8.cjs → copilotkit-By2G6-Zx.cjs} +250 -63
  2. package/dist/copilotkit-By2G6-Zx.cjs.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-Cj2ZIxVr.mjs → copilotkit-PzJlPKcU.mjs} +251 -64
  8. package/dist/copilotkit-PzJlPKcU.mjs.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 +249 -66
  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/CopilotChatInput.tsx +22 -0
  31. package/src/v2/components/chat/CopilotChatView.tsx +206 -11
  32. package/src/v2/components/chat/__tests__/CopilotChat.absentThreadConnect.test.tsx +66 -0
  33. package/src/v2/components/chat/__tests__/CopilotChat.welcomeGate.test.tsx +186 -0
  34. package/src/v2/components/chat/__tests__/CopilotChatActivityRendering.e2e.test.tsx +300 -2
  35. package/src/v2/components/chat/__tests__/CopilotChatView.connectingGate.test.tsx +56 -0
  36. package/src/v2/components/chat/__tests__/CopilotChatView.pinToSend.test.tsx +94 -0
  37. package/src/v2/components/chat/__tests__/copilot-chat-throttle.test.tsx +0 -1
  38. package/src/v2/components/chat/__tests__/normalize-auto-scroll.test.ts +37 -0
  39. package/src/v2/components/chat/index.ts +2 -0
  40. package/src/v2/components/chat/last-user-message-context.ts +21 -0
  41. package/src/v2/components/chat/normalize-auto-scroll.ts +17 -0
  42. package/src/v2/components/license-warning-banner.tsx +20 -1
  43. package/src/v2/hooks/__tests__/use-agent-stability.test.tsx +6 -0
  44. package/src/v2/hooks/__tests__/use-agent-thread-isolation.test.tsx +6 -0
  45. package/src/v2/hooks/__tests__/use-agent-throttle.test.tsx +76 -50
  46. package/src/v2/hooks/__tests__/use-pin-to-send.test.tsx +219 -0
  47. package/src/v2/hooks/__tests__/use-threads.test.tsx +68 -0
  48. package/src/v2/hooks/use-agent.tsx +34 -77
  49. package/src/v2/hooks/use-pin-to-send.ts +94 -0
  50. package/src/v2/hooks/use-threads.tsx +55 -12
  51. package/src/v2/providers/CopilotChatConfigurationProvider.tsx +29 -1
  52. package/src/v2/providers/CopilotKitProvider.tsx +2 -11
  53. package/src/v2/providers/__tests__/CopilotChatConfigurationProvider.test.tsx +106 -0
  54. package/dist/copilotkit-BtP7w7cT.d.cts.map +0 -1
  55. package/dist/copilotkit-CCbxm6JM.d.mts.map +0 -1
  56. package/dist/copilotkit-CSJw5BG8.cjs.map +0 -1
  57. package/dist/copilotkit-Cj2ZIxVr.mjs.map +0 -1
@@ -18,7 +18,7 @@ import { A2UIProvider, A2UIRenderer, A2UI_SCHEMA_CONTEXT_DESCRIPTION, DEFAULT_SU
18
18
  import { zodToJsonSchema } from "zod-to-json-schema";
19
19
  import { useVirtualizer } from "@tanstack/react-virtual";
20
20
  import { createPortal, flushSync } from "react-dom";
21
- import { StickToBottom, useStickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
21
+ import { StickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
22
22
  import ReactMarkdown from "react-markdown";
23
23
 
24
24
  //#region src/v2/lib/slots.tsx
@@ -145,7 +145,7 @@ const CopilotChatDefaultLabels = {
145
145
  welcomeMessageText: "How can I help you today?"
146
146
  };
147
147
  const CopilotChatConfiguration = createContext(null);
148
- const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId, isModalDefaultOpen }) => {
148
+ const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId, hasExplicitThreadId, isModalDefaultOpen }) => {
149
149
  const parentConfig = useContext(CopilotChatConfiguration);
150
150
  const stableLabels = useShallowStableRef(labels);
151
151
  const mergedLabels = useMemo(() => ({
@@ -159,6 +159,7 @@ const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId,
159
159
  if (parentConfig?.threadId) return parentConfig.threadId;
160
160
  return randomUUID();
161
161
  }, [threadId, parentConfig?.threadId]);
162
+ const resolvedHasExplicitThreadId = (hasExplicitThreadId !== void 0 ? hasExplicitThreadId : !!threadId) || !!parentConfig?.hasExplicitThreadId;
162
163
  const [internalModalOpen, setInternalModalOpen] = useState(isModalDefaultOpen ?? true);
163
164
  const hasExplicitDefault = isModalDefaultOpen !== void 0;
164
165
  const setAndSync = useCallback((open) => {
@@ -181,12 +182,14 @@ const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId,
181
182
  labels: mergedLabels,
182
183
  agentId: resolvedAgentId,
183
184
  threadId: resolvedThreadId,
185
+ hasExplicitThreadId: resolvedHasExplicitThreadId,
184
186
  isModalOpen: resolvedIsModalOpen,
185
187
  setModalOpen: resolvedSetModalOpen
186
188
  }), [
187
189
  mergedLabels,
188
190
  resolvedAgentId,
189
191
  resolvedThreadId,
192
+ resolvedHasExplicitThreadId,
190
193
  resolvedIsModalOpen,
191
194
  resolvedSetModalOpen
192
195
  ]);
@@ -580,7 +583,7 @@ CopilotChatAudioRecorder.displayName = "CopilotChatAudioRecorder";
580
583
  //#region src/v2/components/chat/CopilotChatInput.tsx
581
584
  const SLASH_MENU_MAX_VISIBLE_ITEMS = 5;
582
585
  const SLASH_MENU_ITEM_HEIGHT_PX = 40;
583
- 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 }) {
586
+ 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 }) {
584
587
  const isControlled = value !== void 0;
585
588
  const [internalValue, setInternalValue] = useState(() => value ?? "");
586
589
  useEffect(() => {
@@ -1104,7 +1107,8 @@ function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning =
1104
1107
  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),
1105
1108
  style: {
1106
1109
  transform: keyboardHeight > 0 ? `translateY(-${keyboardHeight}px)` : void 0,
1107
- transition: "transform 0.2s ease-out"
1110
+ transition: "transform 0.2s ease-out",
1111
+ ...positioning === "absolute" || bottomAnchored ? { paddingBottom: "var(--copilotkit-license-banner-offset, 0px)" } : {}
1108
1112
  },
1109
1113
  ...props,
1110
1114
  children: [/* @__PURE__ */ jsx("div", {
@@ -1319,6 +1323,8 @@ CopilotKitInspector.displayName = "CopilotKitInspector";
1319
1323
 
1320
1324
  //#endregion
1321
1325
  //#region src/v2/components/license-warning-banner.tsx
1326
+ const LICENSE_BANNER_OFFSET_PX = 52;
1327
+ const LICENSE_BANNER_OFFSET_VAR = "--copilotkit-license-banner-offset";
1322
1328
  const BANNER_STYLES = {
1323
1329
  base: {
1324
1330
  position: "fixed",
@@ -1360,6 +1366,14 @@ function getSeverityStyle(severity) {
1360
1366
  }
1361
1367
  }
1362
1368
  function BannerShell({ severity, message, actionLabel, actionUrl, onDismiss }) {
1369
+ useEffect(() => {
1370
+ if (typeof document === "undefined") return;
1371
+ const root = document.documentElement;
1372
+ root.style.setProperty(LICENSE_BANNER_OFFSET_VAR, `${LICENSE_BANNER_OFFSET_PX}px`);
1373
+ return () => {
1374
+ root.style.removeProperty(LICENSE_BANNER_OFFSET_VAR);
1375
+ };
1376
+ }, []);
1363
1377
  return /* @__PURE__ */ jsxs("div", {
1364
1378
  style: {
1365
1379
  ...BANNER_STYLES.base,
@@ -3312,7 +3326,6 @@ const CopilotKitProvider = ({ children, runtimeUrl, headers: headersProp = {}, c
3312
3326
  didMountRef.current = true;
3313
3327
  }, []);
3314
3328
  useEffect(() => {
3315
- 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.`);
3316
3329
  copilotkit.setDefaultThrottleMs(defaultThrottleMs);
3317
3330
  }, [copilotkit, defaultThrottleMs]);
3318
3331
  const designSkill = openGenerativeUI?.designSkill ?? DEFAULT_DESIGN_SKILL;
@@ -3529,15 +3542,6 @@ function useAgent({ agentId, threadId, updates, throttleMs } = {}) {
3529
3542
  const providerThrottleMs = copilotkit.defaultThrottleMs;
3530
3543
  const chatConfig = useCopilotChatConfiguration();
3531
3544
  threadId ??= chatConfig?.threadId;
3532
- const effectiveThrottleMs = useMemo(() => {
3533
- const resolved = throttleMs ?? providerThrottleMs ?? 0;
3534
- if (!Number.isFinite(resolved) || resolved < 0) {
3535
- const source = throttleMs !== void 0 ? "hook-level throttleMs" : "provider-level defaultThrottleMs";
3536
- console.error(`useAgent: ${source} must be a non-negative finite number, got ${resolved}. Falling back to unthrottled.`);
3537
- return 0;
3538
- }
3539
- return resolved;
3540
- }, [throttleMs, providerThrottleMs]);
3541
3545
  const [, forceUpdate] = useReducer((x) => x + 1, 0);
3542
3546
  const updateFlags = useMemo(() => updates ?? ALL_UPDATES, [JSON.stringify(updates)]);
3543
3547
  const provisionalAgentCache = useRef(/* @__PURE__ */ new Map());
@@ -3600,9 +3604,8 @@ function useAgent({ agentId, threadId, updates, throttleMs } = {}) {
3600
3604
  ]);
3601
3605
  useEffect(() => {
3602
3606
  if (updateFlags.length === 0) return;
3603
- const handlers = {};
3604
- let timerId = null;
3605
3607
  let active = true;
3608
+ const handlers = {};
3606
3609
  let batchScheduled = false;
3607
3610
  const batchedForceUpdate = () => {
3608
3611
  if (!active) return;
@@ -3614,46 +3617,24 @@ function useAgent({ agentId, threadId, updates, throttleMs } = {}) {
3614
3617
  });
3615
3618
  }
3616
3619
  };
3617
- if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) {
3618
- const ms = effectiveThrottleMs;
3619
- if (ms > 0) {
3620
- let throttleActive = false;
3621
- let pending = false;
3622
- const throttledNotify = () => {
3623
- if (!active) return;
3624
- if (!throttleActive) {
3625
- throttleActive = true;
3626
- pending = false;
3627
- forceUpdate();
3628
- timerId = setTimeout(function trailingEdge() {
3629
- timerId = null;
3630
- if (active && pending) {
3631
- pending = false;
3632
- forceUpdate();
3633
- timerId = setTimeout(trailingEdge, ms);
3634
- } else throttleActive = false;
3635
- }, ms);
3636
- } else pending = true;
3637
- };
3638
- handlers.onMessagesChanged = throttledNotify;
3639
- } else handlers.onMessagesChanged = forceUpdate;
3640
- }
3620
+ if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) handlers.onMessagesChanged = forceUpdate;
3641
3621
  if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) handlers.onStateChanged = batchedForceUpdate;
3642
3622
  if (updateFlags.includes(UseAgentUpdate.OnRunStatusChanged)) {
3643
3623
  handlers.onRunInitialized = batchedForceUpdate;
3644
3624
  handlers.onRunFinalized = batchedForceUpdate;
3645
3625
  handlers.onRunFailed = batchedForceUpdate;
3626
+ handlers.onRunErrorEvent = batchedForceUpdate;
3646
3627
  }
3647
- const subscription = agent.subscribe(handlers);
3628
+ const subscription = copilotkit.subscribeToAgentWithOptions(agent, handlers, { throttleMs });
3648
3629
  return () => {
3649
3630
  active = false;
3650
- if (timerId !== null) clearTimeout(timerId);
3651
3631
  subscription.unsubscribe();
3652
3632
  };
3653
3633
  }, [
3654
3634
  agent,
3655
3635
  forceUpdate,
3656
- effectiveThrottleMs,
3636
+ throttleMs,
3637
+ providerThrottleMs,
3657
3638
  updateFlags
3658
3639
  ]);
3659
3640
  useEffect(() => {
@@ -4607,13 +4588,14 @@ function useThreads$1({ agentId, includeArchived, limit }) {
4607
4588
  const { copilotkit } = useCopilotKit();
4608
4589
  const [store] = useState(() => ɵcreateThreadStore({ fetch: globalThis.fetch }));
4609
4590
  const coreThreads = useThreadStoreSelector(store, ɵselectThreads);
4610
- const threads = useMemo(() => coreThreads.map(({ id, agentId, name, archived, createdAt, updatedAt }) => ({
4591
+ const threads = useMemo(() => coreThreads.map(({ id, agentId, name, archived, createdAt, updatedAt, lastRunAt }) => ({
4611
4592
  id,
4612
4593
  agentId,
4613
4594
  name,
4614
4595
  archived,
4615
4596
  createdAt,
4616
- updatedAt
4597
+ updatedAt,
4598
+ ...lastRunAt !== void 0 ? { lastRunAt } : {}
4617
4599
  })), [coreThreads]);
4618
4600
  const storeIsLoading = useThreadStoreSelector(store, ɵselectThreadsIsLoading);
4619
4601
  const storeError = useThreadStoreSelector(store, ɵselectThreadsError);
@@ -4626,7 +4608,9 @@ function useThreads$1({ agentId, includeArchived, limit }) {
4626
4608
  if (copilotkit.runtimeUrl) return null;
4627
4609
  return /* @__PURE__ */ new Error("Runtime URL is not configured");
4628
4610
  }, [copilotkit.runtimeUrl]);
4629
- const isLoading = runtimeError ? false : storeIsLoading;
4611
+ const [hasDispatchedContext, setHasDispatchedContext] = useState(false);
4612
+ const preConnectLoading = !!copilotkit.runtimeUrl && !hasDispatchedContext;
4613
+ const isLoading = runtimeError ? false : preConnectLoading || storeIsLoading;
4630
4614
  const error = runtimeError ?? storeError;
4631
4615
  useEffect(() => {
4632
4616
  store.start();
@@ -4634,19 +4618,27 @@ function useThreads$1({ agentId, includeArchived, limit }) {
4634
4618
  store.stop();
4635
4619
  };
4636
4620
  }, [store]);
4621
+ const runtimeStatus = copilotkit.runtimeConnectionStatus;
4637
4622
  useEffect(() => {
4638
- const context = copilotkit.runtimeUrl ? {
4623
+ if (!copilotkit.runtimeUrl) {
4624
+ store.setContext(null);
4625
+ return;
4626
+ }
4627
+ if (runtimeStatus !== CopilotKitCoreRuntimeConnectionStatus.Connected) return;
4628
+ const context = {
4639
4629
  runtimeUrl: copilotkit.runtimeUrl,
4640
4630
  headers: { ...copilotkit.headers },
4641
4631
  wsUrl: copilotkit.intelligence?.wsUrl,
4642
4632
  agentId,
4643
4633
  includeArchived,
4644
4634
  limit
4645
- } : null;
4635
+ };
4646
4636
  store.setContext(context);
4637
+ setHasDispatchedContext(true);
4647
4638
  }, [
4648
4639
  store,
4649
4640
  copilotkit.runtimeUrl,
4641
+ runtimeStatus,
4650
4642
  headersKey,
4651
4643
  copilotkit.intelligence?.wsUrl,
4652
4644
  agentId,
@@ -6171,9 +6163,97 @@ function useKeyboardHeight() {
6171
6163
  return keyboardState;
6172
6164
  }
6173
6165
 
6166
+ //#endregion
6167
+ //#region src/v2/components/chat/normalize-auto-scroll.ts
6168
+ const VALID = [
6169
+ "pin-to-bottom",
6170
+ "pin-to-send",
6171
+ "none"
6172
+ ];
6173
+ function normalizeAutoScroll(value) {
6174
+ if (value === void 0) return "pin-to-bottom";
6175
+ if (value === true) return "pin-to-bottom";
6176
+ if (value === false) return "none";
6177
+ if (VALID.includes(value)) return value;
6178
+ return "pin-to-bottom";
6179
+ }
6180
+
6181
+ //#endregion
6182
+ //#region src/v2/components/chat/last-user-message-context.ts
6183
+ const LastUserMessageContext = React.createContext({
6184
+ id: null,
6185
+ sendNonce: 0
6186
+ });
6187
+
6188
+ //#endregion
6189
+ //#region src/v2/hooks/use-pin-to-send.ts
6190
+ function usePinToSend({ scrollRef, contentRef, spacerRef, topOffset = 16 }) {
6191
+ const { id, sendNonce } = useContext(LastUserMessageContext);
6192
+ const lastNonceRef = useRef(-1);
6193
+ const currentSpacerHeightRef = useRef(0);
6194
+ useEffect(() => {
6195
+ if (sendNonce === lastNonceRef.current) return;
6196
+ lastNonceRef.current = sendNonce;
6197
+ if (!id) return;
6198
+ const scrollEl = scrollRef.current;
6199
+ const contentEl = contentRef.current;
6200
+ const spacerEl = spacerRef.current;
6201
+ if (!scrollEl || !contentEl || !spacerEl) return;
6202
+ const escaped = typeof CSS !== "undefined" && CSS.escape ? CSS.escape(id) : id.replace(/[!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g, "\\$&");
6203
+ const targetEl = contentEl.querySelector(`[data-message-id="${escaped}"]`);
6204
+ if (!targetEl) return;
6205
+ const viewportHeight = scrollEl.clientHeight;
6206
+ const userMessageHeight = targetEl.getBoundingClientRect().height;
6207
+ const paddingTop = parseFloat(getComputedStyle(targetEl).paddingTop) || 0;
6208
+ const bubbleHeight = Math.max(0, userMessageHeight - paddingTop);
6209
+ const spacerHeight = Math.max(0, viewportHeight - bubbleHeight - topOffset);
6210
+ spacerEl.style.height = `${spacerHeight}px`;
6211
+ currentSpacerHeightRef.current = spacerHeight;
6212
+ const raf = requestAnimationFrame(() => {
6213
+ const targetTop = computeOffsetTop(targetEl, scrollEl) + paddingTop - topOffset;
6214
+ scrollEl.scrollTo({
6215
+ top: Math.max(0, targetTop),
6216
+ behavior: "smooth"
6217
+ });
6218
+ });
6219
+ const ro = new ResizeObserver(() => {
6220
+ if (!contentEl || !spacerEl || !scrollEl) return;
6221
+ const consumedBelow = contentEl.getBoundingClientRect().height - computeOffsetTop(targetEl, contentEl) - userMessageHeight;
6222
+ const remaining = Math.max(0, spacerHeight - consumedBelow);
6223
+ if (remaining < currentSpacerHeightRef.current) {
6224
+ spacerEl.style.height = `${remaining}px`;
6225
+ currentSpacerHeightRef.current = remaining;
6226
+ }
6227
+ });
6228
+ ro.observe(contentEl);
6229
+ return () => {
6230
+ cancelAnimationFrame(raf);
6231
+ ro.disconnect();
6232
+ };
6233
+ }, [
6234
+ id,
6235
+ sendNonce,
6236
+ scrollRef,
6237
+ contentRef,
6238
+ spacerRef,
6239
+ topOffset
6240
+ ]);
6241
+ }
6242
+ function computeOffsetTop(el, stopAt) {
6243
+ const elRect = el.getBoundingClientRect();
6244
+ const stopRect = stopAt.getBoundingClientRect();
6245
+ return elRect.top - stopRect.top + stopAt.scrollTop;
6246
+ }
6247
+
6174
6248
  //#endregion
6175
6249
  //#region src/v2/components/chat/CopilotChatView.tsx
6176
6250
  const FEATHER_HEIGHT = 96;
6251
+ const PIN_TO_SEND_FEATHER_HEIGHT = 48;
6252
+ const PinToSendSoftFeather = ({ className, style, ...props }) => /* @__PURE__ */ jsx("div", {
6253
+ className: cn("cpk:absolute cpk:bottom-0 cpk:left-0 cpk:right-4 cpk:h-12 cpk:pointer-events-none cpk:z-10 cpk:bg-gradient-to-t", "cpk:from-white cpk:to-transparent", "cpk:dark:from-[rgb(33,33,33)]", className),
6254
+ style,
6255
+ ...props
6256
+ });
6177
6257
  function DropOverlay() {
6178
6258
  return /* @__PURE__ */ jsx("div", {
6179
6259
  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"),
@@ -6186,7 +6266,7 @@ function DropOverlay() {
6186
6266
  })
6187
6267
  });
6188
6268
  }
6189
- 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 }) {
6269
+ 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 }) {
6190
6270
  const inputContainerRef = useRef(null);
6191
6271
  const [inputContainerHeight, setInputContainerHeight] = useState(0);
6192
6272
  const [isResizing, setIsResizing] = useState(false);
@@ -6238,9 +6318,10 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
6238
6318
  keyboardHeight: isKeyboardOpen ? keyboardHeight : 0,
6239
6319
  containerRef: inputContainerRef,
6240
6320
  showDisclaimer: true,
6321
+ bottomAnchored: true,
6241
6322
  ...disclaimer !== void 0 ? { disclaimer } : {}
6242
6323
  });
6243
- const hasSuggestions = Array.isArray(suggestions) && suggestions.length > 0;
6324
+ const hasSuggestions = !isConnecting && !isRunning && Array.isArray(suggestions) && suggestions.length > 0;
6244
6325
  const BoundSuggestionView = hasSuggestions ? renderSlot(suggestionView, CopilotChatSuggestionView, {
6245
6326
  suggestions,
6246
6327
  loadingIndexes: suggestionLoadingIndexes,
@@ -6262,7 +6343,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
6262
6343
  })
6263
6344
  })
6264
6345
  });
6265
- if (messages.length === 0 && !(welcomeScreen === false)) {
6346
+ if (messages.length === 0 && !(welcomeScreen === false) && !isConnecting && !hasExplicitThreadId) {
6266
6347
  const BoundInputForWelcome = renderSlot(input, CopilotChatInput_default, {
6267
6348
  onSubmitMessage,
6268
6349
  onStop,
@@ -6369,9 +6450,60 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
6369
6450
  ] })
6370
6451
  });
6371
6452
  };
6372
- _CopilotChatView.ScrollView = ({ children, autoScroll = true, scrollToBottomButton, feather, inputContainerHeight = 0, isResizing = false, className, ...props }) => {
6453
+ const PinToSendScrollContainer = ({ children, scrollRef, contentRef, scrollToBottom, scrollToBottomButton, feather, inputContainerHeight, isResizing, nonAutoScrollEl, nonAutoScrollRefCallback, showScrollButton, className, ...props }) => {
6454
+ const spacerRef = useRef(null);
6455
+ usePinToSend({
6456
+ scrollRef,
6457
+ contentRef,
6458
+ spacerRef,
6459
+ topOffset: 16
6460
+ });
6461
+ const BoundFeather = renderSlot(feather, PinToSendSoftFeather, {});
6462
+ return /* @__PURE__ */ jsx(ScrollElementContext.Provider, {
6463
+ value: nonAutoScrollEl,
6464
+ children: /* @__PURE__ */ jsxs("div", {
6465
+ className: cn("cpk:h-full cpk:max-h-full cpk:flex cpk:flex-col cpk:min-h-0 cpk:relative", className),
6466
+ children: [
6467
+ /* @__PURE__ */ jsxs("div", {
6468
+ ref: nonAutoScrollRefCallback,
6469
+ className: "cpk:flex-1 cpk:min-h-0 cpk:overflow-y-auto cpk:overflow-x-hidden",
6470
+ ...props,
6471
+ children: [/* @__PURE__ */ jsx("div", {
6472
+ ref: contentRef,
6473
+ className: "cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-6",
6474
+ children
6475
+ }), /* @__PURE__ */ jsx("div", {
6476
+ ref: spacerRef,
6477
+ "data-pin-to-send-spacer": true,
6478
+ "aria-hidden": "true",
6479
+ style: {
6480
+ height: 0,
6481
+ flex: "0 0 auto"
6482
+ }
6483
+ })]
6484
+ }),
6485
+ BoundFeather,
6486
+ showScrollButton && !isResizing && /* @__PURE__ */ jsx("div", {
6487
+ className: "cpk:absolute cpk:inset-x-0 cpk:flex cpk:justify-center cpk:z-30 cpk:pointer-events-none",
6488
+ style: { bottom: `${inputContainerHeight + PIN_TO_SEND_FEATHER_HEIGHT + 16}px` },
6489
+ children: renderSlot(scrollToBottomButton, CopilotChatView.ScrollToBottomButton, { onClick: () => scrollToBottom() })
6490
+ })
6491
+ ]
6492
+ })
6493
+ });
6494
+ };
6495
+ _CopilotChatView.ScrollView = ({ children, autoScroll = "pin-to-bottom", scrollToBottomButton, feather, inputContainerHeight = 0, isResizing = false, className, ...props }) => {
6496
+ const mode = normalizeAutoScroll(autoScroll);
6373
6497
  const [hasMounted, setHasMounted] = useState(false);
6374
- const { scrollRef, contentRef, scrollToBottom } = useStickToBottom();
6498
+ const scrollRef = useRef(null);
6499
+ const contentRef = useRef(null);
6500
+ const scrollToBottom = useCallback(() => {
6501
+ const el = scrollRef.current;
6502
+ if (el) el.scrollTo({
6503
+ top: el.scrollHeight,
6504
+ behavior: "smooth"
6505
+ });
6506
+ }, []);
6375
6507
  const [showScrollButton, setShowScrollButton] = useState(false);
6376
6508
  const [nonAutoScrollEl, setNonAutoScrollEl] = useState(null);
6377
6509
  const nonAutoScrollRefCallback = useCallback((el) => {
@@ -6382,7 +6514,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
6382
6514
  setHasMounted(true);
6383
6515
  }, []);
6384
6516
  useEffect(() => {
6385
- if (autoScroll) return;
6517
+ if (mode === "pin-to-bottom") return;
6386
6518
  const scrollElement = scrollRef.current;
6387
6519
  if (!scrollElement) return;
6388
6520
  const checkScroll = () => {
@@ -6396,7 +6528,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
6396
6528
  scrollElement.removeEventListener("scroll", checkScroll);
6397
6529
  resizeObserver.disconnect();
6398
6530
  };
6399
- }, [scrollRef, autoScroll]);
6531
+ }, [scrollRef, mode]);
6400
6532
  if (!hasMounted) return /* @__PURE__ */ jsx("div", {
6401
6533
  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",
6402
6534
  children: /* @__PURE__ */ jsx("div", {
@@ -6404,7 +6536,7 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
6404
6536
  children
6405
6537
  })
6406
6538
  });
6407
- if (!autoScroll) {
6539
+ if (mode === "none") {
6408
6540
  const BoundFeather = renderSlot(feather, CopilotChatView.Feather, {});
6409
6541
  return /* @__PURE__ */ jsx(ScrollElementContext.Provider, {
6410
6542
  value: nonAutoScrollEl,
@@ -6428,6 +6560,21 @@ function CopilotChatView({ messageView, input, scrollView, suggestionView, welco
6428
6560
  })
6429
6561
  });
6430
6562
  }
6563
+ if (mode === "pin-to-send") return /* @__PURE__ */ jsx(PinToSendScrollContainer, {
6564
+ scrollRef,
6565
+ contentRef,
6566
+ scrollToBottom,
6567
+ scrollToBottomButton,
6568
+ feather,
6569
+ inputContainerHeight,
6570
+ isResizing,
6571
+ nonAutoScrollEl,
6572
+ nonAutoScrollRefCallback,
6573
+ showScrollButton,
6574
+ className,
6575
+ ...props,
6576
+ children
6577
+ });
6431
6578
  return /* @__PURE__ */ jsx(StickToBottom, {
6432
6579
  className: cn("cpk:flex-1 cpk:max-h-full cpk:flex cpk:flex-col cpk:min-h-0", className),
6433
6580
  resize: "smooth",
@@ -6620,7 +6767,9 @@ async function transcribeAudio(core, audioBlob, filename = "recording.webm") {
6620
6767
  function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen, attachments: attachmentsConfig, onError, throttleMs, ...props }) {
6621
6768
  const existingConfig = useCopilotChatConfiguration();
6622
6769
  const resolvedAgentId = agentId ?? existingConfig?.agentId ?? DEFAULT_AGENT_ID;
6623
- const resolvedThreadId = useMemo(() => threadId ?? existingConfig?.threadId ?? randomUUID(), [threadId, existingConfig?.threadId]);
6770
+ const providedThreadId = threadId ?? existingConfig?.threadId;
6771
+ const resolvedThreadId = useMemo(() => providedThreadId ?? randomUUID(), [providedThreadId]);
6772
+ const hasExplicitThreadId = !!threadId || !!existingConfig?.hasExplicitThreadId;
6624
6773
  const { agent } = useAgent({
6625
6774
  agentId: resolvedAgentId,
6626
6775
  threadId: resolvedThreadId,
@@ -6658,7 +6807,10 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
6658
6807
  const isTranscriptionEnabled = copilotkit.audioFileTranscriptionEnabled;
6659
6808
  const isMediaRecorderSupported = typeof window !== "undefined" && typeof MediaRecorder !== "undefined";
6660
6809
  const { messageView: providedMessageView, suggestionView: providedSuggestionView, onStop: providedStopHandler, ...restProps } = props;
6810
+ const [lastConnectedThreadId, setLastConnectedThreadId] = useState(null);
6811
+ const isConnecting = hasExplicitThreadId && lastConnectedThreadId !== resolvedThreadId;
6661
6812
  useEffect(() => {
6813
+ if (!hasExplicitThreadId) return;
6662
6814
  let detached = false;
6663
6815
  const connectAbortController = new AbortController();
6664
6816
  if (agent instanceof HttpAgent) agent.abortController = connectAbortController;
@@ -6668,6 +6820,10 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
6668
6820
  } catch (error) {
6669
6821
  if (detached) return;
6670
6822
  console.error("CopilotChat: connectAgent failed", error);
6823
+ } finally {
6824
+ if (!detached) (typeof requestAnimationFrame === "function" ? requestAnimationFrame : (cb) => setTimeout(cb, 16))(() => {
6825
+ if (!detached) setLastConnectedThreadId(resolvedThreadId);
6826
+ });
6671
6827
  }
6672
6828
  };
6673
6829
  connect(agent);
@@ -6679,7 +6835,8 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
6679
6835
  }, [
6680
6836
  resolvedThreadId,
6681
6837
  agent,
6682
- resolvedAgentId
6838
+ resolvedAgentId,
6839
+ hasExplicitThreadId
6683
6840
  ]);
6684
6841
  const onSubmitInput = useCallback(async (value) => {
6685
6842
  if (selectedAttachments.some((a) => a.status === "uploading")) {
@@ -6832,6 +6989,22 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
6832
6989
  const toolCallsKey = "toolCalls" in m && Array.isArray(m.toolCalls) ? m.toolCalls.map((tc) => `${tc.id}:${tc.function?.arguments?.length ?? 0}`).join(";") : "";
6833
6990
  return `${m.id}:${m.role}:${contentKey}:${toolCallsKey}`;
6834
6991
  }).join(",")]);
6992
+ const lastUserMessageId = useMemo(() => {
6993
+ for (let i = messages.length - 1; i >= 0; i--) if (messages[i].role === "user") return messages[i].id;
6994
+ return null;
6995
+ }, [messages]);
6996
+ const [sendNonce, setSendNonce] = useState(0);
6997
+ const prevLastUserMessageIdRef = useRef(lastUserMessageId);
6998
+ useEffect(() => {
6999
+ if (lastUserMessageId && lastUserMessageId !== prevLastUserMessageIdRef.current) {
7000
+ setSendNonce((n) => n + 1);
7001
+ prevLastUserMessageIdRef.current = lastUserMessageId;
7002
+ }
7003
+ }, [lastUserMessageId]);
7004
+ const lastUserMessageState = useMemo(() => ({
7005
+ id: lastUserMessageId,
7006
+ sendNonce
7007
+ }), [lastUserMessageId, sendNonce]);
6835
7008
  const RenderedChatView = renderSlot(chatView, CopilotChatView, {
6836
7009
  ...mergedProps,
6837
7010
  messages,
@@ -6850,11 +7023,14 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
6850
7023
  dragOver,
6851
7024
  onDragOver: handleDragOver,
6852
7025
  onDragLeave: handleDragLeave,
6853
- onDrop: handleDrop
7026
+ onDrop: handleDrop,
7027
+ isConnecting,
7028
+ hasExplicitThreadId
6854
7029
  });
6855
7030
  return /* @__PURE__ */ jsx(CopilotChatConfigurationProvider, {
6856
7031
  agentId: resolvedAgentId,
6857
7032
  threadId: resolvedThreadId,
7033
+ hasExplicitThreadId,
6858
7034
  labels,
6859
7035
  isModalDefaultOpen,
6860
7036
  children: /* @__PURE__ */ jsxs("div", {
@@ -6885,7 +7061,10 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
6885
7061
  },
6886
7062
  children: transcriptionError
6887
7063
  }),
6888
- RenderedChatView
7064
+ /* @__PURE__ */ jsx(LastUserMessageContext.Provider, {
7065
+ value: lastUserMessageState,
7066
+ children: RenderedChatView
7067
+ })
6889
7068
  ]
6890
7069
  })
6891
7070
  });
@@ -8611,12 +8790,19 @@ function useCoAgentStateRenders() {
8611
8790
  //#region src/context/threads-context.tsx
8612
8791
  const ThreadsContext = createContext(void 0);
8613
8792
  function ThreadsProvider({ children, threadId: explicitThreadId }) {
8614
- const [internalThreadId, setThreadId] = useState(() => randomUUID());
8793
+ const [internalThreadId, setInternalThreadId] = useState(() => randomUUID());
8794
+ const [internalIsExplicit, setInternalIsExplicit] = useState(false);
8615
8795
  const threadId = explicitThreadId ?? internalThreadId;
8796
+ const isThreadIdExplicit = explicitThreadId != null || internalIsExplicit;
8797
+ const setThreadId = useCallback((value) => {
8798
+ setInternalThreadId(value);
8799
+ setInternalIsExplicit(true);
8800
+ }, []);
8616
8801
  return /* @__PURE__ */ jsx(ThreadsContext.Provider, {
8617
8802
  value: {
8618
8803
  threadId,
8619
- setThreadId
8804
+ setThreadId,
8805
+ isThreadIdExplicit
8620
8806
  },
8621
8807
  children
8622
8808
  });
@@ -9318,7 +9504,7 @@ function CopilotKitInternal(cpkProps) {
9318
9504
  if (props.agent) setAgentSession({ agentName: props.agent });
9319
9505
  else setAgentSession(null);
9320
9506
  }, [props.agent]);
9321
- const { threadId, setThreadId: setInternalThreadId } = useThreads();
9507
+ const { threadId, setThreadId: setInternalThreadId, isThreadIdExplicit } = useThreads();
9322
9508
  const setThreadId = useCallback((value) => {
9323
9509
  if (props.threadId) throw new Error("Cannot call setThreadId() when threadId is provided via props.");
9324
9510
  setInternalThreadId(value);
@@ -9518,6 +9704,7 @@ function CopilotKitInternal(cpkProps) {
9518
9704
  return /* @__PURE__ */ jsx(CopilotChatConfigurationProvider, {
9519
9705
  agentId: props.agent ?? "default",
9520
9706
  threadId,
9707
+ hasExplicitThreadId: isThreadIdExplicit,
9521
9708
  children: /* @__PURE__ */ jsxs(CopilotContext.Provider, {
9522
9709
  value: copilotContextValue,
9523
9710
  children: [
@@ -9568,4 +9755,4 @@ function validateProps(props) {
9568
9755
 
9569
9756
  //#endregion
9570
9757
  export { CopilotKitProvider as $, CopilotChatSuggestionView as A, useConfigureSuggestions as B, CopilotChatToggleButton as C, CopilotChatView_default as D, CopilotChat as E, CopilotChatAssistantMessage_default as F, useRenderTool as G, useCapabilities as H, CopilotChatToolCallsView as I, useRenderActivityMessage as J, useComponent as K, useAttachments as L, CopilotChatReasoningMessage_default as M, CopilotChatUserMessage_default as N, CopilotChatAttachmentQueue as O, CopilotChatAttachmentRenderer as P, useRenderToolCall as Q, useThreads$1 as R, CopilotModalHeader as S, DefaultOpenIcon as T, useHumanInTheLoop as U, useSuggestions as V, useDefaultRenderTool as W, UseAgentUpdate as X, useRenderCustomMessages as Y, useAgent as Z, WildcardToolCallRender as _, ThreadsProvider as a, SandboxFunctionsContext as at, CopilotPopupView as b, CoAgentStateRendersProvider as c, MCPAppsActivityRenderer as ct, shouldShowDevConsole as d, CopilotChatInput_default as dt, useCopilotKit as et, useToast as f, AudioRecorderError as ft, useCopilotContext as g, CopilotContext as h, useCopilotChatConfiguration as ht, ThreadsContext as i, createA2UIMessageRenderer as it, CopilotChatSuggestionPill as j, CopilotChatMessageView as k, useCoAgentStateRenders as l, MCPAppsActivityType as lt, useCopilotMessagesContext as m, CopilotChatConfigurationProvider as mt, defaultCopilotContextCategories as n, useAgentContext as nt, useThreads as o, useSandboxFunctions as ot, CopilotMessagesContext as p, CopilotChatAudioRecorder as pt, useFrontendTool as q, CoAgentStateRenderBridge as r, defineToolCallRenderer as rt, CoAgentStateRendersContext as s, MCPAppsActivityContentSchema as st, CopilotKit as t, CopilotKitCoreReact as tt, useAsyncCallback as u, CopilotKitInspector as ut, CopilotPopup as v, DefaultCloseIcon as w, CopilotSidebarView as x, CopilotSidebar as y, useInterrupt as z };
9571
- //# sourceMappingURL=copilotkit-Cj2ZIxVr.mjs.map
9758
+ //# sourceMappingURL=copilotkit-PzJlPKcU.mjs.map