skills/copilotkit-upgrade/references/v1-to-v2-migration.md
Remove v1 packages and install v2 equivalents:
# Remove v1
npm uninstall @copilotkit/react-core @copilotkit/react-ui @copilotkit/react-textarea \
@copilotkit/runtime @copilotkit/runtime-client-gql @copilotkit/shared @copilotkit/sdk-js
# Install v2
npm install @copilotkit/react @copilotkit/runtime @copilotkit/agent @copilotkit/shared
Package mapping:
| v1 Package | v2 Package | Notes |
|---|---|---|
@copilotkit/react-core | @copilotkit/react | Hooks, provider, types merged into one package |
@copilotkit/react-ui | @copilotkit/react | Chat components merged into same package |
@copilotkit/react-textarea | -- | Removed entirely, no v2 equivalent |
@copilotkit/runtime | @copilotkit/runtime | New agent-based architecture |
@copilotkit/runtime-client-gql | @ag-ui/client | Re-exported by @copilotkit/react |
@copilotkit/shared | @copilotkit/shared | Utility types and constants |
@copilotkit/sdk-js | @copilotkit/agent | Agent definitions (BuiltInAgent, etc.) |
Find-and-replace import paths across your codebase:
@copilotkit/react-core -> @copilotkit/react
@copilotkit/react-ui -> @copilotkit/react
@copilotkit/runtime -> @copilotkit/runtime
@copilotkit/shared -> @copilotkit/shared
CopilotKitimport { CopilotKit } from "@copilotkit/react-core";
function App() {
return (
<CopilotKit runtimeUrl="/api/copilotkit">
<MyApp />
</CopilotKit>
);
}
CopilotKitProviderimport { CopilotKitProvider } from "@copilotkit/react";
function App() {
return (
<CopilotKitProvider runtimeUrl="/api/copilotkit">
<MyApp />
</CopilotKitProvider>
);
}
Key differences:
CopilotKit to CopilotKitProviderCopilotKitProps to CopilotKitProviderPropscredentials, selfManagedAgents, renderToolCalls, renderActivityMessages propsagents prop (use selfManagedAgents or agents__unsafe_dev_only for local dev)v1:
import { useCopilotAction } from "@copilotkit/react-core";
useCopilotAction({
name: "addTodo",
description: "Add a new todo item",
parameters: [
{
name: "title",
type: "string",
description: "Todo title",
required: true,
},
{ name: "priority", type: "number", description: "Priority 1-5" },
],
handler: ({ title, priority }) => {
addTodo({ title, priority: priority ?? 3 });
},
render: ({ status, args }) => (
<div>
Adding: {args.title} (status: {status})
</div>
),
});
v2:
import { useFrontendTool } from "@copilotkit/react";
import { z } from "zod";
useFrontendTool({
name: "addTodo",
description: "Add a new todo item",
parameters: z.object({
title: z.string().describe("Todo title"),
priority: z.number().optional().describe("Priority 1-5"),
}),
handler: async (args) => {
addTodo({ title: args.title, priority: args.priority ?? 3 });
},
render: ({ status, args }) => (
<div>
Adding: {args.title} (status: {status})
</div>
),
});
Key differences:
useCopilotAction to useFrontendTool{ name, type, required })handler receives the full args object directly (not destructured from { arg1, arg2 })render prop works similarly but uses ToolCallStatus enum ("inProgress", "executing", "complete")available prop ("enabled" | "disabled") replacing the v1 disabled booleanagentId prop to scope a tool to a specific agentv1:
import { useCopilotReadable } from "@copilotkit/react-core";
function EmployeeList({ employees }) {
useCopilotReadable({
description: "The list of employees",
value: employees,
});
return <div>...</div>;
}
v2:
import { useAgentContext } from "@copilotkit/react";
function EmployeeList({ employees }) {
useAgentContext({
description: "The list of employees",
value: employees,
});
return <div>...</div>;
}
Key differences:
useCopilotReadable to useAgentContextparentId hierarchical context feature from v1 is not available in v2; flatten your context insteadvalue prop accepts JsonSerializable (string, number, boolean, null, arrays, objects) -- objects are auto-serialized to JSON stringsv1:
import { useMakeCopilotDocumentReadable } from "@copilotkit/react-core";
useMakeCopilotDocumentReadable(documentPointer, ["category1"]);
v2:
import { useAgentContext } from "@copilotkit/react";
useAgentContext({
description: "Document content for category1",
value: documentContent,
});
Key differences:
DocumentPointer equivalent in v2; pass document content directly via useAgentContextdescription field to provide contextv1:
import { useCoAgent } from "@copilotkit/react-core";
type AgentState = { count: number };
const { name, state, setState, running, start, stop, run } =
useCoAgent<AgentState>({
name: "my-agent",
initialState: { count: 0 },
});
v2:
import { useAgent } from "@copilotkit/react";
const agent = useAgent({ agentId: "my-agent" });
// Access agent state, messages, run status through the AbstractAgent interface
// agent.run(), agent.stop(), etc.
Key differences:
useCoAgent to useAgentname prop renamed to agentIdinitialState / setState -- agent state is managed through AG-UI protocol events (StateSnapshotEvent, StateDeltaEvent)AbstractAgent instance instead of a destructured state objectrun / start / stop API surface differs -- v2 uses AG-UI protocol methodsv1:
import { useCoAgentStateRender } from "@copilotkit/react-core";
useCoAgentStateRender<YourAgentState>({
name: "basic_agent",
nodeName: "search_node",
render: ({ status, state, nodeName }) => (
<SearchProgress state={state} status={status} />
),
});
v2:
import { useRenderToolCall } from "@copilotkit/react";
// For tool call rendering:
useRenderToolCall({
toolCall,
toolMessage,
});
// Or for activity messages:
import { useRenderActivityMessage } from "@copilotkit/react";
useRenderActivityMessage({
// renders activity/progress messages from the agent
});
Key differences:
useCoAgentStateRender is split into useRenderToolCall (for tool execution UI) and useRenderActivityMessage (for progress/activity rendering)nodeName filtering -- tool rendering is keyed by tool call IDToolCall and ToolMessage types instead of the v1 agent state shapev1:
import { useLangGraphInterrupt } from "@copilotkit/react-core";
useLangGraphInterrupt({
name: "confirm-action",
nodeName: "confirmation_node",
agentName: "my-agent",
render: ({ event, resolve }) => (
<ConfirmDialog
message={event.value}
onConfirm={() => resolve("confirmed")}
onCancel={() => resolve("cancelled")}
/>
),
});
v2:
import { useInterrupt } from "@copilotkit/react";
const interruptElement = useInterrupt({
renderInChat: false, // false = you render it yourself; true = renders in CopilotChat
render: ({ event, resolve }) => (
<ConfirmDialog
message={event.value}
onConfirm={() => resolve("confirmed")}
onCancel={() => resolve("cancelled")}
/>
),
handler: async ({ event }) => {
// Optional: handle programmatically before render
},
enabled: (event) => event.value?.type === "confirm", // Optional filter
agentId: "my-agent",
});
// If renderInChat is false, render interruptElement in your UI:
return <div>{interruptElement}</div>;
Key differences:
useLangGraphInterrupt to useInterruptagentName renamed to agentIdnodeName filtering removed; use the enabled predicate insteadrenderInChat prop (default true) -- when true, interrupt UI renders inside <CopilotChat> automaticallyhandler for programmatic interrupt handling before renderingReact.ReactElement | null when renderInChat: falsev1:
import { useCopilotChat } from "@copilotkit/react-core";
import { TextMessage, MessageRole } from "@copilotkit/runtime-client-gql";
const { appendMessage, visibleMessages, isLoading, stopGeneration, reset } =
useCopilotChat();
await appendMessage(
new TextMessage({ role: MessageRole.User, content: "Hello" }),
);
v2:
import { useAgent } from "@copilotkit/react";
const agent = useAgent({ agentId: "my-agent" });
// Messages, run status, etc. are available through the agent's AG-UI event stream
// Chat UI components (CopilotChat, CopilotPopup, CopilotSidebar) handle this automatically
Key differences:
useCopilotChat is replaced by useAgent for agent interactionTextMessage/MessageRole (GraphQL) to AG-UI event typesAbstractAgent API directlyv1:
import { useCopilotChatSuggestions } from "@copilotkit/react-core";
useCopilotChatSuggestions({
instructions: "Suggest helpful actions based on the current page",
maxSuggestions: 3,
});
v2:
import { useConfigureSuggestions, useSuggestions } from "@copilotkit/react";
// Configure suggestion generation:
useConfigureSuggestions({
instructions: "Suggest helpful actions based on the current page",
maxSuggestions: 3,
});
// Read suggestions:
const { suggestions, reloadSuggestions, clearSuggestions, isLoading } =
useSuggestions({
agentId: "my-agent",
});
Key differences:
useConfigureSuggestions (write config) and useSuggestions (read state)useSuggestions returns { suggestions, reloadSuggestions, clearSuggestions, isLoading }v1:
import { useCopilotAdditionalInstructions } from "@copilotkit/react-core";
useCopilotAdditionalInstructions({
instructions: "Do not answer questions about the weather.",
});
v2:
import { useAgentContext } from "@copilotkit/react";
useAgentContext({
description: "Additional instructions for the agent",
value: "Do not answer questions about the weather.",
});
v1:
import { useHumanInTheLoop } from "@copilotkit/react-core";
v2:
import { useHumanInTheLoop } from "@copilotkit/react";
The API is similar -- registers a tool that pauses for user input via a render function with a respond callback.
import {
CopilotRuntime,
OpenAIAdapter,
GoogleGenerativeAIAdapter,
} from "@copilotkit/runtime";
import { copilotKitEndpoint } from "@copilotkit/runtime"; // Next.js App Router
const serviceAdapter = new OpenAIAdapter({ model: "gpt-4o" });
const runtime = new CopilotRuntime({
actions: [
{
name: "lookupWeather",
description: "Look up the weather",
parameters: [{ name: "city", type: "string" }],
handler: async ({ city }) => fetchWeather(city),
},
],
remoteEndpoints: [
{ url: "http://localhost:8000/copilotkit", type: "langgraph" },
],
});
// Next.js App Router
export const POST = copilotKitEndpoint(runtime, serviceAdapter);
import { CopilotRuntime, createCopilotEndpoint } from "@copilotkit/runtime";
import { BuiltInAgent } from "@copilotkit/agent";
import { LangGraphAgent } from "@ag-ui/langgraph";
const runtime = new CopilotRuntime({
agents: {
default: new BuiltInAgent({
model: "openai:gpt-4o",
// Frontend tools are registered client-side via useFrontendTool
}),
myLangGraphAgent: new LangGraphAgent({
url: "http://localhost:8000",
graphId: "my-graph",
}),
},
// Optional middleware
a2ui: {}, // A2UI middleware config
mcpApps: {
// MCP Apps middleware
servers: [{ transport: { type: "sse", url: "http://localhost:3001/sse" } }],
},
});
// Hono-based endpoint (works with Next.js, Express, standalone)
const app = createCopilotEndpoint({
runtime,
basePath: "/api/copilotkit",
});
export default app;
Key differences:
OpenAIAdapter, LangChainAdapter, etc.) -- model selection is done inside agentsactions array on the runtime -- frontend tools are registered via useFrontendTool, backend tools via agent configurationremoteEndpoints -- agents are passed directly as AbstractAgent instancescreateCopilotEndpoint (Hono) or createCopilotEndpointExpress (Express) instead of framework-specific integrations// SSE Mode (default -- stateless, server-sent events)
const runtime = new CopilotRuntime({
agents: { default: myAgent },
});
// Intelligence Mode (durable threads, realtime, requires CopilotKitIntelligence)
import { CopilotKitIntelligence } from "@copilotkit/runtime";
const runtime = new CopilotRuntime({
agents: { default: myAgent },
intelligence: new CopilotKitIntelligence({
/* config */
}),
identifyUser: (request) => ({ id: "user-123" }),
generateThreadNames: true,
});
Chat components have the same names but move to @copilotkit/react:
v1:
import {
CopilotChat,
CopilotPopup,
CopilotSidebar,
} from "@copilotkit/react-ui";
v2:
import { CopilotChat, CopilotPopup, CopilotSidebar } from "@copilotkit/react";
v2 adds new chat sub-components for granular customization:
CopilotChatView -- the main chat viewCopilotChatInput -- input areaCopilotChatAssistantMessage / CopilotChatUserMessage -- message componentsCopilotChatReasoningMessage -- reasoning/thinking displayCopilotChatToolCallsView -- tool call renderingCopilotChatSuggestionView / CopilotChatSuggestionPill -- suggestion UICopilotChatToggleButton -- toggle button for popup/sidebarCopilotSidebarView / CopilotPopupView -- layout containersCopilotModalHeader -- header for modal layoutsCopilotTextarea from @copilotkit/react-textarea has no v2 equivalent. If you were using it for AI-assisted text input, replace it with:
<textarea> or rich text editoruseFrontendTool hook to provide AI writing assistanceCopilotChat in a sidebar/popup for inline AI helpv1 used GraphQL-based message types from @copilotkit/runtime-client-gql:
import { TextMessage, MessageRole } from "@copilotkit/runtime-client-gql";
v2 uses AG-UI protocol types from @ag-ui/client (re-exported by @copilotkit/react):
import {
Message,
TextMessage,
ToolCall,
ToolMessage,
EventType,
} from "@copilotkit/react"; // re-exports from @ag-ui/client