@copilotkit/react-core 1.55.0-next.8 → 1.55.0-next.9

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 (42) hide show
  1. package/CHANGELOG.md +18 -5
  2. package/dist/{copilotkit-B3Mb1yVE.cjs → copilotkit-BDNjFNmk.cjs} +156 -92
  3. package/dist/copilotkit-BDNjFNmk.cjs.map +1 -0
  4. package/dist/{copilotkit-Dy5w3qEV.d.mts → copilotkit-BqcyhQjT.d.mts} +3 -1
  5. package/dist/{copilotkit-Dy5w3qEV.d.mts.map → copilotkit-BqcyhQjT.d.mts.map} +1 -1
  6. package/dist/{copilotkit-DNYSFuz5.mjs → copilotkit-DeOzjPsb.mjs} +157 -93
  7. package/dist/copilotkit-DeOzjPsb.mjs.map +1 -0
  8. package/dist/{copilotkit-DBzgOMby.d.cts → copilotkit-l-IBF4Xp.d.cts} +3 -1
  9. package/dist/{copilotkit-DBzgOMby.d.cts.map → copilotkit-l-IBF4Xp.d.cts.map} +1 -1
  10. package/dist/index.cjs +9 -4
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +1 -1
  13. package/dist/index.d.mts +1 -1
  14. package/dist/index.mjs +9 -4
  15. package/dist/index.mjs.map +1 -1
  16. package/dist/index.umd.js +160 -94
  17. package/dist/index.umd.js.map +1 -1
  18. package/dist/v2/index.cjs +1 -1
  19. package/dist/v2/index.d.cts +1 -1
  20. package/dist/v2/index.d.mts +1 -1
  21. package/dist/v2/index.mjs +1 -1
  22. package/dist/v2/index.umd.js +160 -94
  23. package/dist/v2/index.umd.js.map +1 -1
  24. package/package.json +6 -6
  25. package/src/components/copilot-provider/__tests__/copilot-messages-key.test.tsx +92 -0
  26. package/src/components/copilot-provider/copilotkit.tsx +3 -3
  27. package/src/hooks/__tests__/use-copilot-chat-internal-connect.test.tsx +27 -16
  28. package/src/hooks/use-copilot-chat_internal.ts +15 -4
  29. package/src/v2/__tests__/utils/test-helpers.tsx +40 -7
  30. package/src/v2/components/chat/CopilotChat.tsx +4 -2
  31. package/src/v2/components/chat/CopilotChatMessageView.tsx +7 -2
  32. package/src/v2/components/chat/__tests__/CopilotChatToolRendering.e2e.test.tsx +5 -2
  33. package/src/v2/components/chat/__tests__/CopilotChatToolRerenders.e2e.test.tsx +5 -2
  34. package/src/v2/components/chat/__tests__/MCPAppsActivityRenderer.e2e.test.tsx +17 -1
  35. package/src/v2/hooks/__tests__/use-agent-context-timing.e2e.test.tsx +8 -0
  36. package/src/v2/hooks/__tests__/use-agent-thread-isolation.test.tsx +327 -0
  37. package/src/v2/hooks/__tests__/use-agent.e2e.test.tsx +13 -2
  38. package/src/v2/hooks/__tests__/use-frontend-tool.e2e.test.tsx +23 -4
  39. package/src/v2/hooks/use-agent.tsx +126 -6
  40. package/src/v2/hooks/use-render-custom-messages.tsx +6 -1
  41. package/dist/copilotkit-B3Mb1yVE.cjs.map +0 -1
  42. package/dist/copilotkit-DNYSFuz5.mjs.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,14 +1,27 @@
1
1
  # ui
2
2
 
3
+ ## 1.55.0-next.9
4
+
5
+ ### Patch Changes
6
+
7
+ - c341ed2: chore: kick CI to verify publish pipeline
8
+ - @copilotkit/a2ui-renderer@1.55.0-next.9
9
+ - @copilotkit/core@1.55.0-next.9
10
+ - @copilotkit/runtime-client-gql@1.55.0-next.9
11
+ - @copilotkit/shared@1.55.0-next.9
12
+ - @copilotkit/web-inspector@1.55.0-next.9
13
+
3
14
  ## 1.55.0-next.8
4
15
 
5
16
  ### Patch Changes
6
17
 
7
- - @copilotkit/a2ui-renderer@1.55.0-next.8
8
- - @copilotkit/core@1.55.0-next.8
9
- - @copilotkit/runtime-client-gql@1.55.0-next.8
10
- - @copilotkit/shared@1.55.0-next.8
11
- - @copilotkit/web-inspector@1.55.0-next.8
18
+ - 0f6a61c: fix: add React keys to CopilotMessages children to suppress "unique key prop" warning
19
+ - 53e5669: Fix multiple CopilotChat components with different threadIds sharing message state. The useAgent hook now creates per-thread agent clones when threadId is provided, ensuring each chat instance maintains isolated messages and state.
20
+ - @copilotkit/a2ui-renderer@1.55.0-next.8
21
+ - @copilotkit/core@1.55.0-next.8
22
+ - @copilotkit/runtime-client-gql@1.55.0-next.8
23
+ - @copilotkit/shared@1.55.0-next.8
24
+ - @copilotkit/web-inspector@1.55.0-next.8
12
25
 
13
26
  ## 1.55.0-next.7
14
27
 
@@ -2471,6 +2471,151 @@ function useRenderToolCall() {
2471
2471
  ]);
2472
2472
  }
2473
2473
 
2474
+ //#endregion
2475
+ //#region src/v2/hooks/use-agent.tsx
2476
+ let UseAgentUpdate = /* @__PURE__ */ function(UseAgentUpdate) {
2477
+ UseAgentUpdate["OnMessagesChanged"] = "OnMessagesChanged";
2478
+ UseAgentUpdate["OnStateChanged"] = "OnStateChanged";
2479
+ UseAgentUpdate["OnRunStatusChanged"] = "OnRunStatusChanged";
2480
+ return UseAgentUpdate;
2481
+ }({});
2482
+ const ALL_UPDATES = [
2483
+ UseAgentUpdate.OnMessagesChanged,
2484
+ UseAgentUpdate.OnStateChanged,
2485
+ UseAgentUpdate.OnRunStatusChanged
2486
+ ];
2487
+ /**
2488
+ * Clone a registry agent for per-thread isolation.
2489
+ * Copies agent configuration (transport, headers, etc.) but resets conversation
2490
+ * state (messages, threadId, state) so each thread starts fresh.
2491
+ */
2492
+ function cloneForThread(source, threadId, headers) {
2493
+ const clone = source.clone();
2494
+ if (clone === source) throw new Error(`useAgent: ${source.constructor.name}.clone() returned the same instance. clone() must return a new, independent object.`);
2495
+ clone.threadId = threadId;
2496
+ clone.setMessages([]);
2497
+ clone.setState({});
2498
+ if (clone instanceof _ag_ui_client.HttpAgent) clone.headers = { ...headers };
2499
+ return clone;
2500
+ }
2501
+ /**
2502
+ * Module-level WeakMap: registryAgent → (threadId → clone).
2503
+ * Shared across all useAgent() calls so that every component using the same
2504
+ * (agentId, threadId) pair receives the same agent instance. Using WeakMap
2505
+ * ensures the clone map is garbage-collected when the registry agent is
2506
+ * replaced (e.g. after reconnect or hot-reload).
2507
+ */
2508
+ const globalThreadCloneMap = /* @__PURE__ */ new WeakMap();
2509
+ /**
2510
+ * Look up an existing per-thread clone without creating one.
2511
+ * Returns undefined when no clone has been created yet for this pair.
2512
+ */
2513
+ function getThreadClone(registryAgent, threadId) {
2514
+ if (!registryAgent || !threadId) return void 0;
2515
+ return globalThreadCloneMap.get(registryAgent)?.get(threadId);
2516
+ }
2517
+ function getOrCreateThreadClone(existing, threadId, headers) {
2518
+ let byThread = globalThreadCloneMap.get(existing);
2519
+ if (!byThread) {
2520
+ byThread = /* @__PURE__ */ new Map();
2521
+ globalThreadCloneMap.set(existing, byThread);
2522
+ }
2523
+ const cached = byThread.get(threadId);
2524
+ if (cached) return cached;
2525
+ const clone = cloneForThread(existing, threadId, headers);
2526
+ byThread.set(threadId, clone);
2527
+ return clone;
2528
+ }
2529
+ function useAgent({ agentId, threadId, updates } = {}) {
2530
+ agentId ??= _copilotkit_shared.DEFAULT_AGENT_ID;
2531
+ const { copilotkit } = useCopilotKit();
2532
+ const chatConfig = useCopilotChatConfiguration();
2533
+ threadId ??= chatConfig?.threadId;
2534
+ const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
2535
+ const updateFlags = (0, react.useMemo)(() => updates ?? ALL_UPDATES, [JSON.stringify(updates)]);
2536
+ const provisionalAgentCache = (0, react.useRef)(/* @__PURE__ */ new Map());
2537
+ const agent = (0, react.useMemo)(() => {
2538
+ const cacheKey = threadId ? `${agentId}:${threadId}` : agentId;
2539
+ const existing = copilotkit.getAgent(agentId);
2540
+ if (existing) {
2541
+ provisionalAgentCache.current.delete(cacheKey);
2542
+ provisionalAgentCache.current.delete(agentId);
2543
+ if (!threadId) return existing;
2544
+ return getOrCreateThreadClone(existing, threadId, copilotkit.headers);
2545
+ }
2546
+ const isRuntimeConfigured = copilotkit.runtimeUrl !== void 0;
2547
+ const status = copilotkit.runtimeConnectionStatus;
2548
+ if (isRuntimeConfigured && (status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Disconnected || status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Connecting)) {
2549
+ const cached = provisionalAgentCache.current.get(cacheKey);
2550
+ if (cached) {
2551
+ cached.headers = { ...copilotkit.headers };
2552
+ return cached;
2553
+ }
2554
+ const provisional = new _copilotkit_core.ProxiedCopilotRuntimeAgent({
2555
+ runtimeUrl: copilotkit.runtimeUrl,
2556
+ agentId,
2557
+ transport: copilotkit.runtimeTransport,
2558
+ runtimeMode: "pending"
2559
+ });
2560
+ provisional.headers = { ...copilotkit.headers };
2561
+ if (threadId) provisional.threadId = threadId;
2562
+ provisionalAgentCache.current.set(cacheKey, provisional);
2563
+ return provisional;
2564
+ }
2565
+ if (isRuntimeConfigured && status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Error) {
2566
+ const cached = provisionalAgentCache.current.get(cacheKey);
2567
+ if (cached) {
2568
+ cached.headers = { ...copilotkit.headers };
2569
+ return cached;
2570
+ }
2571
+ const provisional = new _copilotkit_core.ProxiedCopilotRuntimeAgent({
2572
+ runtimeUrl: copilotkit.runtimeUrl,
2573
+ agentId,
2574
+ transport: copilotkit.runtimeTransport,
2575
+ runtimeMode: "pending"
2576
+ });
2577
+ provisional.headers = { ...copilotkit.headers };
2578
+ if (threadId) provisional.threadId = threadId;
2579
+ provisionalAgentCache.current.set(cacheKey, provisional);
2580
+ return provisional;
2581
+ }
2582
+ const knownAgents = Object.keys(copilotkit.agents ?? {});
2583
+ const runtimePart = isRuntimeConfigured ? `runtimeUrl=${copilotkit.runtimeUrl}` : "no runtimeUrl";
2584
+ throw new Error(`useAgent: Agent '${agentId}' not found after runtime sync (${runtimePart}). ` + (knownAgents.length ? `Known agents: [${knownAgents.join(", ")}]` : "No agents registered.") + " Verify your runtime /info and/or agents__unsafe_dev_only.");
2585
+ }, [
2586
+ agentId,
2587
+ threadId,
2588
+ copilotkit.agents,
2589
+ copilotkit.runtimeConnectionStatus,
2590
+ copilotkit.runtimeUrl,
2591
+ copilotkit.runtimeTransport,
2592
+ JSON.stringify(copilotkit.headers)
2593
+ ]);
2594
+ (0, react.useEffect)(() => {
2595
+ if (updateFlags.length === 0) return;
2596
+ const handlers = {};
2597
+ if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) handlers.onMessagesChanged = () => {
2598
+ forceUpdate();
2599
+ };
2600
+ if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) handlers.onStateChanged = forceUpdate;
2601
+ if (updateFlags.includes(UseAgentUpdate.OnRunStatusChanged)) {
2602
+ handlers.onRunInitialized = forceUpdate;
2603
+ handlers.onRunFinalized = forceUpdate;
2604
+ handlers.onRunFailed = forceUpdate;
2605
+ }
2606
+ const subscription = agent.subscribe(handlers);
2607
+ return () => subscription.unsubscribe();
2608
+ }, [
2609
+ agent,
2610
+ forceUpdate,
2611
+ JSON.stringify(updateFlags)
2612
+ ]);
2613
+ (0, react.useEffect)(() => {
2614
+ if (agent instanceof _ag_ui_client.HttpAgent) agent.headers = { ...copilotkit.headers };
2615
+ }, [agent, JSON.stringify(copilotkit.headers)]);
2616
+ return { agent };
2617
+ }
2618
+
2474
2619
  //#endregion
2475
2620
  //#region src/v2/hooks/use-render-custom-messages.tsx
2476
2621
  function useRenderCustomMessages() {
@@ -2488,7 +2633,8 @@ function useRenderCustomMessages() {
2488
2633
  const { message, position } = params;
2489
2634
  const resolvedRunId = copilotkit.getRunIdForMessage(agentId, threadId, message.id) ?? copilotkit.getRunIdsForThread(agentId, threadId).slice(-1)[0];
2490
2635
  const runId = resolvedRunId ?? `missing-run-id:${message.id}`;
2491
- const agent = copilotkit.getAgent(agentId);
2636
+ const registryAgent = copilotkit.getAgent(agentId);
2637
+ const agent = getThreadClone(registryAgent, threadId) ?? registryAgent;
2492
2638
  if (!agent) throw new Error("Agent not found");
2493
2639
  const messagesIdsInRun = resolvedRunId ? agent.messages.filter((msg) => copilotkit.getRunIdForMessage(agentId, threadId, msg.id) === resolvedRunId).map((msg) => msg.id) : [message.id];
2494
2640
  const rawMessageIndex = agent.messages.findIndex((msg) => msg.id === message.id);
@@ -2996,92 +3142,6 @@ function useHumanInTheLoop(tool, deps) {
2996
3142
  ]);
2997
3143
  }
2998
3144
 
2999
- //#endregion
3000
- //#region src/v2/hooks/use-agent.tsx
3001
- let UseAgentUpdate = /* @__PURE__ */ function(UseAgentUpdate) {
3002
- UseAgentUpdate["OnMessagesChanged"] = "OnMessagesChanged";
3003
- UseAgentUpdate["OnStateChanged"] = "OnStateChanged";
3004
- UseAgentUpdate["OnRunStatusChanged"] = "OnRunStatusChanged";
3005
- return UseAgentUpdate;
3006
- }({});
3007
- const ALL_UPDATES = [
3008
- UseAgentUpdate.OnMessagesChanged,
3009
- UseAgentUpdate.OnStateChanged,
3010
- UseAgentUpdate.OnRunStatusChanged
3011
- ];
3012
- function useAgent({ agentId, updates } = {}) {
3013
- agentId ??= _copilotkit_shared.DEFAULT_AGENT_ID;
3014
- const { copilotkit } = useCopilotKit();
3015
- const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
3016
- const updateFlags = (0, react.useMemo)(() => updates ?? ALL_UPDATES, [JSON.stringify(updates)]);
3017
- const provisionalAgentCache = (0, react.useRef)(/* @__PURE__ */ new Map());
3018
- const agent = (0, react.useMemo)(() => {
3019
- const existing = copilotkit.getAgent(agentId);
3020
- if (existing) {
3021
- provisionalAgentCache.current.delete(agentId);
3022
- return existing;
3023
- }
3024
- const isRuntimeConfigured = copilotkit.runtimeUrl !== void 0;
3025
- const status = copilotkit.runtimeConnectionStatus;
3026
- if (isRuntimeConfigured && (status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Disconnected || status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Connecting)) {
3027
- const cached = provisionalAgentCache.current.get(agentId);
3028
- if (cached) {
3029
- cached.headers = { ...copilotkit.headers };
3030
- return cached;
3031
- }
3032
- const provisional = new _copilotkit_core.ProxiedCopilotRuntimeAgent({
3033
- runtimeUrl: copilotkit.runtimeUrl,
3034
- agentId,
3035
- transport: copilotkit.runtimeTransport,
3036
- runtimeMode: "pending"
3037
- });
3038
- provisional.headers = { ...copilotkit.headers };
3039
- provisionalAgentCache.current.set(agentId, provisional);
3040
- return provisional;
3041
- }
3042
- if (isRuntimeConfigured && status === _copilotkit_core.CopilotKitCoreRuntimeConnectionStatus.Error) {
3043
- const provisional = new _copilotkit_core.ProxiedCopilotRuntimeAgent({
3044
- runtimeUrl: copilotkit.runtimeUrl,
3045
- agentId,
3046
- transport: copilotkit.runtimeTransport,
3047
- runtimeMode: "pending"
3048
- });
3049
- provisional.headers = { ...copilotkit.headers };
3050
- return provisional;
3051
- }
3052
- const knownAgents = Object.keys(copilotkit.agents ?? {});
3053
- const runtimePart = isRuntimeConfigured ? `runtimeUrl=${copilotkit.runtimeUrl}` : "no runtimeUrl";
3054
- throw new Error(`useAgent: Agent '${agentId}' not found after runtime sync (${runtimePart}). ` + (knownAgents.length ? `Known agents: [${knownAgents.join(", ")}]` : "No agents registered.") + " Verify your runtime /info and/or agents__unsafe_dev_only.");
3055
- }, [
3056
- agentId,
3057
- copilotkit.agents,
3058
- copilotkit.runtimeConnectionStatus,
3059
- copilotkit.runtimeUrl,
3060
- copilotkit.runtimeTransport,
3061
- JSON.stringify(copilotkit.headers)
3062
- ]);
3063
- (0, react.useEffect)(() => {
3064
- if (updateFlags.length === 0) return;
3065
- const handlers = {};
3066
- if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) handlers.onMessagesChanged = () => {
3067
- forceUpdate();
3068
- };
3069
- if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) handlers.onStateChanged = forceUpdate;
3070
- if (updateFlags.includes(UseAgentUpdate.OnRunStatusChanged)) {
3071
- handlers.onRunInitialized = forceUpdate;
3072
- handlers.onRunFinalized = forceUpdate;
3073
- handlers.onRunFailed = forceUpdate;
3074
- }
3075
- const subscription = agent.subscribe(handlers);
3076
- return () => subscription.unsubscribe();
3077
- }, [
3078
- agent,
3079
- forceUpdate,
3080
- JSON.stringify(updateFlags)
3081
- ]);
3082
- return { agent };
3083
- }
3084
-
3085
3145
  //#endregion
3086
3146
  //#region src/v2/hooks/use-agent-context.tsx
3087
3147
  function useAgentContext(context) {
@@ -4248,12 +4308,14 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
4248
4308
  const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
4249
4309
  (0, react.useEffect)(() => {
4250
4310
  if (!config?.agentId) return;
4251
- const agent = copilotkit.getAgent(config.agentId);
4311
+ const registryAgent = copilotkit.getAgent(config.agentId);
4312
+ const agent = getThreadClone(registryAgent, config.threadId) ?? registryAgent;
4252
4313
  if (!agent) return;
4253
4314
  const subscription = agent.subscribe({ onStateChanged: forceUpdate });
4254
4315
  return () => subscription.unsubscribe();
4255
4316
  }, [
4256
4317
  config?.agentId,
4318
+ config?.threadId,
4257
4319
  copilotkit,
4258
4320
  forceUpdate
4259
4321
  ]);
@@ -4804,7 +4866,10 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
4804
4866
  const existingConfig = useCopilotChatConfiguration();
4805
4867
  const resolvedAgentId = agentId ?? existingConfig?.agentId ?? _copilotkit_shared.DEFAULT_AGENT_ID;
4806
4868
  const resolvedThreadId = (0, react.useMemo)(() => threadId ?? existingConfig?.threadId ?? (0, _copilotkit_shared.randomUUID)(), [threadId, existingConfig?.threadId]);
4807
- const { agent } = useAgent({ agentId: resolvedAgentId });
4869
+ const { agent } = useAgent({
4870
+ agentId: resolvedAgentId,
4871
+ threadId: resolvedThreadId
4872
+ });
4808
4873
  const { copilotkit } = useCopilotKit();
4809
4874
  const { suggestions: autoSuggestions } = useSuggestions({ agentId: resolvedAgentId });
4810
4875
  const { checkFeature } = useLicenseContext();
@@ -4848,7 +4913,6 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
4848
4913
  console.error("CopilotChat: connectAgent failed", error);
4849
4914
  }
4850
4915
  };
4851
- agent.threadId = resolvedThreadId;
4852
4916
  connect(agent);
4853
4917
  return () => {
4854
4918
  detached = true;
@@ -7544,7 +7608,7 @@ function CopilotKitInternal(cpkProps) {
7544
7608
  children: [
7545
7609
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotListeners, {}),
7546
7610
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotKitErrorBridge, {}),
7547
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CoAgentStateRendersProvider, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessagesTapProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CopilotMessages, { children: [memoizedChildren, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RegisteredActionsRenderer, {})] }) }), bannerError && showDevConsole && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageBanner, {
7611
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CoAgentStateRendersProvider, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessagesTapProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CopilotMessages, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.default.Fragment, { children: memoizedChildren }, "children"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RegisteredActionsRenderer, {}, "actions")] }) }), bannerError && showDevConsole && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageBanner, {
7548
7612
  severity: bannerError.severity,
7549
7613
  message: bannerError.message,
7550
7614
  onClose: () => setBannerError(null),
@@ -7972,4 +8036,4 @@ Object.defineProperty(exports, 'useToast', {
7972
8036
  return useToast;
7973
8037
  }
7974
8038
  });
7975
- //# sourceMappingURL=copilotkit-B3Mb1yVE.cjs.map
8039
+ //# sourceMappingURL=copilotkit-BDNjFNmk.cjs.map