Skip to content

Commit c3c3096

Browse files
mxmzbjpr5
authored andcommitted
refactor: extract react-core context and headless hook exports
Extract CopilotKitContext, useCopilotKit, and LicenseContext into src/v2/context.ts. Add src/v2/headless.ts barrel export for platform-agnostic hooks. Add v2/context and v2/headless entry points to tsdown config and package.json exports. Update all hook imports to use the new context module. Always subscribe to onError in web provider (matching RN pattern). Use batchedForceUpdate for onMessagesChanged. Replace extraDeps spread with JSON.stringify in useFrontendTool and useRenderTool dependency arrays.
1 parent 94c9c1d commit c3c3096

15 files changed

Lines changed: 219 additions & 72 deletions

packages/react-core/package.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@
4646
"require": "./dist/v2/index.cjs"
4747
},
4848
"./package.json": "./package.json",
49+
"./v2/context": {
50+
"import": "./dist/v2/context.mjs",
51+
"require": "./dist/v2/context.cjs"
52+
},
53+
"./v2/headless": {
54+
"import": "./dist/v2/headless.mjs",
55+
"require": "./dist/v2/headless.cjs"
56+
},
4957
"./v2/styles.css": "./dist/v2/index.css"
5058
},
5159
"publishConfig": {
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
"use client";
2+
3+
import { createContext, useContext, useEffect, useReducer } from "react";
4+
import { CopilotKitCoreReact } from "./lib/react-core";
5+
import type { CopilotKitCoreReactConfig } from "./lib/react-core";
6+
import type { LicenseContextValue } from "@copilotkit/shared";
7+
8+
// Re-export so headless.ts (and consumers) reference the same type declaration.
9+
export { CopilotKitCoreReact };
10+
export type { CopilotKitCoreReactConfig };
11+
12+
export interface CopilotKitContextValue {
13+
copilotkit: CopilotKitCoreReact;
14+
/**
15+
* Set of tool call IDs currently being executed.
16+
* This is tracked at the provider level to ensure tool execution events
17+
* are captured even before child components mount.
18+
*/
19+
executingToolCallIds: ReadonlySet<string>;
20+
}
21+
22+
export const EMPTY_SET: ReadonlySet<string> = new Set();
23+
24+
export const CopilotKitContext = createContext<CopilotKitContextValue | null>(
25+
null,
26+
);
27+
28+
export const useCopilotKit = (): CopilotKitContextValue => {
29+
const context = useContext(CopilotKitContext);
30+
const [, forceUpdate] = useReducer((x: number) => x + 1, 0);
31+
32+
if (!context) {
33+
throw new Error("useCopilotKit must be used within CopilotKitProvider");
34+
}
35+
useEffect(() => {
36+
const subscription = context.copilotkit.subscribe({
37+
onRuntimeConnectionStatusChanged: () => {
38+
forceUpdate();
39+
},
40+
});
41+
return () => {
42+
subscription.unsubscribe();
43+
};
44+
// eslint-disable-next-line react-hooks/exhaustive-deps
45+
}, []);
46+
47+
return context;
48+
};
49+
50+
// License context — shared between web and RN providers.
51+
// Default is permissive (all features allowed) — providers override via createLicenseContextValue.
52+
// Inlined here to avoid a runtime import from @copilotkit/shared, which pulls in
53+
// Node-only deps (jose) that break React Native's Metro bundler.
54+
export const LicenseContext = createContext<LicenseContextValue>({
55+
status: null,
56+
license: null,
57+
checkFeature: () => true,
58+
getLimit: () => null,
59+
} as LicenseContextValue);
60+
61+
export const useLicenseContext = (): LicenseContextValue =>
62+
useContext(LicenseContext);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* Headless (platform-agnostic) exports from @copilotkit/react-core/v2.
3+
*
4+
* No CSS, no web UI components, no DOM dependencies.
5+
* Used by @copilotkit/react-native.
6+
*/
7+
8+
// Re-export from context (which is external in this build) so the .d.ts
9+
// references the same type declaration. This avoids a nominal type mismatch
10+
// caused by private class members being declared in two separate .d.ts files.
11+
export { CopilotKitCoreReact } from "./context";
12+
export type { CopilotKitCoreReactConfig } from "./context";
13+
14+
// Chat configuration provider (no UI, just context)
15+
export {
16+
CopilotChatConfigurationProvider,
17+
useCopilotChatConfiguration,
18+
CopilotChatDefaultLabels,
19+
type CopilotChatLabels,
20+
type CopilotChatConfigurationValue,
21+
type CopilotChatConfigurationProviderProps,
22+
} from "./providers/CopilotChatConfigurationProvider";
23+
24+
// Platform-agnostic hooks
25+
export { useAgent, type UseAgentUpdate } from "./hooks/use-agent";
26+
export { useFrontendTool } from "./hooks/use-frontend-tool";
27+
export { useComponent } from "./hooks/use-component";
28+
export { useHumanInTheLoop } from "./hooks/use-human-in-the-loop";
29+
export { useInterrupt, type UseInterruptConfig } from "./hooks/use-interrupt";
30+
export { useSuggestions } from "./hooks/use-suggestions";
31+
export { useConfigureSuggestions } from "./hooks/use-configure-suggestions";
32+
export {
33+
useAgentContext,
34+
type AgentContextInput,
35+
type JsonSerializable,
36+
} from "./hooks/use-agent-context";
37+
export {
38+
useThreads,
39+
type Thread,
40+
type UseThreadsInput,
41+
type UseThreadsResult,
42+
} from "./hooks/use-threads";

packages/react-core/src/v2/hooks/use-agent-context.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCopilotKit } from "../providers/CopilotKitProvider";
1+
import { useCopilotKit } from "../context";
22
import { useLayoutEffect, useMemo } from "react";
33

44
/**

packages/react-core/src/v2/hooks/use-agent.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCopilotKit } from "../providers/CopilotKitProvider";
1+
import { useCopilotKit } from "../context";
22
import { useCopilotChatConfiguration } from "../providers/CopilotChatConfigurationProvider";
33
import { useMemo, useEffect, useReducer, useRef } from "react";
44
import { DEFAULT_AGENT_ID } from "@copilotkit/shared";
@@ -290,7 +290,7 @@ export function useAgent({
290290
};
291291

292292
if (updateFlags.includes(UseAgentUpdate.OnMessagesChanged)) {
293-
handlers.onMessagesChanged = forceUpdate;
293+
handlers.onMessagesChanged = batchedForceUpdate;
294294
}
295295

296296
if (updateFlags.includes(UseAgentUpdate.OnStateChanged)) {

packages/react-core/src/v2/hooks/use-configure-suggestions.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useCallback, useEffect, useMemo, useRef } from "react";
2-
import { useCopilotKit } from "../providers/CopilotKitProvider";
2+
import { useCopilotKit } from "../context";
33
import { useCopilotChatConfiguration } from "../providers/CopilotChatConfigurationProvider";
44
import { DEFAULT_AGENT_ID } from "@copilotkit/shared";
55
import type {

packages/react-core/src/v2/hooks/use-frontend-tool.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useEffect } from "react";
2-
import { useCopilotKit } from "../providers/CopilotKitProvider";
2+
import { useCopilotKit } from "../context";
33
import type { ReactFrontendTool } from "../types/frontend-tool";
44

55
const EMPTY_DEPS: ReadonlyArray<unknown> = [];
@@ -42,5 +42,5 @@ export function useFrontendTool<
4242
// Depend on stable keys by default and allow callers to opt into
4343
// additional dependencies for dynamic tool configuration.
4444
// 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)]);
4646
}

packages/react-core/src/v2/hooks/use-human-in-the-loop.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCopilotKit } from "../providers/CopilotKitProvider";
1+
import { useCopilotKit } from "../context";
22
import type { ReactFrontendTool } from "../types/frontend-tool";
33
import type { ReactHumanInTheLoop } from "../types/human-in-the-loop";
44
import type { ReactToolCallRenderer } from "../types/react-tool-call-renderer";

packages/react-core/src/v2/hooks/use-interrupt.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import React, {
55
useMemo,
66
useRef,
77
} from "react";
8-
import { useCopilotKit } from "../providers/CopilotKitProvider";
8+
import { useCopilotKit } from "../context";
99
import { useAgent } from "./use-agent";
1010
import type {
1111
InterruptEvent,

packages/react-core/src/v2/hooks/use-render-tool-call.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { useCallback, useMemo, useSyncExternalStore } from "react";
22
import { ToolCall, ToolMessage } from "@ag-ui/core";
33
import { ToolCallStatus } from "@copilotkit/core";
4-
import { useCopilotKit } from "../providers/CopilotKitProvider";
4+
import { useCopilotKit } from "../context";
55
import { useCopilotChatConfiguration } from "../providers/CopilotChatConfigurationProvider";
66
import { DEFAULT_AGENT_ID } from "@copilotkit/shared";
77
import { partialJSONParse } from "@copilotkit/shared";

0 commit comments

Comments
 (0)