skills/copilotkit-upgrade/references/breaking-changes.md
v1 split React functionality across three packages:
@copilotkit/react-core -- provider, hooks, types@copilotkit/react-ui -- chat components (CopilotChat, CopilotPopup, CopilotSidebar)@copilotkit/react-textarea -- CopilotTextarea componentv2 consolidates the React surface under the /v2 subpath of the same @copilotkit/react-core package (there is no @copilotkit/react package):
@copilotkit/react-core/v2 -- provider, hooks, types, chat components, and AG-UI re-exports (@copilotkit/react-core/v2/styles.css for styles)The v2 API is exposed through the same @copilotkit/* packages -- no package name changes are required when upgrading. The v2 symbols live under the /v2 subpath (@copilotkit/react-core/v2, @copilotkit/runtime/v2, @copilotkit/runtime/v2/express).
| Package | Status |
|---|---|
@copilotkit/react-textarea | No v2 equivalent. The v1 package stays installable; remove it only after migrating off CopilotTextarea. |
@copilotkit/runtime-client-gql | Replaced by @ag-ui/client (re-exported from @copilotkit/react-core/v2) |
@copilotkit/sdk-js | Removed. BuiltInAgent and agent definitions ship from @copilotkit/runtime/v2 |
The most fundamental breaking change is the protocol layer. v1 used a GraphQL-based protocol (@copilotkit/runtime-client-gql). v2 uses the AG-UI protocol (@ag-ui/client / @ag-ui/core), which is SSE-based.
Impact:
TextMessage, ActionExecutionMessage, ResultMessage, etc.) are replaced by AG-UI event types (TextMessageChunkEvent, ToolCallStartEvent, ToolCallArgsEvent, ToolCallEndEvent, ToolCallResultEvent, etc.)MessageRole enum is replaced by AG-UI message rolesThe provider keeps the name CopilotKit; the import path changes from the package root (@copilotkit/react-core, legacy v1) to the /v2 subpath (@copilotkit/react-core/v2). The /v2 subpath also exports a CopilotKitProvider component -- do not migrate to it. It is a functionality subset of CopilotKit, which is the compatibility bridge across v1 and v2 (its CopilotKitProps extends Omit<CopilotKitProviderProps, "children"> with a narrowed children type, so every non-children CopilotKitProvider prop works on it).
| v1 Prop | v2 Status | Notes |
|---|---|---|
runtimeUrl | Kept | Same behavior |
headers | Kept | Same behavior |
publicApiKey | Kept (deprecated) | publicLicenseKey is the canonical name |
properties | Kept | Same behavior |
agents | Removed | Use selfManagedAgents or agents__unsafe_dev_only |
guardrails_c | Kept (CopilotCloud only) | Marked @internal/defunct in source, but still wired into the legacy CopilotCloud restrictToTopic config when a cloud key (publicApiKey) is set; has no effect on the v2 AG-UI runtime path |
children | Kept | Same behavior |
| -- | Added: credentials | RequestCredentials for fetch (e.g., "include" for cookies) |
| -- | Added: selfManagedAgents | Record<string, AbstractAgent> for client-side agents |
| -- | Added: renderToolCalls | ReactToolCallRenderer[] for provider-level tool renderers |
| -- | Added: renderActivityMessages | ReactActivityMessageRenderer[] for activity renderers |
| -- | Added: useSingleEndpoint | Boolean to use single-route endpoint mode |
useCopilotContext is replaced by useCopilotKit (imported from @copilotkit/react-core/v2/context), which returns { copilotkit: CopilotKitCoreReact, executingToolCallIds: ReadonlySet<string> }.
Parameter definition change: v1 used a custom parameter descriptor format. v2 uses Zod schemas.
// v1 parameters
parameters: [
{ name: "city", type: "string", description: "City name", required: true },
{ name: "units", type: "string", enum: ["celsius", "fahrenheit"] },
];
// v2 parameters (Zod)
parameters: z.object({
city: z.string().describe("City name"),
units: z.enum(["celsius", "fahrenheit"]).optional(),
});
Handler signature change:
// v1
handler: ({ city, units }) => { ... }
// v2
handler: async (args) => { ... } // args is typed from the Zod schema
Render props change:
// v1 render status: the string literals "inProgress" | "executing" | "complete"
// v1 uses `respond()` callback for interactive actions
// v2 render status: the `ToolCallStatus` enum (ToolCallStatus.InProgress | .Executing | .Complete).
// Its values ARE those same strings, so `status === "inProgress"` still works;
// prefer comparing against the enum members.
// v2 render props: { name, toolCallId, args, status, result }
Availability change:
// v1
disabled: true;
// v2 — `available` is a boolean (defaults to true; set false to hide the tool)
available: false;
Breaking: The parentId parameter for hierarchical context is removed. Flatten nested contexts.
// v1 (hierarchical)
const parentId = useCopilotReadable({ description: "Parent", value: "..." });
useCopilotReadable({ description: "Child", value: "...", parentId });
// v2 (flat)
useAgentContext({
description: "Parent - Child context",
value: { parent: "...", child: "..." },
});
Breaking: Completely different return type.
// v1 returns
{
(name, nodeName, state, setState, running, start, stop, run);
}
// v2 returns
AbstractAgent; // AG-UI agent instance with run(), stop(), etc.
name -> agentId (in props)initialState -> removed (no client-side state initialization)setState -> removed (state flows via AG-UI events)nodeName -> removedstate -> accessed through AG-UI StateSnapshotEvent / StateDeltaEventBreaking: Different API shape.
agentName -> agentIdnodeName -> removed (use enabled predicate to filter)render props change: v2 receives InterruptRenderProps<TValue, TResult> = { event, resolve, result } (still includes event/resolve, adds result)renderInChat prop (default true) controls whether interrupt renders inside CopilotChathandler prop for programmatic handling before renderingenabled predicate prop for filtering interruptsReplaced by useAgent for agent interaction. The headless chat API (appendMessage, visibleMessages, etc.) is replaced by the AG-UI agent event stream.
Split into two hooks: one for configuration, one for reading state.
Split into two hooks based on the type of rendering needed.
Use useAgentContext with an appropriate description to provide instructions.
Use useAgentContext to pass document content. The DocumentPointer type and category-based filtering are removed.
All service adapters are removed from the runtime:
| Removed Adapter | v2 Alternative |
|---|---|
OpenAIAdapter | Use BuiltInAgent({ model: "openai/gpt-4o" }) |
AnthropicAdapter | Use BuiltInAgent({ model: "anthropic/claude-sonnet-4.5" }) |
GoogleGenerativeAIAdapter | Use BuiltInAgent({ model: "google/gemini-2.5-pro" }) |
LangChainAdapter | Use a custom AbstractAgent implementation |
GroqAdapter | Use a custom AbstractAgent (pass a Groq LanguageModel instance) |
UnifyAdapter | Use a custom AbstractAgent implementation |
OpenAIAssistantAdapter | Use a custom AbstractAgent implementation |
BedrockAdapter | Use a custom AbstractAgent implementation |
OllamaAdapter | Use a custom AbstractAgent implementation |
EmptyAdapter | Not needed |
// v1
new CopilotRuntime({
actions: [...], // Removed
remoteEndpoints: [...], // Removed
remoteActions: [...], // Removed
onBeforeRequest: (options) => {}, // Deprecated
onAfterRequest: (options) => {}, // Deprecated
})
// v2
new CopilotRuntime({
agents: { ... }, // Required: Record<string, AbstractAgent>
transcriptionService: ..., // Optional: TranscriptionService
beforeRequestMiddleware: ..., // Optional: BeforeRequestMiddleware
afterRequestMiddleware: ..., // Optional: AfterRequestMiddleware
a2ui: { ... }, // Optional: A2UIMiddleware config
mcpApps: { servers: [...] }, // Optional: MCP Apps middleware
// Intelligence mode only:
intelligence: new CopilotKitIntelligence({ ... }),
identifyUser: (request) => ({ id: "..." }),
generateThreadNames: true,
})
v1 had built-in integrations for Next.js (App Router, Pages Router), Express, NestJS, and Node HTTP. v2 uses Hono as the standard HTTP layer:
| v1 Integration | v2 Replacement |
|---|---|
copilotRuntimeNextJSAppRouterEndpoint | createCopilotHonoHandler (Hono, works with Next.js) |
copilotRuntimeNextJSPagesRouterEndpoint | createCopilotHonoHandler (Hono) |
CopilotRuntimeNodeExpressEndpoint | createCopilotExpressHandler (@copilotkit/runtime/v2/express) |
CopilotRuntimeNestEndpoint | Use Hono adapter or Express endpoint |
CopilotRuntimeNodeHttpEndpoint | Use Hono or Express endpoint |
// v1 (Next.js App Router example)
import { copilotRuntimeNextJSAppRouterEndpoint } from "@copilotkit/runtime";
export const POST = copilotRuntimeNextJSAppRouterEndpoint({
runtime,
serviceAdapter,
endpoint: "/api/copilotkit",
});
// v2 -- use createCopilotHonoHandler (createCopilotEndpoint is a deprecated alias)
import { createCopilotHonoHandler } from "@copilotkit/runtime/v2";
const app = createCopilotHonoHandler({
runtime,
basePath: "/api/copilotkit",
cors: {
origin: "https://myapp.com",
credentials: true,
},
});
// For Next.js App Router, export the Hono app's fetch handler
export const POST = app.fetch;
export const GET = app.fetch;
// v1 (remote endpoint)
new CopilotRuntime({
remoteEndpoints: [
{
url: "http://localhost:8000/copilotkit",
type: "langgraph",
},
],
});
// v2 (direct agent instance)
import { LangGraphAgent } from "@copilotkit/runtime/langgraph";
new CopilotRuntime({
agents: {
myAgent: new LangGraphAgent({
deploymentUrl: "http://localhost:8000",
graphId: "my-graph",
}),
},
});
v1 used a custom Parameter type for defining tool parameters:
type Parameter = {
name: string;
type:
| "string"
| "number"
| "boolean"
| "object"
| "string[]"
| "number[]"
| "boolean[]"
| "object[]";
description?: string;
required?: boolean;
enum?: string[];
attributes?: Parameter[]; // for object types
};
v2 uses Zod schemas (z.object(...)) or Standard Schema V1 (StandardSchemaV1).
v1 GraphQL types from @copilotkit/runtime-client-gql are replaced by AG-UI types:
| v1 Type | v2 Type |
|---|---|
TextMessage | Message with text content |
ActionExecutionMessage | ToolCall |
ResultMessage | ToolMessage |
MessageRole | AG-UI role types |
v2 introduces AG-UI event types for streaming:
RunStartedEvent, RunFinishedEvent, RunErrorEventTextMessageChunkEventToolCallStartEvent, ToolCallArgsEvent, ToolCallEndEvent, ToolCallResultEventStateSnapshotEvent, StateDeltaEventReasoningStartEvent, ReasoningMessageStartEvent, ReasoningMessageContentEvent, ReasoningMessageEndEvent, ReasoningEndEvent