@copilotkit/react-core 1.57.0 → 1.57.1

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 (82) hide show
  1. package/dist/{copilotkit-DFaI4j2r.d.mts → copilotkit-BN4I_y1n.d.mts} +64 -8
  2. package/dist/copilotkit-BN4I_y1n.d.mts.map +1 -0
  3. package/dist/{copilotkit-DGbvw8n2.cjs → copilotkit-C3k13WZn.cjs} +572 -435
  4. package/dist/copilotkit-C3k13WZn.cjs.map +1 -0
  5. package/dist/{copilotkit-CPe2-340.mjs → copilotkit-DjxXMYHG.mjs} +571 -440
  6. package/dist/copilotkit-DjxXMYHG.mjs.map +1 -0
  7. package/dist/{copilotkit-Dg4r4Gi_.d.cts → copilotkit-sQWiKtxA.d.cts} +64 -8
  8. package/dist/copilotkit-sQWiKtxA.d.cts.map +1 -0
  9. package/dist/index.cjs +2 -5
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +1 -1
  12. package/dist/index.d.mts +1 -1
  13. package/dist/index.mjs +2 -5
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/index.umd.js +449 -502
  16. package/dist/index.umd.js.map +1 -1
  17. package/dist/v2/context.cjs +135 -0
  18. package/dist/v2/context.cjs.map +1 -0
  19. package/dist/v2/context.d.cts +148 -0
  20. package/dist/v2/context.d.cts.map +1 -0
  21. package/dist/v2/context.d.mts +148 -0
  22. package/dist/v2/context.d.mts.map +1 -0
  23. package/dist/v2/context.mjs +129 -0
  24. package/dist/v2/context.mjs.map +1 -0
  25. package/dist/v2/headless.cjs +1043 -0
  26. package/dist/v2/headless.cjs.map +1 -0
  27. package/dist/v2/headless.d.cts +605 -0
  28. package/dist/v2/headless.d.cts.map +1 -0
  29. package/dist/v2/headless.d.mts +512 -0
  30. package/dist/v2/headless.d.mts.map +1 -0
  31. package/dist/v2/headless.mjs +997 -0
  32. package/dist/v2/headless.mjs.map +1 -0
  33. package/dist/v2/index.cjs +2 -1
  34. package/dist/v2/index.css +1 -1
  35. package/dist/v2/index.d.cts +2 -2
  36. package/dist/v2/index.d.mts +2 -2
  37. package/dist/v2/index.mjs +2 -2
  38. package/dist/v2/index.umd.js +584 -441
  39. package/dist/v2/index.umd.js.map +1 -1
  40. package/package.json +14 -6
  41. package/src/hooks/__tests__/use-copilot-chat-internal-connect.test.tsx +5 -6
  42. package/src/hooks/use-copilot-chat_internal.ts +0 -1
  43. package/src/v2/components/chat/CopilotChat.tsx +2 -1
  44. package/src/v2/components/chat/CopilotChatMessageView.tsx +24 -9
  45. package/src/v2/components/chat/CopilotChatView.tsx +2 -2
  46. package/src/v2/components/chat/__tests__/CopilotChat.welcomeGate.test.tsx +1 -3
  47. package/src/v2/components/chat/__tests__/CopilotChatActivityRendering.e2e.test.tsx +29 -25
  48. package/src/v2/components/chat/__tests__/MCPAppsUiMessage.e2e.test.tsx +5 -60
  49. package/src/v2/components/index.ts +1 -0
  50. package/src/v2/components/intelligence-indicator/IntelligenceIndicator.tsx +286 -0
  51. package/src/v2/components/intelligence-indicator/__tests__/IntelligenceIndicator.e2e.test.tsx +464 -0
  52. package/src/v2/components/intelligence-indicator/index.ts +2 -0
  53. package/src/v2/context.ts +62 -0
  54. package/src/v2/headless.ts +42 -0
  55. package/src/v2/hooks/__tests__/standard-schema.test.tsx +2 -2
  56. package/src/v2/hooks/__tests__/use-agent-context.test.tsx +3 -3
  57. package/src/v2/hooks/__tests__/use-agent-stability.test.tsx +3 -3
  58. package/src/v2/hooks/__tests__/use-agent-throttle.test.tsx +85 -85
  59. package/src/v2/hooks/__tests__/use-interrupt.test.tsx +2 -2
  60. package/src/v2/hooks/__tests__/use-render-tool.test.tsx +2 -2
  61. package/src/v2/hooks/__tests__/use-threads.test.tsx +2 -2
  62. package/src/v2/hooks/__tests__/zod-regression.test.tsx +2 -2
  63. package/src/v2/hooks/use-agent-context.tsx +1 -1
  64. package/src/v2/hooks/use-agent.tsx +9 -118
  65. package/src/v2/hooks/use-configure-suggestions.tsx +1 -1
  66. package/src/v2/hooks/use-frontend-tool.tsx +2 -2
  67. package/src/v2/hooks/use-human-in-the-loop.tsx +1 -1
  68. package/src/v2/hooks/use-interrupt.tsx +1 -1
  69. package/src/v2/hooks/use-render-activity-message.tsx +3 -11
  70. package/src/v2/hooks/use-render-custom-messages.tsx +1 -6
  71. package/src/v2/hooks/use-render-tool-call.tsx +1 -1
  72. package/src/v2/hooks/use-render-tool.tsx +2 -2
  73. package/src/v2/hooks/use-suggestions.tsx +1 -1
  74. package/src/v2/hooks/use-threads.tsx +1 -1
  75. package/src/v2/providers/CopilotKitProvider.tsx +19 -59
  76. package/src/v2/styles/globals.css +118 -0
  77. package/tsdown.config.ts +75 -0
  78. package/dist/copilotkit-CPe2-340.mjs.map +0 -1
  79. package/dist/copilotkit-DFaI4j2r.d.mts.map +0 -1
  80. package/dist/copilotkit-DGbvw8n2.cjs.map +0 -1
  81. package/dist/copilotkit-Dg4r4Gi_.d.cts.map +0 -1
  82. package/src/v2/hooks/__tests__/use-agent-thread-isolation.test.tsx +0 -333
@@ -2,10 +2,10 @@ import React from "react";
2
2
  import { act, render, screen, waitFor } from "@testing-library/react";
3
3
  import { beforeEach, describe, expect, it, vi } from "vitest";
4
4
  import { useInterrupt } from "../use-interrupt";
5
- import { useCopilotKit } from "../../providers/CopilotKitProvider";
5
+ import { useCopilotKit } from "../../context";
6
6
  import { useAgent } from "../use-agent";
7
7
 
8
- vi.mock("../../providers/CopilotKitProvider", () => ({
8
+ vi.mock("../../context", () => ({
9
9
  useCopilotKit: vi.fn(),
10
10
  }));
11
11
 
@@ -3,10 +3,10 @@ import { render } from "@testing-library/react";
3
3
  import { describe, it, expect, beforeEach, vi } from "vitest";
4
4
  import { z } from "zod";
5
5
  import { useRenderTool, type RenderToolProps } from "../use-render-tool";
6
- import { useCopilotKit } from "../../providers/CopilotKitProvider";
6
+ import { useCopilotKit } from "../../context";
7
7
  import type { ReactToolCallRenderer } from "../../types/react-tool-call-renderer";
8
8
 
9
- vi.mock("../../providers/CopilotKitProvider", () => ({
9
+ vi.mock("../../context", () => ({
10
10
  useCopilotKit: vi.fn(),
11
11
  }));
12
12
 
@@ -1,13 +1,13 @@
1
1
  import React from "react";
2
2
  import { act, renderHook, waitFor } from "@testing-library/react";
3
3
  import { afterAll, beforeEach, describe, expect, it, vi } from "vitest";
4
- import { useCopilotKit } from "../../providers/CopilotKitProvider";
4
+ import { useCopilotKit } from "../../context";
5
5
  import {
6
6
  CopilotKitCoreRuntimeConnectionStatus,
7
7
  ɵMAX_SOCKET_RETRIES,
8
8
  } from "@copilotkit/core";
9
9
 
10
- vi.mock("../../providers/CopilotKitProvider", () => ({
10
+ vi.mock("../../context", () => ({
11
11
  useCopilotKit: vi.fn(),
12
12
  }));
13
13
 
@@ -15,11 +15,11 @@ import { z } from "zod";
15
15
  import { useRenderTool } from "../use-render-tool";
16
16
  import { useComponent } from "../use-component";
17
17
  import { defineToolCallRenderer } from "../../types/defineToolCallRenderer";
18
- import { useCopilotKit } from "../../providers/CopilotKitProvider";
18
+ import { useCopilotKit } from "../../context";
19
19
  import { useFrontendTool } from "../use-frontend-tool";
20
20
  import type { ReactToolCallRenderer } from "../../types/react-tool-call-renderer";
21
21
 
22
- vi.mock("../../providers/CopilotKitProvider", () => ({
22
+ vi.mock("../../context", () => ({
23
23
  useCopilotKit: vi.fn(),
24
24
  }));
25
25
 
@@ -1,4 +1,4 @@
1
- import { useCopilotKit } from "../providers/CopilotKitProvider";
1
+ import { useCopilotKit } from "../context";
2
2
  import { useLayoutEffect, useMemo } from "react";
3
3
 
4
4
  /**
@@ -1,5 +1,4 @@
1
- import { useCopilotKit } from "../providers/CopilotKitProvider";
2
- import { useCopilotChatConfiguration } from "../providers/CopilotChatConfigurationProvider";
1
+ import { useCopilotKit } from "../context";
3
2
  import { useMemo, useEffect, useReducer, useRef } from "react";
4
3
  import { DEFAULT_AGENT_ID } from "@copilotkit/shared";
5
4
  import { AbstractAgent, HttpAgent } from "@ag-ui/client";
@@ -23,7 +22,6 @@ const ALL_UPDATES: UseAgentUpdate[] = [
23
22
 
24
23
  export interface UseAgentProps {
25
24
  agentId?: string;
26
- threadId?: string;
27
25
  updates?: UseAgentUpdate[];
28
26
  /**
29
27
  * Throttle interval (in milliseconds) for re-renders triggered by
@@ -50,80 +48,7 @@ export interface UseAgentProps {
50
48
  throttleMs?: number;
51
49
  }
52
50
 
53
- /**
54
- * Clone a registry agent for per-thread isolation.
55
- * Copies agent configuration (transport, headers, etc.) but resets conversation
56
- * state (messages, threadId, state) so each thread starts fresh.
57
- */
58
- function cloneForThread(
59
- source: AbstractAgent,
60
- threadId: string,
61
- headers: Record<string, string>,
62
- ): AbstractAgent {
63
- const clone = source.clone();
64
- if (clone === source) {
65
- throw new Error(
66
- `useAgent: ${source.constructor.name}.clone() returned the same instance. ` +
67
- `clone() must return a new, independent object.`,
68
- );
69
- }
70
- clone.threadId = threadId;
71
- clone.setMessages([]);
72
- clone.setState({});
73
- if (clone instanceof HttpAgent) {
74
- clone.headers = { ...headers };
75
- }
76
- return clone;
77
- }
78
-
79
- /**
80
- * Module-level WeakMap: registryAgent → (threadId → clone).
81
- * Shared across all useAgent() calls so that every component using the same
82
- * (agentId, threadId) pair receives the same agent instance. Using WeakMap
83
- * ensures the clone map is garbage-collected when the registry agent is
84
- * replaced (e.g. after reconnect or hot-reload).
85
- */
86
- export const globalThreadCloneMap = new WeakMap<
87
- AbstractAgent,
88
- Map<string, AbstractAgent>
89
- >();
90
-
91
- /**
92
- * Look up an existing per-thread clone without creating one.
93
- * Returns undefined when no clone has been created yet for this pair.
94
- */
95
- export function getThreadClone(
96
- registryAgent: AbstractAgent | undefined | null,
97
- threadId: string | undefined | null,
98
- ): AbstractAgent | undefined {
99
- if (!registryAgent || !threadId) return undefined;
100
- return globalThreadCloneMap.get(registryAgent)?.get(threadId);
101
- }
102
-
103
- function getOrCreateThreadClone(
104
- existing: AbstractAgent,
105
- threadId: string,
106
- headers: Record<string, string>,
107
- ): AbstractAgent {
108
- let byThread = globalThreadCloneMap.get(existing);
109
- if (!byThread) {
110
- byThread = new Map();
111
- globalThreadCloneMap.set(existing, byThread);
112
- }
113
- const cached = byThread.get(threadId);
114
- if (cached) return cached;
115
-
116
- const clone = cloneForThread(existing, threadId, headers);
117
- byThread.set(threadId, clone);
118
- return clone;
119
- }
120
-
121
- export function useAgent({
122
- agentId,
123
- threadId,
124
- updates,
125
- throttleMs,
126
- }: UseAgentProps = {}) {
51
+ export function useAgent({ agentId, updates, throttleMs }: UseAgentProps = {}) {
127
52
  agentId ??= DEFAULT_AGENT_ID;
128
53
 
129
54
  const { copilotkit } = useCopilotKit();
@@ -131,12 +56,6 @@ export function useAgent({
131
56
  // subscribeToAgentWithOptions reads it from the core instance, but React needs the dep
132
57
  // to know when to re-subscribe.
133
58
  const providerThrottleMs = copilotkit.defaultThrottleMs;
134
- // Fall back to the enclosing CopilotChatConfigurationProvider's threadId so
135
- // that useAgent() called without explicit threadId (e.g. inside a custom
136
- // message renderer) automatically uses the same per-thread clone as the
137
- // CopilotChat component it lives within.
138
- const chatConfig = useCopilotChatConfiguration();
139
- threadId ??= chatConfig?.threadId;
140
59
 
141
60
  const [, forceUpdate] = useReducer((x) => x + 1, 0);
142
61
 
@@ -153,29 +72,11 @@ export function useAgent({
153
72
  );
154
73
 
155
74
  const agent: AbstractAgent = useMemo(() => {
156
- // Use a composite key when threadId is provided so that different threads
157
- // for the same agent get independent instances.
158
- const cacheKey = threadId ? `${agentId}:${threadId}` : agentId;
159
-
160
75
  const existing = copilotkit.getAgent(agentId);
161
76
  if (existing) {
162
- // Real agent found — clear any cached provisionals for this key and the
163
- // bare agentId key (handles the case where a provisional was created
164
- // before threadId was available, then the component re-renders with one).
165
- provisionalAgentCache.current.delete(cacheKey);
77
+ // Real agent found — clear any cached provisional for this ID
166
78
  provisionalAgentCache.current.delete(agentId);
167
-
168
- if (!threadId) {
169
- // No threadId — return the shared registry agent (original behavior)
170
- return existing;
171
- }
172
-
173
- // threadId provided — return the shared per-thread clone.
174
- // The global WeakMap ensures all components using the same
175
- // (registryAgent, threadId) pair receive the same instance, so state
176
- // mutations (addMessage, setState) are visible everywhere. The WeakMap
177
- // entry is GC-collected automatically when the registry agent is replaced.
178
- return getOrCreateThreadClone(existing, threadId, copilotkit.headers);
79
+ return existing;
179
80
  }
180
81
 
181
82
  const isRuntimeConfigured = copilotkit.runtimeUrl !== undefined;
@@ -188,7 +89,7 @@ export function useAgent({
188
89
  status === CopilotKitCoreRuntimeConnectionStatus.Connecting)
189
90
  ) {
190
91
  // Return cached provisional if available (keeps reference stable)
191
- const cached = provisionalAgentCache.current.get(cacheKey);
92
+ const cached = provisionalAgentCache.current.get(agentId);
192
93
  if (cached) {
193
94
  // Update headers on the cached agent in case they changed
194
95
  cached.headers = { ...copilotkit.headers };
@@ -203,10 +104,7 @@ export function useAgent({
203
104
  });
204
105
  // Apply current headers so runs/connects inherit them
205
106
  provisional.headers = { ...copilotkit.headers };
206
- if (threadId) {
207
- provisional.threadId = threadId;
208
- }
209
- provisionalAgentCache.current.set(cacheKey, provisional);
107
+ provisionalAgentCache.current.set(agentId, provisional);
210
108
  return provisional;
211
109
  }
212
110
 
@@ -219,10 +117,7 @@ export function useAgent({
219
117
  isRuntimeConfigured &&
220
118
  status === CopilotKitCoreRuntimeConnectionStatus.Error
221
119
  ) {
222
- // Cache the provisional so that dep changes while in Error state (e.g.
223
- // headers update) return the same agent reference, matching the
224
- // Disconnected/Connecting path and preventing spurious re-subscriptions.
225
- const cached = provisionalAgentCache.current.get(cacheKey);
120
+ const cached = provisionalAgentCache.current.get(agentId);
226
121
  if (cached) {
227
122
  cached.headers = { ...copilotkit.headers };
228
123
  return cached;
@@ -234,10 +129,7 @@ export function useAgent({
234
129
  runtimeMode: "pending",
235
130
  });
236
131
  provisional.headers = { ...copilotkit.headers };
237
- if (threadId) {
238
- provisional.threadId = threadId;
239
- }
240
- provisionalAgentCache.current.set(cacheKey, provisional);
132
+ provisionalAgentCache.current.set(agentId, provisional);
241
133
  return provisional;
242
134
  }
243
135
 
@@ -256,7 +148,6 @@ export function useAgent({
256
148
  // eslint-disable-next-line react-hooks/exhaustive-deps
257
149
  }, [
258
150
  agentId,
259
- threadId,
260
151
  copilotkit.agents,
261
152
  copilotkit.runtimeConnectionStatus,
262
153
  copilotkit.runtimeUrl,
@@ -290,7 +181,7 @@ export function useAgent({
290
181
  };
291
182
 
292
183
  if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) {
293
- handlers.onMessagesChanged = forceUpdate;
184
+ handlers.onMessagesChanged = batchedForceUpdate;
294
185
  }
295
186
 
296
187
  if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) {
@@ -1,5 +1,5 @@
1
1
  import { useCallback, useEffect, useMemo, useRef } from "react";
2
- import { useCopilotKit } from "../providers/CopilotKitProvider";
2
+ import { useCopilotKit } from "../context";
3
3
  import { useCopilotChatConfiguration } from "../providers/CopilotChatConfigurationProvider";
4
4
  import { DEFAULT_AGENT_ID } from "@copilotkit/shared";
5
5
  import type {
@@ -1,5 +1,5 @@
1
1
  import { useEffect } from "react";
2
- import { useCopilotKit } from "../providers/CopilotKitProvider";
2
+ import { useCopilotKit } from "../context";
3
3
  import type { ReactFrontendTool } from "../types/frontend-tool";
4
4
 
5
5
  const EMPTY_DEPS: ReadonlyArray<unknown> = [];
@@ -42,5 +42,5 @@ export function useFrontendTool<
42
42
  // Depend on stable keys by default and allow callers to opt into
43
43
  // additional dependencies for dynamic tool configuration.
44
44
  // tool.available is included so toggling availability re-registers the tool.
45
- }, [tool.name, tool.available, copilotkit, extraDeps.length, ...extraDeps]);
45
+ }, [tool.name, tool.available, copilotkit, JSON.stringify(extraDeps)]);
46
46
  }
@@ -1,4 +1,4 @@
1
- import { useCopilotKit } from "../providers/CopilotKitProvider";
1
+ import { useCopilotKit } from "../context";
2
2
  import type { ReactFrontendTool } from "../types/frontend-tool";
3
3
  import type { ReactHumanInTheLoop } from "../types/human-in-the-loop";
4
4
  import type { ReactToolCallRenderer } from "../types/react-tool-call-renderer";
@@ -5,7 +5,7 @@ import React, {
5
5
  useMemo,
6
6
  useRef,
7
7
  } from "react";
8
- import { useCopilotKit } from "../providers/CopilotKitProvider";
8
+ import { useCopilotKit } from "../context";
9
9
  import { useAgent } from "./use-agent";
10
10
  import type {
11
11
  InterruptEvent,
@@ -3,12 +3,10 @@ import { DEFAULT_AGENT_ID } from "@copilotkit/shared";
3
3
  import { useCopilotKit, useCopilotChatConfiguration } from "../providers";
4
4
  import { useCallback, useMemo } from "react";
5
5
  import { ReactActivityMessageRenderer } from "../types";
6
- import { getThreadClone } from "./use-agent";
7
6
 
8
7
  export function useRenderActivityMessage() {
9
8
  const { copilotkit } = useCopilotKit();
10
- const config = useCopilotChatConfiguration();
11
- const agentId = config?.agentId ?? DEFAULT_AGENT_ID;
9
+ const agentId = useCopilotChatConfiguration()?.agentId ?? DEFAULT_AGENT_ID;
12
10
 
13
11
  const renderers = copilotkit.renderActivityMessages;
14
12
 
@@ -52,13 +50,7 @@ export function useRenderActivityMessage() {
52
50
  }
53
51
 
54
52
  const Component = renderer.render;
55
- // Prefer the per-thread clone so that handleAction in ReactSurfaceHost
56
- // calls runAgent on the same agent instance that CopilotChat renders from.
57
- // Without this, button clicks accumulate messages on the registry agent
58
- // while CopilotChat displays from the clone — responses appear to vanish.
59
- const registryAgent = copilotkit.getAgent(agentId);
60
- const agent =
61
- getThreadClone(registryAgent, config?.threadId) ?? registryAgent;
53
+ const agent = copilotkit.getAgent(agentId);
62
54
 
63
55
  return (
64
56
  <Component
@@ -70,7 +62,7 @@ export function useRenderActivityMessage() {
70
62
  />
71
63
  );
72
64
  },
73
- [agentId, config?.threadId, copilotkit, findRenderer],
65
+ [agentId, copilotkit, findRenderer],
74
66
  );
75
67
 
76
68
  return useMemo(
@@ -1,5 +1,4 @@
1
1
  import { useCopilotChatConfiguration, useCopilotKit } from "../providers";
2
- import { getThreadClone } from "./use-agent";
3
2
  import { ReactCustomMessageRendererPosition } from "../types/react-custom-message-renderer";
4
3
  import { Message } from "@ag-ui/core";
5
4
 
@@ -39,11 +38,7 @@ export function useRenderCustomMessages() {
39
38
  copilotkit.getRunIdForMessage(agentId, threadId, message.id) ??
40
39
  copilotkit.getRunIdsForThread(agentId, threadId).slice(-1)[0];
41
40
  const runId = resolvedRunId ?? `missing-run-id:${message.id}`;
42
- // Prefer the per-thread clone so that agent.messages reflects the actual
43
- // conversation state (messages live on the clone, not the registry agent).
44
- // Fall back to the registry agent when no clone exists (no threadId).
45
- const registryAgent = copilotkit.getAgent(agentId);
46
- const agent = getThreadClone(registryAgent, threadId) ?? registryAgent;
41
+ const agent = copilotkit.getAgent(agentId);
47
42
  if (!agent) {
48
43
  return null;
49
44
  }
@@ -1,7 +1,7 @@
1
1
  import React, { useCallback, useMemo, useSyncExternalStore } from "react";
2
2
  import { ToolCall, ToolMessage } from "@ag-ui/core";
3
3
  import { ToolCallStatus } from "@copilotkit/core";
4
- import { useCopilotKit } from "../providers/CopilotKitProvider";
4
+ import { useCopilotKit } from "../context";
5
5
  import { useCopilotChatConfiguration } from "../providers/CopilotChatConfigurationProvider";
6
6
  import { DEFAULT_AGENT_ID } from "@copilotkit/shared";
7
7
  import { partialJSONParse } from "@copilotkit/shared";
@@ -1,6 +1,6 @@
1
1
  import { useEffect } from "react";
2
2
  import type { StandardSchemaV1, InferSchemaOutput } from "@copilotkit/shared";
3
- import { useCopilotKit } from "../providers/CopilotKitProvider";
3
+ import { useCopilotKit } from "../context";
4
4
  import { defineToolCallRenderer } from "../types/defineToolCallRenderer";
5
5
 
6
6
  const EMPTY_DEPS: ReadonlyArray<unknown> = [];
@@ -180,5 +180,5 @@ export function useRenderTool<S extends StandardSchemaV1>(
180
180
  copilotkit.addHookRenderToolCall(renderer);
181
181
 
182
182
  // No cleanup removal — keeps renderer for chat history, same as useFrontendTool
183
- }, [config.name, copilotkit, extraDeps.length, ...extraDeps]);
183
+ }, [config.name, copilotkit, JSON.stringify(extraDeps)]);
184
184
  }
@@ -1,6 +1,6 @@
1
1
  import { useCallback, useEffect, useMemo, useState } from "react";
2
2
  import { Suggestion } from "@copilotkit/core";
3
- import { useCopilotKit } from "../providers/CopilotKitProvider";
3
+ import { useCopilotKit } from "../context";
4
4
  import { useCopilotChatConfiguration } from "../providers/CopilotChatConfigurationProvider";
5
5
  import { DEFAULT_AGENT_ID } from "@copilotkit/shared";
6
6
 
@@ -1,4 +1,4 @@
1
- import { useCopilotKit } from "../providers/CopilotKitProvider";
1
+ import { useCopilotKit } from "../context";
2
2
  import {
3
3
  CopilotKitCoreRuntimeConnectionStatus,
4
4
  ɵcreateThreadStore,
@@ -4,8 +4,6 @@ import type { AbstractAgent } from "@ag-ui/client";
4
4
  import type { FrontendTool } from "@copilotkit/core";
5
5
  import type React from "react";
6
6
  import {
7
- createContext,
8
- useContext,
9
7
  type ReactNode,
10
8
  useMemo,
11
9
  useEffect,
@@ -14,6 +12,14 @@ import {
14
12
  useRef,
15
13
  useState,
16
14
  } from "react";
15
+ // Context extracted to ../context.ts for cross-platform reuse (React Native)
16
+ import {
17
+ CopilotKitContext,
18
+ type CopilotKitContextValue,
19
+ LicenseContext,
20
+ } from "../context";
21
+ export type { CopilotKitContextValue } from "../context";
22
+ export { CopilotKitContext, useLicenseContext } from "../context";
17
23
  import { z } from "zod";
18
24
  import { CopilotKitInspector } from "../components/CopilotKitInspector";
19
25
  import type { Anchor } from "@copilotkit/web-inspector";
@@ -81,34 +87,6 @@ const GENERATE_SANDBOXED_UI_DESCRIPTION =
81
87
  "3. html (streams in live — the user watches the UI build as HTML is generated)\n" +
82
88
  "4. jsFunctions (reusable helper functions)\n" +
83
89
  "5. jsExpressions (applied one-by-one — the user sees each expression take effect)";
84
-
85
- // Define the context value interface - idiomatic React naming
86
- export interface CopilotKitContextValue {
87
- copilotkit: CopilotKitCoreReact;
88
- /**
89
- * Set of tool call IDs currently being executed.
90
- * This is tracked at the provider level to ensure tool execution events
91
- * are captured even before child components mount.
92
- */
93
- executingToolCallIds: ReadonlySet<string>;
94
- }
95
-
96
- // Empty set for default context value
97
- const EMPTY_SET: ReadonlySet<string> = new Set();
98
-
99
- // Create the CopilotKit context
100
- const CopilotKitContext = createContext<CopilotKitContextValue>({
101
- copilotkit: null!,
102
- executingToolCallIds: EMPTY_SET,
103
- });
104
-
105
- const LicenseContext = createContext<LicenseContextValue>(
106
- createLicenseContextValue(null),
107
- );
108
-
109
- export const useLicenseContext = (): LicenseContextValue =>
110
- useContext(LicenseContext);
111
-
112
90
  // Provider props interface
113
91
  export interface CopilotKitProviderProps {
114
92
  children: ReactNode;
@@ -642,15 +620,17 @@ export const CopilotKitProvider: React.FC<CopilotKitProviderProps> = ({
642
620
  }, [onError]);
643
621
 
644
622
  useEffect(() => {
645
- if (!onErrorRef.current) return;
646
-
647
623
  const subscription = copilotkit.subscribe({
648
624
  onError: (event) => {
649
- onErrorRef.current?.({
650
- error: event.error,
651
- code: event.code,
652
- context: event.context,
653
- });
625
+ if (onErrorRef.current) {
626
+ onErrorRef.current(event);
627
+ } else {
628
+ console.error(
629
+ `[CopilotKit] Error (${event.code}):`,
630
+ event.error,
631
+ event.context ?? {},
632
+ );
633
+ }
654
634
  },
655
635
  });
656
636
 
@@ -816,25 +796,5 @@ export const CopilotKitProvider: React.FC<CopilotKitProviderProps> = ({
816
796
  );
817
797
  };
818
798
 
819
- // Hook to use the CopilotKit instance - returns the full context value
820
- export const useCopilotKit = (): CopilotKitContextValue => {
821
- const context = useContext(CopilotKitContext);
822
- const [, forceUpdate] = useReducer((x) => x + 1, 0);
823
-
824
- if (!context) {
825
- throw new Error("useCopilotKit must be used within CopilotKitProvider");
826
- }
827
- useEffect(() => {
828
- const subscription = context.copilotkit.subscribe({
829
- onRuntimeConnectionStatusChanged: () => {
830
- forceUpdate();
831
- },
832
- });
833
- return () => {
834
- subscription.unsubscribe();
835
- };
836
- // eslint-disable-next-line react-hooks/exhaustive-deps
837
- }, []);
838
-
839
- return context;
840
- };
799
+ // Re-export useCopilotKit from context for backward compatibility
800
+ export { useCopilotKit } from "../context";
@@ -229,3 +229,121 @@
229
229
  background-repeat: no-repeat;
230
230
  background-size: 100% 100%;
231
231
  }
232
+ /*
233
+ * IntelligenceIndicator pill — visual styles ported from
234
+ * CopilotKit/Intelligence #155.
235
+ *
236
+ * Violet/indigo glassmorphism — text #5B21B6, icon #7C3AED, border
237
+ * #9599E0, gradient stop #EEE6FE, soft shadow #5E64AD.
238
+ */
239
+ .cpk-intelligence-pill {
240
+ display: inline-flex;
241
+ /* Opt out of the parent flex-column's default `align-items: stretch`
242
+ (the auto-mount lives inside `CopilotChatMessageView`'s
243
+ `cpk:flex cpk:flex-col` container). Without this the pill would
244
+ stretch to the full chat width even though `inline-flex` is
245
+ supposed to shrink to its content. */
246
+ align-self: flex-start;
247
+ align-items: center;
248
+ gap: 0.55rem;
249
+ margin: 0.4rem 0;
250
+ padding: 0.4rem 0.85rem;
251
+ border: 1px solid rgb(149 153 224 / 0.32);
252
+ border-radius: 999px;
253
+ background: linear-gradient(
254
+ 135deg,
255
+ rgb(255 255 255 / 0.55) 0%,
256
+ rgb(238 230 254 / 0.55) 100%
257
+ );
258
+ -webkit-backdrop-filter: blur(14px) saturate(160%);
259
+ backdrop-filter: blur(14px) saturate(160%);
260
+ color: rgb(91 33 182 / 0.92);
261
+ font-size: 0.78rem;
262
+ font-weight: 500;
263
+ letter-spacing: 0.01em;
264
+ white-space: nowrap;
265
+ box-shadow:
266
+ 0 1px 2px rgb(94 100 173 / 0.06),
267
+ 0 8px 24px rgb(149 153 224 / 0.08),
268
+ inset 0 1px 0 rgb(255 255 255 / 0.6);
269
+ opacity: 0;
270
+ transform: translateY(2px);
271
+ animation: cpk-intelligence-pill-fade-in 280ms ease-out forwards;
272
+ }
273
+
274
+ .cpk-intelligence-pill--fading {
275
+ /* Switching the `animation` property cancels the in-flight fade-in
276
+ and replaces the held final-value (opacity:1 from `forwards`) with
277
+ this fade-out keyframe set. Plain `opacity: 0` declarations would
278
+ otherwise lose to the animation's held value. */
279
+ animation: cpk-intelligence-pill-fade-out 480ms ease-out forwards;
280
+ pointer-events: none;
281
+ }
282
+
283
+ .cpk-intelligence-pill__icon {
284
+ display: block;
285
+ flex-shrink: 0;
286
+ color: rgb(124 58 237);
287
+ }
288
+
289
+ .cpk-intelligence-pill__ring {
290
+ stroke: currentColor;
291
+ stroke-dasharray: 40 16.55;
292
+ stroke-dashoffset: 0;
293
+ transform-origin: 12px 12px;
294
+ animation: cpk-intelligence-pill-spin 0.9s linear infinite;
295
+ transition: stroke-dasharray 320ms ease-out;
296
+ }
297
+
298
+ .cpk-intelligence-pill__ring--done {
299
+ stroke-dasharray: 56.55 0;
300
+ animation: none;
301
+ }
302
+
303
+ .cpk-intelligence-pill__check {
304
+ stroke: currentColor;
305
+ stroke-dasharray: 12;
306
+ stroke-dashoffset: 12;
307
+ /* Opacity zero hides the rounded `stroke-linecap` end-cap that would
308
+ otherwise leak through as a stationary dot at path end (16, 9.5)
309
+ during spinner phase. The `dasharray + dashoffset` zeroes the
310
+ visible stroke length, but the cap is drawn at the dash boundary
311
+ regardless, so we need opacity to fully hide it. */
312
+ opacity: 0;
313
+ transition:
314
+ stroke-dashoffset 320ms ease-out 200ms,
315
+ opacity 200ms ease-out 200ms;
316
+ }
317
+
318
+ .cpk-intelligence-pill__check--shown {
319
+ stroke-dashoffset: 0;
320
+ opacity: 1;
321
+ }
322
+
323
+ @keyframes cpk-intelligence-pill-fade-in {
324
+ from {
325
+ opacity: 0;
326
+ transform: translateY(2px);
327
+ }
328
+ to {
329
+ opacity: 1;
330
+ transform: translateY(0);
331
+ }
332
+ }
333
+
334
+ @keyframes cpk-intelligence-pill-fade-out {
335
+ from {
336
+ opacity: 1;
337
+ transform: translateY(0);
338
+ }
339
+ to {
340
+ opacity: 0;
341
+ transform: translateY(-2px);
342
+ }
343
+ }
344
+
345
+ @keyframes cpk-intelligence-pill-spin {
346
+ to {
347
+ transform: rotate(360deg);
348
+ }
349
+ }