Skip to content

Commit b54eb3a

Browse files
committed
fix(react-core): stabilize provider defaults
1 parent bcda2a1 commit b54eb3a

2 files changed

Lines changed: 50 additions & 21 deletions

File tree

packages/react-core/src/v2/providers/CopilotKitProvider.tsx

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,25 @@ import type { AbstractAgent } from "@ag-ui/client";
44
import type { FrontendTool } from "@copilotkit/core";
55
import type React from "react";
66
import {
7-
type ReactNode,
87
useMemo,
98
useEffect,
109
useLayoutEffect,
1110
useReducer,
1211
useRef,
1312
useState,
1413
} from "react";
14+
import type { ReactNode } from "react";
1515
// Context extracted to ../context.ts for cross-platform reuse (React Native)
16-
import {
17-
CopilotKitContext,
18-
type CopilotKitContextValue,
19-
LicenseContext,
20-
} from "../context";
16+
import { CopilotKitContext, LicenseContext } from "../context";
17+
import type { CopilotKitContextValue } from "../context";
2118
export type { CopilotKitContextValue } from "../context";
2219
export { CopilotKitContext, useLicenseContext } from "../context";
2320
import { z } from "zod";
2421
import { CopilotKitInspector } from "../components/CopilotKitInspector";
2522
import type { Anchor } from "@copilotkit/web-inspector";
2623
import { LicenseWarningBanner } from "../components/license-warning-banner";
27-
import {
28-
createLicenseContextValue,
29-
type LicenseContextValue,
30-
type DebugConfig,
31-
} from "@copilotkit/shared";
24+
import { createLicenseContextValue } from "@copilotkit/shared";
25+
import type { LicenseContextValue, DebugConfig } from "@copilotkit/shared";
3226
import type { CopilotKitCoreErrorCode } from "@copilotkit/core";
3327
import {
3428
MCPAppsActivityContentSchema,
@@ -62,6 +56,9 @@ import { zodToJsonSchema } from "zod-to-json-schema";
6256

6357
const HEADER_NAME = "X-CopilotCloud-Public-Api-Key";
6458
const COPILOT_CLOUD_CHAT_URL = "https://api.cloud.copilotkit.ai/copilotkit/v1";
59+
const EMPTY_HEADERS: Record<string, string> = {};
60+
const EMPTY_PROPERTIES: Record<string, unknown> = {};
61+
const EMPTY_AGENTS: Record<string, AbstractAgent> = {};
6562

6663
const DEFAULT_DESIGN_SKILL = `When generating UI with generateSandboxedUi, follow these design principles inspired by shadcn/ui:
6764
@@ -246,14 +243,14 @@ function useStableArrayProp<T>(
246243
export const CopilotKitProvider: React.FC<CopilotKitProviderProps> = ({
247244
children,
248245
runtimeUrl,
249-
headers: headersProp = {},
246+
headers: headersProp = EMPTY_HEADERS,
250247
credentials,
251248
publicApiKey,
252249
publicLicenseKey,
253250
licenseToken,
254-
properties = {},
255-
agents__unsafe_dev_only: agents = {},
256-
selfManagedAgents = {},
251+
properties = EMPTY_PROPERTIES,
252+
agents__unsafe_dev_only: agents = EMPTY_AGENTS,
253+
selfManagedAgents = EMPTY_AGENTS,
257254
renderToolCalls,
258255
renderActivityMessages,
259256
renderCustomMessages,

packages/react-core/src/v2/providers/__tests__/CopilotKitProvider.stability.test.tsx

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,9 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
44
import { z } from "zod";
55
import type { ReactFrontendTool } from "../../types/frontend-tool";
66
import type { ReactToolCallRenderer } from "../../types";
7-
import {
8-
CopilotKitProvider,
9-
useCopilotKit,
10-
type CopilotKitContextValue,
11-
} from "../CopilotKitProvider";
12-
import { CopilotKitCoreReact } from "../../lib/react-core";
7+
import { CopilotKitProvider, useCopilotKit } from "../CopilotKitProvider";
8+
import type { CopilotKitContextValue } from "../CopilotKitProvider";
9+
import type { CopilotKitCoreReact } from "../../lib/react-core";
1310
import { useFrontendTool } from "../../hooks/use-frontend-tool";
1411

1512
// Mock console methods to suppress expected warnings
@@ -149,6 +146,41 @@ describe("CopilotKitProvider stability", () => {
149146
});
150147

151148
describe("setter calls on prop changes", () => {
149+
it("does not re-sync an empty local agent registry on unchanged rerenders", () => {
150+
const setAgentsCalls: unknown[] = [];
151+
let spyAttached = false;
152+
153+
function SpyAttacher() {
154+
const { copilotkit } = useCopilotKit();
155+
if (!spyAttached) {
156+
const original =
157+
copilotkit.setAgents__unsafe_dev_only.bind(copilotkit);
158+
copilotkit.setAgents__unsafe_dev_only = (agents) => {
159+
setAgentsCalls.push(agents);
160+
return original(agents);
161+
};
162+
spyAttached = true;
163+
}
164+
return null;
165+
}
166+
167+
const { rerender } = render(
168+
<CopilotKitProvider runtimeUrl="http://localhost:3000/api">
169+
<SpyAttacher />
170+
</CopilotKitProvider>,
171+
);
172+
173+
setAgentsCalls.length = 0;
174+
175+
rerender(
176+
<CopilotKitProvider runtimeUrl="http://localhost:3000/api">
177+
<SpyAttacher />
178+
</CopilotKitProvider>,
179+
);
180+
181+
expect(setAgentsCalls).toHaveLength(0);
182+
});
183+
152184
it("calls setTools when frontendTools change instead of recreating instance", () => {
153185
const setToolsSpy = vi.fn();
154186
let spyAttached = false;

0 commit comments

Comments
 (0)