From 0560247fec16e230efd3b3ff683482e1315612af Mon Sep 17 00:00:00 2001 From: Austin Merrick Date: Fri, 22 May 2026 10:04:41 -0700 Subject: [PATCH 01/30] feat(skills): replace lifecycle skills with copilotkit-* from CopilotKit/skills - Remove 6 old auto-generated lifecycle skills (0-to-working-chat, debug-and-troubleshoot, go-to-production, scale-to-multi-agent, spa-without-runtime, v1-to-v2-migration) - Add 8 canonical skills verbatim from github.com/CopilotKit/skills: copilotkit-setup, copilotkit-develop, copilotkit-agui, copilotkit-integrations, copilotkit-debug, copilotkit-upgrade, copilotkit-contribute, copilotkit-self-update - Update copilotkit-self-update install command to reference CopilotKit/CopilotKit - Add .claude-plugin/{plugin,marketplace}.json and .mcp.json (repo URL and version updated to match monorepo: CopilotKit/CopilotKit, v1.57.3) - Exempt copilotkit-* slugs from sync orphan detection in sync-plugin-skills.ts; runtime/react-core/a2ui-renderer remain synced from packages/*/skills as before --- .claude-plugin/marketplace.json | 30 +- .claude-plugin/plugin.json | 18 +- .mcp.json | 7 +- scripts/sync-plugin-skills.ts | 9 + skills/0-to-working-chat/SKILL.md | 474 ------------ skills/copilotkit-agui/SKILL.md | 91 +++ .../references/building-agents.md | 423 +++++++++++ .../copilotkit-agui/references/client-sdk.md | 574 +++++++++++++++ .../references/event-flow-diagrams.md | 424 +++++++++++ .../references/protocol-spec.md | 681 ++++++++++++++++++ skills/copilotkit-agui/sources.md | 49 ++ skills/copilotkit-contribute/SKILL.md | 72 ++ .../references/contribution-guide.md | 141 ++++ .../references/pr-guidelines.md | 103 +++ .../references/repo-structure.md | 125 ++++ .../references/testing-guide.md | 192 +++++ skills/copilotkit-contribute/sources.md | 36 + skills/copilotkit-debug/SKILL.md | 119 +++ .../references/agent-debugging.md | 284 ++++++++ .../references/error-patterns.md | 268 +++++++ .../references/quick-workflows.md | 284 ++++++++ .../references/runtime-debugging.md | 230 ++++++ skills/copilotkit-debug/sources.md | 35 + skills/copilotkit-develop/SKILL.md | 182 +++++ skills/copilotkit-develop/eval.yaml | 198 +++++ .../references/api-surface.md | 489 +++++++++++++ .../references/chat-customization.md | 240 ++++++ .../references/runtime-api.md | 347 +++++++++ skills/copilotkit-develop/sources.md | 66 ++ skills/copilotkit-integrations/SKILL.md | 182 +++++ .../references/integrations/a2a.md | 167 +++++ .../references/integrations/adk.md | 156 ++++ .../references/integrations/agno.md | 112 +++ .../references/integrations/crewai.md | 221 ++++++ .../references/integrations/langgraph.md | 276 +++++++ .../references/integrations/llamaindex.md | 128 ++++ .../references/integrations/mastra.md | 149 ++++ .../integrations/ms-agent-framework.md | 245 +++++++ .../references/integrations/pydantic-ai.md | 138 ++++ .../references/integrations/strands.md | 157 ++++ skills/copilotkit-integrations/sources.md | 40 + skills/copilotkit-self-update/SKILL.md | 28 + skills/copilotkit-setup/SKILL.md | 395 ++++++++++ .../assets/express-runtime.ts | 66 ++ .../assets/nextjs-app-router-page.tsx | 22 + .../assets/nextjs-app-router-route.ts | 36 + skills/copilotkit-setup/eval.yaml | 191 +++++ .../references/framework-detection.md | 101 +++ .../references/runtime-architecture.md | 237 ++++++ .../references/telemetry-setup.md | 83 +++ skills/copilotkit-setup/sources.md | 32 + skills/copilotkit-upgrade/SKILL.md | 134 ++++ .../references/breaking-changes.md | 336 +++++++++ .../references/deprecation-map.md | 112 +++ .../references/v1-to-v2-migration.md | 553 ++++++++++++++ skills/copilotkit-upgrade/sources.md | 38 + skills/debug-and-troubleshoot/SKILL.md | 282 -------- .../references/error-codes.md | 126 ---- skills/go-to-production/SKILL.md | 416 ----------- skills/scale-to-multi-agent/SKILL.md | 369 ---------- skills/spa-without-runtime/SKILL.md | 278 ------- skills/v1-to-v2-migration/SKILL.md | 414 ----------- .../references/migration-playbook.md | 350 --------- .../references/rename-table.md | 78 -- 64 files changed, 10032 insertions(+), 2807 deletions(-) delete mode 100644 skills/0-to-working-chat/SKILL.md create mode 100644 skills/copilotkit-agui/SKILL.md create mode 100644 skills/copilotkit-agui/references/building-agents.md create mode 100644 skills/copilotkit-agui/references/client-sdk.md create mode 100644 skills/copilotkit-agui/references/event-flow-diagrams.md create mode 100644 skills/copilotkit-agui/references/protocol-spec.md create mode 100644 skills/copilotkit-agui/sources.md create mode 100644 skills/copilotkit-contribute/SKILL.md create mode 100644 skills/copilotkit-contribute/references/contribution-guide.md create mode 100644 skills/copilotkit-contribute/references/pr-guidelines.md create mode 100644 skills/copilotkit-contribute/references/repo-structure.md create mode 100644 skills/copilotkit-contribute/references/testing-guide.md create mode 100644 skills/copilotkit-contribute/sources.md create mode 100644 skills/copilotkit-debug/SKILL.md create mode 100644 skills/copilotkit-debug/references/agent-debugging.md create mode 100644 skills/copilotkit-debug/references/error-patterns.md create mode 100644 skills/copilotkit-debug/references/quick-workflows.md create mode 100644 skills/copilotkit-debug/references/runtime-debugging.md create mode 100644 skills/copilotkit-debug/sources.md create mode 100644 skills/copilotkit-develop/SKILL.md create mode 100644 skills/copilotkit-develop/eval.yaml create mode 100644 skills/copilotkit-develop/references/api-surface.md create mode 100644 skills/copilotkit-develop/references/chat-customization.md create mode 100644 skills/copilotkit-develop/references/runtime-api.md create mode 100644 skills/copilotkit-develop/sources.md create mode 100644 skills/copilotkit-integrations/SKILL.md create mode 100644 skills/copilotkit-integrations/references/integrations/a2a.md create mode 100644 skills/copilotkit-integrations/references/integrations/adk.md create mode 100644 skills/copilotkit-integrations/references/integrations/agno.md create mode 100644 skills/copilotkit-integrations/references/integrations/crewai.md create mode 100644 skills/copilotkit-integrations/references/integrations/langgraph.md create mode 100644 skills/copilotkit-integrations/references/integrations/llamaindex.md create mode 100644 skills/copilotkit-integrations/references/integrations/mastra.md create mode 100644 skills/copilotkit-integrations/references/integrations/ms-agent-framework.md create mode 100644 skills/copilotkit-integrations/references/integrations/pydantic-ai.md create mode 100644 skills/copilotkit-integrations/references/integrations/strands.md create mode 100644 skills/copilotkit-integrations/sources.md create mode 100644 skills/copilotkit-self-update/SKILL.md create mode 100644 skills/copilotkit-setup/SKILL.md create mode 100644 skills/copilotkit-setup/assets/express-runtime.ts create mode 100644 skills/copilotkit-setup/assets/nextjs-app-router-page.tsx create mode 100644 skills/copilotkit-setup/assets/nextjs-app-router-route.ts create mode 100644 skills/copilotkit-setup/eval.yaml create mode 100644 skills/copilotkit-setup/references/framework-detection.md create mode 100644 skills/copilotkit-setup/references/runtime-architecture.md create mode 100644 skills/copilotkit-setup/references/telemetry-setup.md create mode 100644 skills/copilotkit-setup/sources.md create mode 100644 skills/copilotkit-upgrade/SKILL.md create mode 100644 skills/copilotkit-upgrade/references/breaking-changes.md create mode 100644 skills/copilotkit-upgrade/references/deprecation-map.md create mode 100644 skills/copilotkit-upgrade/references/v1-to-v2-migration.md create mode 100644 skills/copilotkit-upgrade/sources.md delete mode 100644 skills/debug-and-troubleshoot/SKILL.md delete mode 100644 skills/debug-and-troubleshoot/references/error-codes.md delete mode 100644 skills/go-to-production/SKILL.md delete mode 100644 skills/scale-to-multi-agent/SKILL.md delete mode 100644 skills/spa-without-runtime/SKILL.md delete mode 100644 skills/v1-to-v2-migration/SKILL.md delete mode 100644 skills/v1-to-v2-migration/references/migration-playbook.md delete mode 100644 skills/v1-to-v2-migration/references/rename-table.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index f01f31df131..2e6c99672cb 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -1,20 +1,38 @@ { - "name": "copilotkit", - "description": "CopilotKit skill plugin — installable directly from the CopilotKit monorepo", + "name": "copilotkit-plugins", "owner": { "name": "CopilotKit", "email": "support@copilotkit.ai" }, + "metadata": { + "description": "AI agent skills for CopilotKit — setup, develop, integrate, debug, upgrade, and contribute", + "version": "1.57.3" + }, "plugins": [ { "name": "copilotkit", - "description": "CopilotKit v2 skills (runtime, react-core, a2ui-renderer + 6 lifecycle journeys)", - "version": "1.57.3", "source": "./", + "description": "AI agent skills for CopilotKit — setup, develop, integrate, debug, upgrade, and contribute to CopilotKit projects", + "version": "1.57.3", "author": { "name": "CopilotKit", - "email": "support@copilotkit.ai" - } + "url": "https://copilotkit.ai" + }, + "homepage": "https://docs.copilotkit.ai", + "repository": "https://github.com/CopilotKit/CopilotKit", + "license": "MIT", + "keywords": [ + "copilotkit", + "ai", + "agents", + "react", + "next.js", + "langgraph", + "crewai", + "ag-ui", + "mcp" + ], + "category": "ai-frameworks" } ] } diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 6fac2967f7d..61e6a8502fc 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,20 +1,18 @@ { "name": "copilotkit", - "description": "CopilotKit v2 skills for Claude Code — runtime (core), react-core (framework), a2ui-renderer (framework), and 6 lifecycle journey skills (0-to-working-chat, spa-without-runtime, go-to-production, scale-to-multi-agent, v1-to-v2-migration, debug-and-troubleshoot).", + "description": "AI agent skills for CopilotKit — setup, develop, integrate, debug, upgrade, and contribute to CopilotKit projects", "version": "1.57.3", "author": { "name": "CopilotKit", - "email": "support@copilotkit.ai" + "url": "https://copilotkit.ai" }, - "homepage": "https://github.com/CopilotKit/CopilotKit", + "homepage": "https://docs.copilotkit.ai", "repository": "https://github.com/CopilotKit/CopilotKit", "license": "MIT", "keywords": [ - "copilotkit", - "ai", - "agents", - "skills", - "ag-ui", - "tanstack-intent" - ] + "copilotkit", "ai", "agents", "react", "next.js", + "langgraph", "crewai", "pydantic-ai", "mastra", + "ag-ui", "mcp", "copilot", "chatbot" + ], + "mcpServers": "./.mcp.json" } diff --git a/.mcp.json b/.mcp.json index 156f7652aa2..99a22ec1133 100644 --- a/.mcp.json +++ b/.mcp.json @@ -1,9 +1,8 @@ { "mcpServers": { - "nx-mcp": { - "type": "stdio", - "command": "npx", - "args": ["nx", "mcp"] + "copilotkit-docs": { + "type": "http", + "url": "https://mcp.copilotkit.ai/mcp" } } } diff --git a/scripts/sync-plugin-skills.ts b/scripts/sync-plugin-skills.ts index cda42e88564..ca1e76c711f 100644 --- a/scripts/sync-plugin-skills.ts +++ b/scripts/sync-plugin-skills.ts @@ -13,6 +13,15 @@ export const RESERVED_LIFECYCLE_SLUGS: ReadonlySet = new Set([ "scale-to-multi-agent", "v1-to-v2-migration", "debug-and-troubleshoot", + // Standalone skills — not generated from packages/*/skills, exempt from orphan detection + "copilotkit-setup", + "copilotkit-develop", + "copilotkit-agui", + "copilotkit-integrations", + "copilotkit-debug", + "copilotkit-upgrade", + "copilotkit-contribute", + "copilotkit-self-update", ]); // Version sync — plugin version tracks this package's version. diff --git a/skills/0-to-working-chat/SKILL.md b/skills/0-to-working-chat/SKILL.md deleted file mode 100644 index a0f1880bf13..00000000000 --- a/skills/0-to-working-chat/SKILL.md +++ /dev/null @@ -1,474 +0,0 @@ ---- -name: 0-to-working-chat -description: > - End-to-end quickstart for CopilotKit v2 — scaffold, mount the runtime, mount - the provider, render chat, add the first tool. Canonical framework order is - React Router v7 framework mode → TanStack Start → Next.js App Router, plus - an SPA-without-runtime branch. Every branch uses createCopilotRuntimeHandler - (the fetch primitive) — avoid createCopilotExpressHandler / - createCopilotHonoHandler. Factory Mode BuiltInAgent with TanStack AI is the - preferred default. Load when bootstrapping a new CopilotKit v2 app, adding - runtime to an existing React app, or deciding which framework branch to - wire. -type: lifecycle -library: copilotkit -library_version: "1.56.2" -requires: - - copilotkit/runtime - - copilotkit/react-core -sources: - - "CopilotKit/CopilotKit:examples/v2/react-router/app/routes/api.copilotkit.$.tsx" - - "CopilotKit/CopilotKit:examples/v2/react-router/app/routes/_index.tsx" - - "CopilotKit/CopilotKit:examples/v2/runtime/node/src/index.ts" - - "CopilotKit/CopilotKit:examples/v2/runtime/cf-workers/src/index.ts" - - "CopilotKit/CopilotKit:packages/cli/src/commands/create.ts" - - "CopilotKit/CopilotKit:docs/snippets/shared/troubleshooting/common-issues.mdx" ---- - -## Setup - -One agent, one tool, one chat. The React Router v7 framework-mode branch is -the canonical example — pick it first unless you're on a different stack. - -### Step 1 — Scaffold - -```bash -npx copilotkit create -f react-router my-app -cd my-app -pnpm install -``` - -### Step 2 — Mount the runtime (React Router v7 framework mode — DEFAULT) - -Create a catch-all resource route `app/routes/api.copilotkit.$.tsx`. React -Router v7 framework mode runs its own server — mounting the runtime as a -loader+action in a resource route is the canonical pattern. Do NOT spin up -a sidecar Express or Hono server. - -```tsx -import type { Route } from "./+types/api.copilotkit.$"; -import { - CopilotRuntime, - createCopilotRuntimeHandler, - InMemoryAgentRunner, - BuiltInAgent, - convertInputToTanStackAI, -} from "@copilotkit/runtime/v2"; -import { chat } from "@tanstack/ai"; -import { openaiText } from "@tanstack/ai-openai"; - -const tanstackAgent = new BuiltInAgent({ - type: "tanstack", - factory: ({ input, abortController }) => { - const { messages, systemPrompts } = convertInputToTanStackAI(input); - return chat({ - adapter: openaiText("gpt-4o"), - messages, - systemPrompts, - abortController, - }); - }, -}); - -const runtime = new CopilotRuntime({ - agents: { default: tanstackAgent }, - runner: new InMemoryAgentRunner(), -}); - -const handler = createCopilotRuntimeHandler({ - runtime, - basePath: "/api/copilotkit", -}); - -export async function loader({ request }: Route.LoaderArgs) { - return handler(request); -} -export async function action({ request }: Route.ActionArgs) { - return handler(request); -} -``` - -### Step 3 — Provider + chat + tool (`app/routes/_index.tsx`) - -```tsx -import { useState } from "react"; -import { - CopilotKitProvider, - CopilotChat, - useFrontendTool, -} from "@copilotkit/react-core/v2"; -import "@copilotkit/react-core/v2/styles.css"; -import { z } from "zod"; - -function RegisterTools() { - useFrontendTool({ - name: "getCurrentLocation", - description: "Return the user's current location name.", - parameters: z.object({}), - handler: async () => ({ city: "San Francisco", country: "US" }), - }); - return null; -} - -export default function Index() { - return ( - - -
- -
-
- ); -} -``` - -That's the quickstart. Run `pnpm dev`; visit the app; the chat connects to -`/api/copilotkit/info`, the agent runs, the tool fires. - -## Core Patterns - -### TanStack Start branch - -No dedicated helper — mount `createCopilotRuntimeHandler` in a Start server -route's Request handler. - -```ts -// app/routes/api/copilotkit.$.ts -import { createAPIFileRoute } from "@tanstack/react-start/api"; -import { - CopilotRuntime, - createCopilotRuntimeHandler, - BuiltInAgent, - convertInputToTanStackAI, -} from "@copilotkit/runtime/v2"; -import { chat } from "@tanstack/ai"; -import { openaiText } from "@tanstack/ai-openai"; - -const runtime = new CopilotRuntime({ - agents: { - default: new BuiltInAgent({ - type: "tanstack", - factory: ({ input, abortController }) => { - const { messages, systemPrompts } = convertInputToTanStackAI(input); - return chat({ - adapter: openaiText("gpt-4o"), - messages, - systemPrompts, - abortController, - }); - }, - }), - }, -}); - -const handler = createCopilotRuntimeHandler({ - runtime, - basePath: "/api/copilotkit", -}); - -export const APIRoute = createAPIFileRoute("/api/copilotkit/$")({ - GET: ({ request }) => handler(request), - POST: ({ request }) => handler(request), -}); -``` - -### Next.js App Router branch - -```ts -// app/api/copilotkit/[[...slug]]/route.ts -// -// Optional catch-all ([[...slug]]) so the bare /api/copilotkit basePath -// and every sub-path (/info, /agent/*/run, /threads, etc.) all route to -// this handler. A non-optional [...slug] 404s the bare basePath. -import { - CopilotRuntime, - createCopilotRuntimeHandler, - BuiltInAgent, - convertInputToTanStackAI, -} from "@copilotkit/runtime/v2"; -import { chat } from "@tanstack/ai"; -import { openaiText } from "@tanstack/ai-openai"; - -const runtime = new CopilotRuntime({ - agents: { - default: new BuiltInAgent({ - type: "tanstack", - factory: ({ input, abortController }) => { - const { messages, systemPrompts } = convertInputToTanStackAI(input); - return chat({ - adapter: openaiText("gpt-4o"), - messages, - systemPrompts, - abortController, - }); - }, - }), - }, -}); - -const handler = createCopilotRuntimeHandler({ - runtime, - basePath: "/api/copilotkit", -}); - -export const GET = handler; -export const POST = handler; -``` - -### Cloudflare Workers branch (edge runtime — env binding for secrets) - -Hoist the runtime + handler to module scope and construct them lazily on -first request. Workers isolates reuse module globals across requests, so -a `let`-cached instance persists in-memory runner state within the isolate -(this does NOT span isolates — for durable cross-isolate state, pair with -`SqliteAgentRunner` or Intelligence). Constructing `new CopilotRuntime(...)` -inside `fetch(request, env)` on every call wastes CPU and throws away the -in-memory thread state. - -```ts -import { - CopilotRuntime, - createCopilotRuntimeHandler, - BuiltInAgent, -} from "@copilotkit/runtime/v2"; - -interface Env { - OPENAI_API_KEY: string; -} - -// Module-scoped cache. `env` arrives per-request, so we initialize lazily -// the first time we see it. Subsequent requests in the same isolate reuse. -let cachedHandler: ((request: Request) => Response | Promise) | null = - null; - -function getHandler(env: Env) { - if (cachedHandler) return cachedHandler; - const runtime = new CopilotRuntime({ - agents: { - // Simple Mode: thread the API key through the `apiKey` option — on - // Workers `process.env` is undefined, so BuiltInAgent's env-var - // fallback never fires. Wire env.OPENAI_API_KEY explicitly. - default: new BuiltInAgent({ - model: "openai/gpt-4o", - apiKey: env.OPENAI_API_KEY, - }), - }, - }); - cachedHandler = createCopilotRuntimeHandler({ - runtime, - basePath: "/api/copilotkit", - cors: true, - }); - return cachedHandler; -} - -export default { - fetch(request: Request, env: Env) { - return getHandler(env)(request); - }, -}; -``` - -### SPA-without-runtime branch (no server) - -Point the provider at CopilotKit Cloud via `publicApiKey` — no backend, -no `runtimeUrl`. This is the ONLY production-safe SPA path. See -`copilotkit/spa-without-runtime` for the full treatment. - -```tsx -import { CopilotKitProvider, CopilotChat } from "@copilotkit/react-core/v2"; -import "@copilotkit/react-core/v2/styles.css"; - -export default function App() { - return ( - - - - ); -} -``` - -## Common Mistakes - -### CRITICAL Express or Hono sidecar when on React Router v7 framework mode - -Wrong: - -```ts -// server.js — spun up alongside the RR v7 app -import express from "express"; -import { createCopilotExpressHandler } from "@copilotkit/runtime/v2/express"; -const app = express(); -app.use( - "/api/copilotkit", - createCopilotExpressHandler({ runtime, basePath: "/api/copilotkit" }), -); -app.listen(3001); -``` - -Correct: - -```tsx -// app/routes/api.copilotkit.$.tsx -export async function loader({ request }: Route.LoaderArgs) { - return handler(request); -} -export async function action({ request }: Route.ActionArgs) { - return handler(request); -} -``` - -RR v7 framework mode already runs its own server; a sidecar Express/Hono app -duplicates servers and breaks unified routing/SSR. Same principle applies to -Next.js (use `route.ts`) and TanStack Start (use an APIRoute). Maintainer -guidance: avoid the Express/Hono adapters. - -Source: examples/v2/react-router/app/routes/api.copilotkit.$.tsx - -### CRITICAL using @copilotkitnext/ scope for non-Angular packages - -Wrong: - -```ts -import { CopilotKitProvider } from "@copilotkitnext/react-core"; -import { CopilotRuntime } from "@copilotkitnext/runtime"; -``` - -Correct: - -```ts -import { CopilotKitProvider } from "@copilotkit/react-core/v2"; -import { CopilotRuntime } from "@copilotkit/runtime/v2"; -// Only Angular uses the @copilotkitnext/ scope: -// import { ... } from "@copilotkitnext/angular"; -``` - -Every CopilotKit package except Angular uses `@copilotkit/`. Agents -over-generalize from the Angular example and hallucinate the scope for -react-core / runtime / etc. - -Source: packages/angular/package.json; all other packages/\*/package.json - -### HIGH missing leading slash in runtimeUrl - -Wrong: - -```tsx - -``` - -Correct: - -```tsx - -``` - -Without the leading slash the URL resolves relative to the current page — -breaks on any nested route. - -Source: docs/snippets/shared/troubleshooting/common-issues.mdx:38-42 - -### HIGH forgetting the styles.css import - -Wrong: - -```tsx -import { CopilotChat } from "@copilotkit/react-core/v2"; -``` - -Correct: - -```tsx -import { CopilotChat } from "@copilotkit/react-core/v2"; -import "@copilotkit/react-core/v2/styles.css"; -``` - -Without the stylesheet, the chat renders unstyled/broken — no layout, no -spacing, no theme. - -Source: examples/v2/react-router/app/routes/\_index.tsx:3 - -### HIGH agentId mismatch between client and server - -Wrong: - -```ts -// server -new CopilotRuntime({ agents: { default: agent } }); -// client - -``` - -Correct: - -```tsx - -// or rename the server key to "main" so both sides match -``` - -Mismatched IDs surface as `agent_not_found` on first run. Keep the string -identical on both sides. - -Source: packages/core/src/core/core.ts:80 - -### HIGH reading process.env on Cloudflare Workers - -Wrong: - -```ts -// Module-scoped — `process.env` is undefined on Workers: -const agent = new BuiltInAgent({ - type: "tanstack", - factory: ({ input, abortController }) => - chat({ - adapter: openaiText("gpt-4o"), // no access to process.env.OPENAI_API_KEY - messages: convertInputToTanStackAI(input).messages, - abortController, - }), -}); -``` - -Correct: use Simple Mode and let the runtime read `OPENAI_API_KEY` from -the `env` binding (see the Cloudflare Workers branch above), or thread -`env.OPENAI_API_KEY` in through a closure if you genuinely need Factory -Mode. - -Workers don't expose `process.env`. Secrets arrive via the `env` binding -argument to `fetch(request, env)`. - -Source: examples/v2/runtime/cf-workers/src/index.ts:7-17 - -### HIGH raw Node http with createCopilotRuntimeHandler - -Wrong: - -```ts -const h = createCopilotRuntimeHandler({ runtime }); -server.on("request", h); -``` - -Correct: - -```ts -import { createCopilotNodeHandler } from "@copilotkit/runtime/v2/node"; - -const node = createCopilotNodeHandler( - createCopilotRuntimeHandler({ - runtime, - basePath: "/api/copilotkit", - cors: true, - }), -); -server.on("request", node); -``` - -`createCopilotRuntimeHandler` takes a Web `Request`; Node's -`IncomingMessage` shape is different. `createCopilotNodeHandler` adapts the -fetch handler for `http.Server` — for frameworks (RR v7 / Start / Next.js) -use the fetch handler directly. - -Source: examples/v2/runtime/node/src/index.ts:1-21 diff --git a/skills/copilotkit-agui/SKILL.md b/skills/copilotkit-agui/SKILL.md new file mode 100644 index 00000000000..469a26b054e --- /dev/null +++ b/skills/copilotkit-agui/SKILL.md @@ -0,0 +1,91 @@ +--- +name: copilotkit-agui +description: "Use when building custom agent backends, implementing the AG-UI protocol, debugging streaming issues, or understanding how agents communicate with frontends. Covers event types, SSE transport, AbstractAgent/HttpAgent patterns, state synchronization, tool calls, and human-in-the-loop flows." +version: 1.0.0 +--- + +# AG-UI Protocol Skill + +## Overview + +AG-UI (Agent-User Interaction) is CopilotKit's open event-based protocol for agent-to-UI communication. All agent-frontend interaction flows through typed events streamed over SSE (Server-Sent Events) or binary protobuf transport. Agents implement `AbstractAgent.run()` returning an RxJS `Observable`, and the client SDK handles event application, state management, and message history. + +## When to Use + +- Building a custom agent backend that needs to speak AG-UI +- Implementing `AbstractAgent.run()` for a new framework integration +- Debugging why events aren't reaching the frontend or arriving malformed +- Understanding event ordering (lifecycle, text, tool calls, state) +- Working with state synchronization (snapshots vs JSON Patch deltas) +- Implementing human-in-the-loop interrupt/resume flows +- Troubleshooting SSE streaming or encoding issues + +## When NOT to Use + +- For CopilotKit React hooks and frontend components, use `copilotkit-develop` +- For CopilotKit runtime setup and configuration, use `copilotkit-setup` +- For framework-specific integration guides (LangGraph, Mastra, CrewAI), use `copilotkit-integrations` + +## Quick Reference + +### Event Families + +| Family | Events | Purpose | +|--------|--------|---------| +| Lifecycle | `RUN_STARTED`, `RUN_FINISHED`, `RUN_ERROR`, `STEP_STARTED`, `STEP_FINISHED` | Run boundaries and progress | +| Text | `TEXT_MESSAGE_START`, `TEXT_MESSAGE_CONTENT`, `TEXT_MESSAGE_END` | Streaming text messages | +| Tool Calls | `TOOL_CALL_START`, `TOOL_CALL_ARGS`, `TOOL_CALL_END`, `TOOL_CALL_RESULT` | Agent tool invocations | +| State | `STATE_SNAPSHOT`, `STATE_DELTA`, `MESSAGES_SNAPSHOT` | State synchronization | +| Reasoning | `REASONING_START`, `REASONING_MESSAGE_START/CONTENT/END`, `REASONING_END`, `REASONING_ENCRYPTED_VALUE` | Chain-of-thought visibility | +| Activity | `ACTIVITY_SNAPSHOT`, `ACTIVITY_DELTA` | Structured progress updates | +| Custom | `RAW`, `CUSTOM` | Extension points | + +### Convenience Chunk Events + +`TEXT_MESSAGE_CHUNK` and `TOOL_CALL_CHUNK` auto-expand into Start/Content/End triads via the client's `transformChunks` pipeline. Use these for simpler backend implementations. + +### SSE Wire Format + +Each event is a JSON object sent as an SSE data line: + +``` +data: {"type":"RUN_STARTED","threadId":"t1","runId":"r1"}\n\n +data: {"type":"TEXT_MESSAGE_START","messageId":"m1","role":"assistant"}\n\n +data: {"type":"TEXT_MESSAGE_CONTENT","messageId":"m1","delta":"Hello"}\n\n +data: {"type":"TEXT_MESSAGE_END","messageId":"m1"}\n\n +data: {"type":"RUN_FINISHED","threadId":"t1","runId":"r1"}\n\n +``` + +### Packages + +| Package | npm | Purpose | +|---------|-----|---------| +| `@ag-ui/core` | Events, types, schemas | Protocol definition | +| `@ag-ui/client` | AbstractAgent, HttpAgent, middleware, event application | Client SDK | +| `@ag-ui/encoder` | EventEncoder (SSE + protobuf) | Server-side encoding | + +## Workflow: Building an AG-UI Backend + +1. **Define your endpoint** -- Accept POST with `RunAgentInput` body, respond with `text/event-stream` +2. **Parse input** -- Extract `threadId`, `runId`, `messages`, `tools`, `state`, `context` from the request body +3. **Emit events in order** -- `RUN_STARTED` first, then content events, then `RUN_FINISHED` or `RUN_ERROR` +4. **Encode as SSE** -- Use `@ag-ui/encoder`'s `EventEncoder.encode()` or manually write `data: JSON\n\n` +5. **Handle tool results** -- Client sends `TOOL_CALL_RESULT` back; agent processes and continues + +See `references/building-agents.md` for a complete working example. + +## Key Protocol Rules + +- Every run MUST start with `RUN_STARTED` and end with `RUN_FINISHED` or `RUN_ERROR` +- `TEXT_MESSAGE_CONTENT.delta` must be non-empty +- Tool call events are linked by `toolCallId` +- `STATE_DELTA` uses RFC 6902 JSON Patch operations +- Multiple sequential runs are supported -- each must complete before the next starts +- Messages accumulate across runs; state continues unless reset by `STATE_SNAPSHOT` + +## References + +- `references/protocol-spec.md` -- Complete event type reference with schemas and examples +- `references/building-agents.md` -- Step-by-step guide to building AG-UI backends +- `references/event-flow-diagrams.md` -- ASCII sequence diagrams for common flows +- `references/client-sdk.md` -- @ag-ui/client API reference diff --git a/skills/copilotkit-agui/references/building-agents.md b/skills/copilotkit-agui/references/building-agents.md new file mode 100644 index 00000000000..9f41a40077e --- /dev/null +++ b/skills/copilotkit-agui/references/building-agents.md @@ -0,0 +1,423 @@ +# Building AG-UI Agent Backends + +Step-by-step guide to building an agent backend that speaks the AG-UI protocol. + +## Architecture + +``` +Client (Browser) Agent Backend (Server) + HttpAgent HTTP Endpoint + | | + |-- POST /agent (RunAgentInput) ----->| + | | + |<---- SSE: RUN_STARTED -------------| + |<---- SSE: TEXT_MESSAGE_START -------| + |<---- SSE: TEXT_MESSAGE_CONTENT -----| + |<---- SSE: TEXT_MESSAGE_CONTENT -----| + |<---- SSE: TEXT_MESSAGE_END ---------| + |<---- SSE: RUN_FINISHED ------------| +``` + +The agent receives a POST request with `RunAgentInput` (JSON body containing `threadId`, `runId`, `messages`, `tools`, `state`, `context`, `forwardedProps`), and responds with a stream of SSE-encoded events. + +## Step 1: Extend AbstractAgent (TypeScript In-Process) + +For agents that run in the same process as the client (e.g., testing, serverless): + +```typescript +import { AbstractAgent } from "@ag-ui/client"; +import { RunAgentInput, BaseEvent, EventType } from "@ag-ui/core"; +import { Observable } from "rxjs"; + +class MyAgent extends AbstractAgent { + run(input: RunAgentInput): Observable { + return new Observable((observer) => { + const { threadId, runId, messages } = input; + + // 1. Always start with RUN_STARTED + observer.next({ + type: EventType.RUN_STARTED, + threadId, + runId, + }); + + // 2. Emit content events + const messageId = `msg-${Date.now()}`; + + observer.next({ + type: EventType.TEXT_MESSAGE_START, + messageId, + role: "assistant", + }); + + // Stream text in chunks + const response = "Hello! I received your message."; + for (const char of response) { + observer.next({ + type: EventType.TEXT_MESSAGE_CONTENT, + messageId, + delta: char, + }); + } + + observer.next({ + type: EventType.TEXT_MESSAGE_END, + messageId, + }); + + // 3. Always end with RUN_FINISHED or RUN_ERROR + observer.next({ + type: EventType.RUN_FINISHED, + threadId, + runId, + }); + + observer.complete(); + }); + } +} +``` + +## Step 2: Constructing Events + +Emit events as plain objects with the `type` field set to the appropriate `EventType` enum value: + +```typescript +import { EventType } from "@ag-ui/core"; + +// Events are plain objects — no factory functions needed +const event = { + type: EventType.TEXT_MESSAGE_CONTENT, + messageId: "msg-1", + delta: "Hello", +}; +``` + +The event schemas are defined as Zod types in `@ag-ui/core` (e.g., `TextMessageContentEventSchema`) and can be used for validation if needed, but emitting plain objects is the standard pattern. + +## Step 3: Expose as HTTP SSE Endpoint + +For a standalone HTTP agent backend: + +```typescript +import { EventEncoder } from "@ag-ui/encoder"; +import { RunAgentInput, EventType } from "@ag-ui/core"; + +// Express example +app.post("/agent", async (req, res) => { + const input: RunAgentInput = req.body; + const encoder = new EventEncoder({ accept: req.headers.accept }); + + // Set SSE headers + res.setHeader("Content-Type", encoder.getContentType()); + res.setHeader("Cache-Control", "no-cache"); + res.setHeader("Connection", "keep-alive"); + res.setHeader("Transfer-Encoding", "chunked"); + + // Helper to emit events + const emit = (event: any) => { + res.write(encoder.encode(event)); + }; + + try { + // 1. RUN_STARTED + emit({ + type: EventType.RUN_STARTED, + threadId: input.threadId, + runId: input.runId, + }); + + // 2. Process messages and generate response + const messageId = `msg-${Date.now()}`; + + emit({ + type: EventType.TEXT_MESSAGE_START, + messageId, + role: "assistant", + }); + + // Stream response chunks (e.g., from LLM) + for await (const chunk of generateResponse(input)) { + emit({ + type: EventType.TEXT_MESSAGE_CONTENT, + messageId, + delta: chunk, + }); + } + + emit({ + type: EventType.TEXT_MESSAGE_END, + messageId, + }); + + // 3. RUN_FINISHED + emit({ + type: EventType.RUN_FINISHED, + threadId: input.threadId, + runId: input.runId, + }); + } catch (error) { + emit({ + type: EventType.RUN_ERROR, + message: error.message, + code: "internal_error", + }); + } + + res.end(); +}); +``` + +## Step 4: Handle Tool Calls + +When your agent needs the frontend to execute a tool: + +```typescript +// Agent emits tool call events +emit({ + type: EventType.TOOL_CALL_START, + toolCallId: "tc-1", + toolCallName: "getUserLocation", + parentMessageId: messageId, // Optional: link to parent message +}); + +emit({ + type: EventType.TOOL_CALL_ARGS, + toolCallId: "tc-1", + delta: JSON.stringify({ userId: "user-123" }), +}); + +emit({ + type: EventType.TOOL_CALL_END, + toolCallId: "tc-1", +}); + +// The client executes the tool and sends the result +// In CopilotKit, this happens via useFrontendTool hook +// The result arrives as a TOOL_CALL_RESULT in the next run's messages: +// { role: "tool", toolCallId: "tc-1", content: "{\"lat\": 40.7, \"lng\": -74.0}" } +``` + +**Tool call flow:** The agent emits `TOOL_CALL_START/ARGS/END`, then typically emits `RUN_FINISHED`. The client executes the tool, adds the result to messages, and starts a new run. The agent sees the tool result in `input.messages` and continues. + +## Step 5: Emit State Updates + +Synchronize agent state with the frontend: + +```typescript +// Full state snapshot (replaces all client state) +emit({ + type: EventType.STATE_SNAPSHOT, + snapshot: { + plan: ["Step 1: Research", "Step 2: Draft", "Step 3: Review"], + currentStep: 0, + progress: 0, + }, +}); + +// Incremental updates via JSON Patch (RFC 6902) +emit({ + type: EventType.STATE_DELTA, + delta: [ + { op: "replace", path: "/currentStep", value: 1 }, + { op: "replace", path: "/progress", value: 0.33 }, + ], +}); +``` + +## Step 6: Emit Activity Updates + +For structured progress that appears between chat messages: + +```typescript +// Create activity +emit({ + type: EventType.ACTIVITY_SNAPSHOT, + messageId: "activity-search", + activityType: "SEARCH", + content: { + query: "CopilotKit documentation", + results: [], + status: "in_progress", + }, +}); + +// Update activity incrementally +emit({ + type: EventType.ACTIVITY_DELTA, + messageId: "activity-search", + activityType: "SEARCH", + patch: [ + { op: "replace", path: "/status", value: "complete" }, + { op: "add", path: "/results/-", value: { title: "Getting Started", url: "..." } }, + ], +}); +``` + +## Step 7: Report Steps (Optional) + +Show granular progress within a run: + +```typescript +emit({ type: EventType.STEP_STARTED, stepName: "planning" }); +// ... do planning work, emit text/tool events ... +emit({ type: EventType.STEP_FINISHED, stepName: "planning" }); + +emit({ type: EventType.STEP_STARTED, stepName: "execution" }); +// ... do execution work ... +emit({ type: EventType.STEP_FINISHED, stepName: "execution" }); +``` + +## Complete Working Example + +A minimal but complete agent that echoes messages and handles tools: + +```typescript +import express from "express"; +import { EventEncoder } from "@ag-ui/encoder"; +import { RunAgentInput, EventType } from "@ag-ui/core"; + +const app = express(); +app.use(express.json()); + +app.post("/agent", async (req, res) => { + const input: RunAgentInput = req.body; + const encoder = new EventEncoder({ accept: req.headers.accept }); + + res.setHeader("Content-Type", encoder.getContentType()); + res.setHeader("Cache-Control", "no-cache"); + res.setHeader("Connection", "keep-alive"); + + const emit = (event: any) => res.write(encoder.encode(event)); + + // RUN_STARTED + emit({ + type: EventType.RUN_STARTED, + threadId: input.threadId, + runId: input.runId, + }); + + // Check if last message has a tool result we need to process + const lastMessage = input.messages[input.messages.length - 1]; + const isToolResult = lastMessage?.role === "tool"; + + if (isToolResult) { + // Continue after tool execution + const msgId = `msg-${Date.now()}`; + emit({ type: EventType.TEXT_MESSAGE_START, messageId: msgId, role: "assistant" }); + emit({ + type: EventType.TEXT_MESSAGE_CONTENT, + messageId: msgId, + delta: `Tool returned: ${lastMessage.content}`, + }); + emit({ type: EventType.TEXT_MESSAGE_END, messageId: msgId }); + } else { + // Check if any tools are available + const hasTools = input.tools.length > 0; + const userMessage = input.messages.filter((m) => m.role === "user").pop(); + + if (hasTools && userMessage) { + // Demonstrate tool calling + const tool = input.tools[0]; + emit({ + type: EventType.TOOL_CALL_START, + toolCallId: `tc-${Date.now()}`, + toolCallName: tool.name, + }); + emit({ + type: EventType.TOOL_CALL_ARGS, + toolCallId: `tc-${Date.now()}`, + delta: "{}", + }); + emit({ + type: EventType.TOOL_CALL_END, + toolCallId: `tc-${Date.now()}`, + }); + } else { + // Simple echo response + const msgId = `msg-${Date.now()}`; + emit({ type: EventType.TEXT_MESSAGE_START, messageId: msgId, role: "assistant" }); + + const content = userMessage?.content || "No message received"; + const text = typeof content === "string" ? content : "[multimodal content]"; + const response = `You said: "${text}"`; + + // Stream character by character for demonstration + for (const char of response) { + emit({ type: EventType.TEXT_MESSAGE_CONTENT, messageId: msgId, delta: char }); + } + + emit({ type: EventType.TEXT_MESSAGE_END, messageId: msgId }); + } + } + + // RUN_FINISHED + emit({ + type: EventType.RUN_FINISHED, + threadId: input.threadId, + runId: input.runId, + }); + + res.end(); +}); + +app.listen(3001, () => console.log("AG-UI agent running on :3001")); +``` + +## Connecting from the Client + +```typescript +import { HttpAgent } from "@ag-ui/client"; + +const agent = new HttpAgent({ + url: "http://localhost:3001/agent", + headers: { Authorization: "Bearer token" }, + initialMessages: [ + { id: "1", role: "user", content: "Hello!" }, + ], +}); + +// Run the agent +const { result, newMessages } = await agent.runAgent({ + tools: [ + { + name: "getWeather", + description: "Get current weather", + parameters: { type: "object", properties: { city: { type: "string" } } }, + }, + ], +}); + +console.log("New messages:", newMessages); +console.log("Agent state:", agent.state); +``` + +## Error Handling + +Always emit `RUN_ERROR` on failure so the client knows the run terminated: + +```typescript +try { + // ... agent logic ... +} catch (error) { + emit({ + type: EventType.RUN_ERROR, + message: error instanceof Error ? error.message : String(error), + code: "internal_error", + }); +} +``` + +The client SDK also handles HTTP-level errors and abort signals, converting them to `RUN_ERROR` events automatically. + +## Event Ordering Rules + +1. `RUN_STARTED` must be the first event +2. `RUN_FINISHED` or `RUN_ERROR` must be the last event +3. `TEXT_MESSAGE_START` must precede `TEXT_MESSAGE_CONTENT` for the same `messageId` +4. `TEXT_MESSAGE_END` must follow all `TEXT_MESSAGE_CONTENT` for the same `messageId` +5. `TOOL_CALL_START` must precede `TOOL_CALL_ARGS` for the same `toolCallId` +6. `TOOL_CALL_END` must follow all `TOOL_CALL_ARGS` for the same `toolCallId` +7. `STEP_STARTED` and `STEP_FINISHED` must be properly paired +8. `STATE_SNAPSHOT` replaces all state; `STATE_DELTA` patches existing state +9. Multiple runs are supported sequentially: each `RUN_FINISHED` before the next `RUN_STARTED` diff --git a/skills/copilotkit-agui/references/client-sdk.md b/skills/copilotkit-agui/references/client-sdk.md new file mode 100644 index 00000000000..aca991524b1 --- /dev/null +++ b/skills/copilotkit-agui/references/client-sdk.md @@ -0,0 +1,574 @@ +# @ag-ui/client SDK Reference + +API reference for the AG-UI client SDK (`@ag-ui/client`). + +## Package Exports + +The client re-exports everything from `@ag-ui/core`, so you typically only need one import: + +```typescript +import { + // Agent classes + AbstractAgent, + HttpAgent, + // Types from @ag-ui/core + EventType, + BaseEvent, + RunAgentInput, + Message, + // Middleware + Middleware, + FilterToolCallsMiddleware, + // Event application + defaultApplyEvents, + // Verification + verifyEvents, + // Transforms + transformChunks, + transformHttpEventStream, + // Compact utilities + compactEvents, +} from "@ag-ui/client"; +``` + +--- + +## AbstractAgent + +Base class for all AG-UI agents. Manages conversation state, message history, event processing, and subscriber notification. + +### Constructor + +```typescript +interface AgentConfig { + agentId?: string; // Unique agent identifier + description?: string; // Human-readable description + threadId?: string; // Conversation thread ID (auto-generated if omitted) + initialMessages?: Message[]; // Starting message history + initialState?: State; // Starting state object + debug?: boolean; // Enable debug logging +} + +const agent = new MyAgent({ + agentId: "my-agent", + threadId: "thread-1", + initialMessages: [{ id: "1", role: "user", content: "Hello" }], + initialState: { preference: "dark" }, + debug: true, +}); +``` + +### Properties + +| Property | Type | Description | +|----------|------|-------------| +| `agentId` | `string?` | Agent identifier | +| `description` | `string` | Human-readable description | +| `threadId` | `string` | Conversation thread ID | +| `messages` | `Message[]` | Current message history | +| `state` | `State` | Current agent state | +| `debug` | `boolean` | Debug logging enabled | +| `isRunning` | `boolean` | Whether a run is currently active | +| `subscribers` | `AgentSubscriber[]` | Registered event subscribers | + +### Abstract Method: `run()` + +Must be implemented by subclasses. Returns an RxJS Observable of AG-UI events. + +```typescript +abstract run(input: RunAgentInput): Observable; +``` + +### `runAgent(parameters?, subscriber?)` + +Executes a full agent run with event application, state management, and subscriber notification. + +```typescript +interface RunAgentParameters { + runId?: string; + tools?: Tool[]; + context?: Context[]; + forwardedProps?: any; +} + +interface RunAgentResult { + result: any; // From RUN_FINISHED.result + newMessages: Message[]; // Messages added during this run +} + +const { result, newMessages } = await agent.runAgent({ + runId: "run-1", + tools: [{ name: "search", description: "Search docs", parameters: {} }], + context: [{ description: "Current page", value: "/dashboard" }], + forwardedProps: { model: "gpt-4" }, +}); +``` + +The pipeline internally: +1. Prepares `RunAgentInput` from current state + parameters +2. Calls `run(input)` to get the event Observable +3. Passes through middleware chain +4. Transforms chunk events into full events (`transformChunks`) +5. Verifies event ordering (`verifyEvents`) +6. Applies events to update messages/state (`defaultApplyEvents`) +7. Notifies subscribers at each step + +### `connectAgent(parameters?, subscriber?)` + +Like `runAgent()` but calls the protected `connect()` method instead of `run()`. Used for persistent connections (WebSocket). + +### `detachActiveRun()` + +Immediately stops processing the current run's event stream. The run's Observable is unsubscribed and the finalize handler runs. + +```typescript +await agent.detachActiveRun(); +``` + +### `abortRun()` + +Aborts the current run. For `HttpAgent`, this calls `AbortController.abort()`. + +### `subscribe(subscriber)` + +Registers an event subscriber. Returns an object with `unsubscribe()`. + +```typescript +const subscription = agent.subscribe({ + onTextMessageContentEvent: ({ event, textMessageBuffer }) => { + console.log("Streaming:", textMessageBuffer + event.delta); + }, + onRunFinishedEvent: ({ result }) => { + console.log("Done:", result); + }, +}); + +// Later: +subscription.unsubscribe(); +``` + +### `use(...middlewares)` + +Adds middleware to the agent's processing pipeline. Middlewares run in order, wrapping the `run()` call. + +```typescript +agent.use(new FilterToolCallsMiddleware(["allowedTool"])); +agent.use((input, next) => { + // Modify input before passing to next + return next.run(input); +}); +``` + +### `addMessage(message)` / `addMessages(messages)` + +Adds messages and notifies subscribers (`onNewMessage`, `onNewToolCall`, `onMessagesChanged`). + +### `setMessages(messages)` / `setState(state)` + +Replaces messages/state and notifies subscribers. + +### `clone()` + +Creates a deep copy of the agent with the same configuration, messages, state, and middleware. + +### `getCapabilities()` + +Optional method that subclasses can implement to advertise supported capabilities: + +```typescript +async getCapabilities(): Promise { + return { + identity: { name: "My Agent", type: "custom", version: "1.0.0" }, + transport: { streaming: true }, + tools: { supported: true, clientProvided: true }, + state: { snapshots: true, deltas: true }, + humanInTheLoop: { supported: true, approvals: true }, + }; +} +``` + +--- + +## HttpAgent + +Concrete agent that connects to a remote HTTP endpoint. Extends `AbstractAgent`. + +### Constructor + +```typescript +interface HttpAgentConfig extends AgentConfig { + url: string; // Agent endpoint URL + headers?: Record; // Custom HTTP headers +} + +const agent = new HttpAgent({ + url: "https://api.example.com/agent", + headers: { + Authorization: "Bearer sk-...", + "X-Custom-Header": "value", + }, + threadId: "thread-1", +}); +``` + +### How It Works + +1. `run()` sends a POST request to `url` with `RunAgentInput` as JSON body +2. Request headers include `Content-Type: application/json` and `Accept: text/event-stream` +3. Response stream is parsed as SSE (or protobuf if content-type matches) +4. Each SSE `data:` line is parsed through `EventSchemas` (Zod discriminated union) + +### Properties + +| Property | Type | Description | +|----------|------|-------------| +| `url` | `string` | Agent endpoint URL | +| `headers` | `Record` | Custom request headers | +| `abortController` | `AbortController` | Controls request cancellation | + +### `requestInit(input)` + +Protected method that builds the `RequestInit` for `fetch()`. Override for custom request behavior: + +```typescript +class CustomHttpAgent extends HttpAgent { + protected requestInit(input: RunAgentInput): RequestInit { + return { + method: "POST", + headers: { + ...this.headers, + "Content-Type": "application/json", + Accept: "text/event-stream", + "X-Request-Id": input.runId, + }, + body: JSON.stringify(input), + signal: this.abortController.signal, + }; + } +} +``` + +### `abortRun()` + +Aborts the HTTP request via `AbortController.abort()`. The client auto-generates a `RUN_ERROR` event with `code: "abort"`. + +--- + +## AgentSubscriber + +Interface for receiving typed event callbacks during agent runs. All callbacks are optional and can be sync or async. + +### Lifecycle Callbacks + +```typescript +interface AgentSubscriber { + // Before events start flowing + onRunInitialized?(params: AgentSubscriberParams): + MaybePromise | void>; + + // On unrecoverable error + onRunFailed?(params: { error: Error } & AgentSubscriberParams): + MaybePromise | void>; + + // After run completes (success or failure) + onRunFinalized?(params: AgentSubscriberParams): + MaybePromise | void>; +} +``` + +### Event Callbacks + +Each event type has a corresponding callback. Key ones: + +```typescript +interface AgentSubscriber { + // Catch-all for every event + onEvent?(params: { event: BaseEvent } & AgentSubscriberParams): + MaybePromise; + + // Lifecycle events + onRunStartedEvent?(params: { event: RunStartedEvent } & ...): ...; + onRunFinishedEvent?(params: { event: RunFinishedEvent; result?: any } & ...): ...; + onRunErrorEvent?(params: { event: RunErrorEvent } & ...): ...; + onStepStartedEvent?(params: { event: StepStartedEvent } & ...): ...; + onStepFinishedEvent?(params: { event: StepFinishedEvent } & ...): ...; + + // Text message events (includes accumulated buffer) + onTextMessageStartEvent?(params: { event: TextMessageStartEvent } & ...): ...; + onTextMessageContentEvent?(params: { + event: TextMessageContentEvent; + textMessageBuffer: string; // Content accumulated so far + } & ...): ...; + onTextMessageEndEvent?(params: { + event: TextMessageEndEvent; + textMessageBuffer: string; // Complete message content + } & ...): ...; + + // Tool call events (includes accumulated args) + onToolCallStartEvent?(params: { event: ToolCallStartEvent } & ...): ...; + onToolCallArgsEvent?(params: { + event: ToolCallArgsEvent; + toolCallBuffer: string; // Raw args accumulated + toolCallName: string; // Tool name + partialToolCallArgs: Record; // Best-effort parsed args + } & ...): ...; + onToolCallEndEvent?(params: { + event: ToolCallEndEvent; + toolCallName: string; + toolCallArgs: Record; // Fully parsed args + } & ...): ...; + onToolCallResultEvent?(params: { event: ToolCallResultEvent } & ...): ...; + + // State events + onStateSnapshotEvent?(params: { event: StateSnapshotEvent } & ...): ...; + onStateDeltaEvent?(params: { event: StateDeltaEvent } & ...): ...; + onMessagesSnapshotEvent?(params: { event: MessagesSnapshotEvent } & ...): ...; + + // Activity events + onActivitySnapshotEvent?(params: { + event: ActivitySnapshotEvent; + activityMessage?: ActivityMessage; + existingMessage?: Message; + } & ...): ...; + onActivityDeltaEvent?(params: { + event: ActivityDeltaEvent; + activityMessage?: ActivityMessage; + } & ...): ...; + + // Reasoning events + onReasoningStartEvent?(params: { event: ReasoningStartEvent } & ...): ...; + onReasoningMessageContentEvent?(params: { + event: ReasoningMessageContentEvent; + reasoningMessageBuffer: string; + } & ...): ...; + onReasoningEndEvent?(params: { event: ReasoningEndEvent } & ...): ...; + onReasoningEncryptedValueEvent?(params: { event: ReasoningEncryptedValueEvent } & ...): ...; + + // Custom/raw events + onRawEvent?(params: { event: RawEvent } & ...): ...; + onCustomEvent?(params: { event: CustomEvent } & ...): ...; + + // State change notifications (fires after state/messages update) + onMessagesChanged?(params: Omit & { input?: RunAgentInput }): ...; + onStateChanged?(params: Omit & { input?: RunAgentInput }): ...; + onNewMessage?(params: { message: Message } & ...): ...; + onNewToolCall?(params: { toolCall: ToolCall } & ...): ...; +} +``` + +### AgentStateMutation + +Subscriber callbacks can return mutations to modify agent state: + +```typescript +interface AgentStateMutation { + messages?: Message[]; // Replace messages + state?: State; // Replace state + stopPropagation?: boolean; // Stop processing this event +} +``` + +If `stopPropagation` is `true`, the default event application logic is skipped and no further subscribers see the event. + +--- + +## Middleware + +Middleware intercepts the `run()` call, enabling event transformation, filtering, and augmentation. + +### Abstract Middleware Class + +```typescript +abstract class Middleware { + // Override this to intercept runs + abstract run(input: RunAgentInput, next: AbstractAgent): Observable; + + // Helper: runs next agent with chunk transformation + protected runNext(input: RunAgentInput, next: AbstractAgent): Observable; + + // Helper: runs next agent and tracks state after each event + protected runNextWithState(input: RunAgentInput, next: AbstractAgent): Observable; +} + +interface EventWithState { + event: BaseEvent; + messages: Message[]; // State AFTER event applied + state: any; // State AFTER event applied +} +``` + +### Function Middleware + +Use a plain function instead of a class: + +```typescript +agent.use((input: RunAgentInput, next: AbstractAgent) => { + // Modify input + const modifiedInput = { ...input, forwardedProps: { ...input.forwardedProps, custom: true } }; + // Pass to next agent/middleware + return next.run(modifiedInput); +}); +``` + +### FilterToolCallsMiddleware + +Built-in middleware that filters tool call events to only allowed tool names: + +```typescript +import { FilterToolCallsMiddleware } from "@ag-ui/client"; + +agent.use(new FilterToolCallsMiddleware(["allowedTool1", "allowedTool2"])); +``` + +### Custom Middleware Example + +```typescript +import { Middleware } from "@ag-ui/client"; +import { map } from "rxjs/operators"; + +class LoggingMiddleware extends Middleware { + run(input: RunAgentInput, next: AbstractAgent): Observable { + console.log("Run started with", input.messages.length, "messages"); + return this.runNext(input, next).pipe( + map((event) => { + console.log("Event:", event.type); + return event; + }), + ); + } +} +``` + +### Middleware with State Tracking + +```typescript +class ConditionalMiddleware extends Middleware { + run(input: RunAgentInput, next: AbstractAgent): Observable { + return this.runNextWithState(input, next).pipe( + map(({ event, messages, state }) => { + // Access messages and state AFTER the event was applied + console.log("Messages after event:", messages.length); + console.log("State after event:", state); + return event; + }), + ); + } +} +``` + +--- + +## Event Application (defaultApplyEvents) + +The `defaultApplyEvents` function processes events and updates agent messages/state: + +```typescript +function defaultApplyEvents( + input: RunAgentInput, + events$: Observable, + agent: AbstractAgent, + subscribers: AgentSubscriber[], +): Observable; +``` + +### What It Does Per Event Type + +| Event | Action | +|-------|--------| +| `TEXT_MESSAGE_START` | Creates new message in messages array | +| `TEXT_MESSAGE_CONTENT` | Appends delta to message content | +| `TEXT_MESSAGE_END` | Fires `onNewMessage` subscriber | +| `TOOL_CALL_START` | Creates assistant message with toolCalls array (or adds to existing if parentMessageId matches) | +| `TOOL_CALL_ARGS` | Appends delta to tool call's `function.arguments` | +| `TOOL_CALL_END` | Fires `onNewToolCall` subscriber | +| `TOOL_CALL_RESULT` | Adds tool message to messages | +| `STATE_SNAPSHOT` | Replaces entire state | +| `STATE_DELTA` | Applies JSON Patch operations to state | +| `MESSAGES_SNAPSHOT` | Edit-based merge preserving activity messages | +| `ACTIVITY_SNAPSHOT` | Creates or replaces activity message | +| `ACTIVITY_DELTA` | Applies JSON Patch to activity content | +| `RUN_STARTED` | Adds input.messages if present (new messages only) | +| `REASONING_MESSAGE_START` | Creates reasoning message | +| `REASONING_MESSAGE_CONTENT` | Appends delta to reasoning message | +| `REASONING_ENCRYPTED_VALUE` | Sets encryptedValue on target message or tool call | + +--- + +## Observable Patterns + +AG-UI uses RxJS Observables throughout. Key patterns: + +### Creating Event Streams + +```typescript +import { Observable } from "rxjs"; +import { BaseEvent, EventType } from "@ag-ui/core"; + +// From scratch +const events$ = new Observable((observer) => { + observer.next({ type: EventType.RUN_STARTED, threadId: "t1", runId: "r1" }); + observer.next({ type: EventType.TEXT_MESSAGE_START, messageId: "m1", role: "assistant" }); + observer.next({ type: EventType.TEXT_MESSAGE_CONTENT, messageId: "m1", delta: "Hello" }); + observer.next({ type: EventType.TEXT_MESSAGE_END, messageId: "m1" }); + observer.next({ type: EventType.RUN_FINISHED, threadId: "t1", runId: "r1" }); + observer.complete(); +}); +``` + +### Async Event Streams + +```typescript +const events$ = new Observable((observer) => { + (async () => { + try { + observer.next({ type: EventType.RUN_STARTED, threadId: "t1", runId: "r1" }); + + for await (const chunk of llmStream) { + observer.next({ + type: EventType.TEXT_MESSAGE_CONTENT, + messageId: "m1", + delta: chunk, + }); + } + + observer.next({ type: EventType.RUN_FINISHED, threadId: "t1", runId: "r1" }); + observer.complete(); + } catch (error) { + observer.next({ + type: EventType.RUN_ERROR, + message: error.message, + }); + observer.complete(); + } + })(); +}); +``` + +--- + +## HTTP Transport Internals + +### Request Flow + +1. `HttpAgent.run()` calls `runHttpRequest(url, requestInit)` which returns `Observable` +2. `HttpEvent` is either `HttpHeadersEvent` (status + headers) or `HttpDataEvent` (Uint8Array chunks) +3. `transformHttpEventStream()` examines the content-type header: + - `application/x-ag-ui` -> protobuf parser + - Everything else -> SSE parser (`parseSSEStream`) +4. SSE parser splits on `\n\n`, extracts `data:` lines, parses JSON +5. JSON is validated through `EventSchemas.parse()` (Zod discriminated union) + +### Error Handling + +- Non-2xx HTTP responses throw with status and body payload +- `AbortError` (from `AbortController`) is converted to `RUN_ERROR` with `code: "abort"` +- SSE parse errors propagate as Observable errors + +--- + +## Built-in Backward Compatibility + +The client automatically applies backward-compatibility middleware: + +- **BackwardCompatibility_0_0_39**: Applied for client versions <= 0.0.39 +- **BackwardCompatibility_0_0_45**: Converts deprecated `THINKING_*` events to `REASONING_*` events diff --git a/skills/copilotkit-agui/references/event-flow-diagrams.md b/skills/copilotkit-agui/references/event-flow-diagrams.md new file mode 100644 index 00000000000..a73385c5838 --- /dev/null +++ b/skills/copilotkit-agui/references/event-flow-diagrams.md @@ -0,0 +1,424 @@ +# AG-UI Event Flow Diagrams + +ASCII sequence diagrams showing common AG-UI event flows. + +--- + +## Simple Text Chat + +The minimal flow for a single text response: + +``` +Agent Client + | | + | RUN_STARTED {threadId, runId} | + |--------------------------------------------->| + | | + | TEXT_MESSAGE_START {messageId, role} | + |--------------------------------------------->| // Client creates message bubble + | | + | TEXT_MESSAGE_CONTENT {messageId, delta} | + |--------------------------------------------->| // Client appends "Hello, " + | | + | TEXT_MESSAGE_CONTENT {messageId, delta} | + |--------------------------------------------->| // Client appends "how can " + | | + | TEXT_MESSAGE_CONTENT {messageId, delta} | + |--------------------------------------------->| // Client appends "I help?" + | | + | TEXT_MESSAGE_END {messageId} | + |--------------------------------------------->| // Client finalizes message + | | + | RUN_FINISHED {threadId, runId} | + |--------------------------------------------->| // Client marks run complete + | | +``` + +--- + +## Tool Call Flow + +Agent invokes a frontend tool, client executes it, then agent continues: + +``` +Agent Client + | | + | RUN_STARTED {threadId, runId: "r1"} | + |--------------------------------------------->| + | | + | TEXT_MESSAGE_START {messageId: "m1"} | + |--------------------------------------------->| + | TEXT_MESSAGE_CONTENT {delta: "Let me check"} | + |--------------------------------------------->| + | TEXT_MESSAGE_END {messageId: "m1"} | + |--------------------------------------------->| + | | + | TOOL_CALL_START {toolCallId: "tc1", | + | toolCallName: "getWeather", | + | parentMessageId: "m1"} | + |--------------------------------------------->| // Client shows tool invocation + | | + | TOOL_CALL_ARGS {toolCallId: "tc1", | + | delta: '{"city":"NYC"}'} | + |--------------------------------------------->| // Client builds args progressively + | | + | TOOL_CALL_END {toolCallId: "tc1"} | + |--------------------------------------------->| // Client executes the tool + | | + | RUN_FINISHED {threadId, runId: "r1"} | + |--------------------------------------------->| + | | + | Client executes getWeather() | + | Adds result to messages | + | Starts new run | + | | + | POST /agent (messages include tool result) | + |<---------------------------------------------| + | | + | RUN_STARTED {threadId, runId: "r2"} | + |--------------------------------------------->| + | | + | TEXT_MESSAGE_START {messageId: "m2"} | + |--------------------------------------------->| + | TEXT_MESSAGE_CONTENT {delta: "It's 72F"} | + |--------------------------------------------->| + | TEXT_MESSAGE_END {messageId: "m2"} | + |--------------------------------------------->| + | | + | RUN_FINISHED {threadId, runId: "r2"} | + |--------------------------------------------->| + | | +``` + +### Tool Call with TOOL_CALL_RESULT in Same Run + +When the backend itself executes the tool (server-side tools): + +``` +Agent Client + | | + | RUN_STARTED | + |--------------------------------------------->| + | | + | TOOL_CALL_START {toolCallId: "tc1", | + | toolCallName: "searchDB"} | + |--------------------------------------------->| + | TOOL_CALL_ARGS {delta: '{"q":"orders"}'} | + |--------------------------------------------->| + | TOOL_CALL_END {toolCallId: "tc1"} | + |--------------------------------------------->| + | | + | TOOL_CALL_RESULT {messageId: "tr1", | + | toolCallId: "tc1", | + | content: '{"results":[...]}'} | + |--------------------------------------------->| // Client adds tool message to history + | | + | TEXT_MESSAGE_START {messageId: "m1"} | + |--------------------------------------------->| + | TEXT_MESSAGE_CONTENT {delta: "Found 3..."} | + |--------------------------------------------->| + | TEXT_MESSAGE_END {messageId: "m1"} | + |--------------------------------------------->| + | | + | RUN_FINISHED | + |--------------------------------------------->| + | | +``` + +--- + +## State Synchronization + +### Snapshot + Delta Pattern + +``` +Agent Client + | | + | RUN_STARTED | + |--------------------------------------------->| + | | + | STATE_SNAPSHOT {snapshot: { | + | plan: ["Research","Draft","Review"], | + | step: 0, progress: 0}} | + |--------------------------------------------->| // Client replaces entire state + | | + | STEP_STARTED {stepName: "research"} | + |--------------------------------------------->| + | | + | STATE_DELTA {delta: [ | + | {op:"replace", path:"/step", value:1}, | + | {op:"replace", path:"/progress", | + | value:0.33}]} | + |--------------------------------------------->| // Client patches state + | | + | STEP_FINISHED {stepName: "research"} | + |--------------------------------------------->| + | | + | STATE_DELTA {delta: [ | + | {op:"replace", path:"/step", value:2}, | + | {op:"replace", path:"/progress", | + | value:0.66}]} | + |--------------------------------------------->| // Client patches state again + | | + | RUN_FINISHED | + |--------------------------------------------->| + | | +``` + +### Messages Snapshot + +``` +Agent Client + | | + | RUN_STARTED | + |--------------------------------------------->| + | | + | MESSAGES_SNAPSHOT {messages: [ | + | {id:"1", role:"user", content:"Hi"}, | + | {id:"2", role:"assistant", | + | content:"Hello!"}]} | + |--------------------------------------------->| // Client merges message history + | | // (preserves activity messages, + | | // replaces known messages, + | | // appends new ones) + | RUN_FINISHED | + |--------------------------------------------->| + | | +``` + +--- + +## Activity Updates + +Structured progress displayed between chat messages: + +``` +Agent Client + | | + | RUN_STARTED | + |--------------------------------------------->| + | | + | ACTIVITY_SNAPSHOT { | + | messageId: "act-1", | + | activityType: "SEARCH", | + | content: {query:"docs", | + | results:[], status:"searching"}} | + |--------------------------------------------->| // Client renders search activity + | | + | ACTIVITY_DELTA { | + | messageId: "act-1", | + | activityType: "SEARCH", | + | patch: [ | + | {op:"add", path:"/results/-", | + | value:{title:"Guide"}}, | + | {op:"replace", path:"/status", | + | value:"found 1 result"}]} | + |--------------------------------------------->| // Client patches activity + | | + | ACTIVITY_DELTA { | + | messageId: "act-1", | + | activityType: "SEARCH", | + | patch: [ | + | {op:"replace", path:"/status", | + | value:"complete"}]} | + |--------------------------------------------->| // Client updates status + | | + | TEXT_MESSAGE_START ... | + |--------------------------------------------->| + | ... (response based on search) ... | + | | + | RUN_FINISHED | + |--------------------------------------------->| + | | +``` + +--- + +## Human-in-the-Loop (Interrupt + Resume) + +Agent pauses for human approval, then resumes: + +``` +Agent Client + | | + | POST /agent (initial request) | + |<---------------------------------------------| + | | + | RUN_STARTED {runId: "r1"} | + |--------------------------------------------->| + | | + | TEXT_MESSAGE_START/CONTENT/END | + | ("I'd like to send an email to...") | + |--------------------------------------------->| + | | + | TOOL_CALL_START {toolCallId: "tc1", | + | toolCallName: "confirmAction"} | + |--------------------------------------------->| // Frontend shows approval UI + | TOOL_CALL_ARGS {delta: '{"action": | + | "send email to client@example.com"}'} | + |--------------------------------------------->| + | TOOL_CALL_END {toolCallId: "tc1"} | + |--------------------------------------------->| + | | + | RUN_FINISHED {runId: "r1"} | + |--------------------------------------------->| // Run pauses here + | | + | ... User reviews and approves ... | + | | + | POST /agent (messages now include: | + | tool result: {approved: true}) | + |<---------------------------------------------| // Client resumes with result + | | + | RUN_STARTED {runId: "r2"} | + |--------------------------------------------->| + | | + | TEXT_MESSAGE_START/CONTENT/END | + | ("Email sent successfully!") | + |--------------------------------------------->| + | | + | RUN_FINISHED {runId: "r2"} | + |--------------------------------------------->| + | | +``` + +The interrupt pattern is implemented via tool calls. The agent calls a "confirmation" tool, the client presents the UI, and the tool result (approve/reject) drives the next run. + +--- + +## Error Handling + +### Agent Error + +``` +Agent Client + | | + | RUN_STARTED {threadId, runId} | + |--------------------------------------------->| + | | + | TEXT_MESSAGE_START {messageId: "m1"} | + |--------------------------------------------->| + | TEXT_MESSAGE_CONTENT {delta: "Processing"} | + |--------------------------------------------->| + | | + | RUN_ERROR {message: "Rate limit exceeded", | + | code: "rate_limit"} | + |--------------------------------------------->| // Client shows error, no RUN_FINISHED + | | +``` + +### HTTP-Level Abort + +``` +Agent Client + | | + | RUN_STARTED | + |--------------------------------------------->| + | TEXT_MESSAGE_START ... | + |--------------------------------------------->| + | | + | Client calls agent.abortRun() | + | AbortController.abort() | + | | + | (connection severed) | + | Client auto-generates: | + | RUN_ERROR {message: "Request aborted", | + | code: "abort"} | + | | +``` + +--- + +## Reasoning Flow + +Agent exposes chain-of-thought: + +``` +Agent Client + | | + | RUN_STARTED | + |--------------------------------------------->| + | | + | REASONING_START {messageId: "r1"} | + |--------------------------------------------->| // Client shows thinking indicator + | | + | REASONING_MESSAGE_START {messageId: "rm1", | + | role: "reasoning"} | + |--------------------------------------------->| + | REASONING_MESSAGE_CONTENT {messageId: "rm1", | + | delta: "The user is asking about..."} | + |--------------------------------------------->| // Client streams reasoning text + | REASONING_MESSAGE_CONTENT {messageId: "rm1", | + | delta: " I should check the docs..."} | + |--------------------------------------------->| + | REASONING_MESSAGE_END {messageId: "rm1"} | + |--------------------------------------------->| + | | + | REASONING_END {messageId: "r1"} | + |--------------------------------------------->| // Client hides thinking indicator + | | + | TEXT_MESSAGE_START/CONTENT/END | + | (actual response) | + |--------------------------------------------->| + | | + | RUN_FINISHED | + |--------------------------------------------->| + | | +``` + +### With Encrypted Reasoning (ZDR) + +``` +Agent Client + | | + | REASONING_START {messageId: "r1"} | + |--------------------------------------------->| + | REASONING_MESSAGE_START ... CONTENT ... END | + |--------------------------------------------->| // Visible summary + | REASONING_END {messageId: "r1"} | + |--------------------------------------------->| + | | + | TEXT_MESSAGE_START/CONTENT/END {msgId:"m1"} | + |--------------------------------------------->| + | | + | REASONING_ENCRYPTED_VALUE { | + | subtype: "message", | + | entityId: "m1", | + | encryptedValue: "eyJhbG..."} | + |--------------------------------------------->| // Client stores opaquely, + | | // forwards on next run + | RUN_FINISHED | + |--------------------------------------------->| + | | +``` + +--- + +## Multiple Sequential Runs + +AG-UI supports multiple runs in a single conversation thread. Messages accumulate across runs: + +``` +Agent Client + | | + | POST /agent (messages: [{user: "Hi"}]) | + |<---------------------------------------------| + | | + | RUN_STARTED {runId: "r1"} | + |--------------------------------------------->| + | TEXT_MESSAGE_* ("Hello!") | + |--------------------------------------------->| // messages: [user, assistant] + | RUN_FINISHED {runId: "r1"} | + |--------------------------------------------->| + | | + | POST /agent (messages: [user, assistant, | + | {user: "What's 2+2?"}]) | + |<---------------------------------------------| + | | + | RUN_STARTED {runId: "r2"} | + |--------------------------------------------->| + | TEXT_MESSAGE_* ("4") | + |--------------------------------------------->| // messages: [user, asst, user, asst] + | RUN_FINISHED {runId: "r2"} | + |--------------------------------------------->| + | | +``` diff --git a/skills/copilotkit-agui/references/protocol-spec.md b/skills/copilotkit-agui/references/protocol-spec.md new file mode 100644 index 00000000000..79e5e81f020 --- /dev/null +++ b/skills/copilotkit-agui/references/protocol-spec.md @@ -0,0 +1,681 @@ +# AG-UI Protocol Specification -- Event Type Reference + +Complete reference for all AG-UI event types, derived from `@ag-ui/core` Zod schemas. + +## Base Event Fields + +All events extend `BaseEventSchema` and share these fields: + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `EventType` (string enum) | Yes | Event type discriminator | +| `timestamp` | `number` | No | Unix timestamp (ms) when event was created | +| `rawEvent` | `any` | No | Original event data if transformed from another format | + +Events use `.passthrough()` so additional fields are preserved through parsing. + +--- + +## Lifecycle Events + +### RUN_STARTED + +Emitted first when an agent begins processing. Establishes the run context. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"RUN_STARTED"` | Yes | | +| `threadId` | `string` | Yes | Conversation thread ID | +| `runId` | `string` | Yes | Unique run ID | +| `parentRunId` | `string` | No | Lineage pointer for branching/time travel | +| `input` | `RunAgentInput` | No | The exact agent input payload for this run | + +```json +{ + "type": "RUN_STARTED", + "threadId": "thread-abc", + "runId": "run-123" +} +``` + +### RUN_FINISHED + +Emitted when an agent run completes successfully. No further events for this run after this. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"RUN_FINISHED"` | Yes | | +| `threadId` | `string` | Yes | Conversation thread ID | +| `runId` | `string` | Yes | Run ID matching `RUN_STARTED` | +| `result` | `any` | No | Output data from the run | + +```json +{ + "type": "RUN_FINISHED", + "threadId": "thread-abc", + "runId": "run-123", + "result": { "summary": "Task completed" } +} +``` + +### RUN_ERROR + +Emitted when an agent encounters an unrecoverable error. Terminates the run. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"RUN_ERROR"` | Yes | | +| `message` | `string` | Yes | Error description | +| `code` | `string` | No | Error code (e.g., `"abort"`, `"timeout"`) | + +```json +{ + "type": "RUN_ERROR", + "message": "Model API rate limited", + "code": "rate_limit" +} +``` + +### STEP_STARTED + +Emitted when a named step/phase begins within a run. Optional but recommended for progress visibility. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"STEP_STARTED"` | Yes | | +| `stepName` | `string` | Yes | Name of the step (e.g., node name, function name) | + +```json +{ + "type": "STEP_STARTED", + "stepName": "retrieve_documents" +} +``` + +### STEP_FINISHED + +Emitted when a named step completes. Must match a corresponding `STEP_STARTED`. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"STEP_FINISHED"` | Yes | | +| `stepName` | `string` | Yes | Name of the step | + +```json +{ + "type": "STEP_FINISHED", + "stepName": "retrieve_documents" +} +``` + +--- + +## Text Message Events + +### TEXT_MESSAGE_START + +Begins a new streaming text message. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"TEXT_MESSAGE_START"` | Yes | | +| `messageId` | `string` | Yes | Unique message ID | +| `role` | `"developer" \| "system" \| "assistant" \| "user"` | No | Defaults to `"assistant"` | +| `name` | `string` | No | Optional sender name | + +```json +{ + "type": "TEXT_MESSAGE_START", + "messageId": "msg-1", + "role": "assistant" +} +``` + +### TEXT_MESSAGE_CONTENT + +Delivers a chunk of text content. Multiple events build the complete message. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"TEXT_MESSAGE_CONTENT"` | Yes | | +| `messageId` | `string` | Yes | Must match `TEXT_MESSAGE_START.messageId` | +| `delta` | `string` | Yes | Text chunk (must be non-empty) | + +```json +{ + "type": "TEXT_MESSAGE_CONTENT", + "messageId": "msg-1", + "delta": "Hello, how can " +} +``` + +### TEXT_MESSAGE_END + +Signals that a text message is complete. No more content for this `messageId`. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"TEXT_MESSAGE_END"` | Yes | | +| `messageId` | `string` | Yes | Must match `TEXT_MESSAGE_START.messageId` | + +```json +{ + "type": "TEXT_MESSAGE_END", + "messageId": "msg-1" +} +``` + +### TEXT_MESSAGE_CHUNK (Convenience) + +Auto-expands into `TEXT_MESSAGE_START` / `TEXT_MESSAGE_CONTENT` / `TEXT_MESSAGE_END` via the client's `transformChunks` pipeline. Simplifies backend implementation. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"TEXT_MESSAGE_CHUNK"` | Yes | | +| `messageId` | `string` | No | Required on first chunk; links subsequent chunks | +| `role` | `"developer" \| "system" \| "assistant" \| "user"` | No | Role for the message | +| `delta` | `string` | No | Text content chunk | +| `name` | `string` | No | Optional sender name | + +The client transformer handles lifecycle: +- First chunk with a new `messageId` emits `TEXT_MESSAGE_START` +- Each chunk with `delta` emits `TEXT_MESSAGE_CONTENT` +- `TEXT_MESSAGE_END` is emitted when the stream switches to a new `messageId` or completes + +```json +{ + "type": "TEXT_MESSAGE_CHUNK", + "messageId": "msg-1", + "role": "assistant", + "delta": "Hello!" +} +``` + +--- + +## Tool Call Events + +### TOOL_CALL_START + +Begins a new tool invocation. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"TOOL_CALL_START"` | Yes | | +| `toolCallId` | `string` | Yes | Unique ID for this tool call | +| `toolCallName` | `string` | Yes | Name of the tool being called | +| `parentMessageId` | `string` | No | Links tool call to a parent assistant message | + +```json +{ + "type": "TOOL_CALL_START", + "toolCallId": "tc-1", + "toolCallName": "searchDatabase", + "parentMessageId": "msg-1" +} +``` + +### TOOL_CALL_ARGS + +Streams argument data for a tool call. Arguments are JSON fragments that concatenate to form the complete arguments object. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"TOOL_CALL_ARGS"` | Yes | | +| `toolCallId` | `string` | Yes | Must match `TOOL_CALL_START.toolCallId` | +| `delta` | `string` | Yes | Argument JSON fragment | + +```json +{ + "type": "TOOL_CALL_ARGS", + "toolCallId": "tc-1", + "delta": "{\"query\": \"recent orders\"}" +} +``` + +### TOOL_CALL_END + +Signals that a tool call's arguments are complete. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"TOOL_CALL_END"` | Yes | | +| `toolCallId` | `string` | Yes | Must match `TOOL_CALL_START.toolCallId` | + +```json +{ + "type": "TOOL_CALL_END", + "toolCallId": "tc-1" +} +``` + +### TOOL_CALL_RESULT + +Delivers the result of a tool execution. Sent after the tool has been executed (typically by the client/frontend). + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"TOOL_CALL_RESULT"` | Yes | | +| `messageId` | `string` | Yes | Message ID for this result in conversation history | +| `toolCallId` | `string` | Yes | Must match the corresponding `TOOL_CALL_START.toolCallId` | +| `content` | `string` | Yes | Tool execution output | +| `role` | `"tool"` | No | Defaults to `"tool"` | + +```json +{ + "type": "TOOL_CALL_RESULT", + "messageId": "msg-tool-1", + "toolCallId": "tc-1", + "content": "{\"results\": [{\"orderId\": \"123\"}]}", + "role": "tool" +} +``` + +### TOOL_CALL_CHUNK (Convenience) + +Auto-expands into `TOOL_CALL_START` / `TOOL_CALL_ARGS` / `TOOL_CALL_END` via the client's `transformChunks` pipeline. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"TOOL_CALL_CHUNK"` | Yes | | +| `toolCallId` | `string` | No | Required on first chunk | +| `toolCallName` | `string` | No | Required on first chunk | +| `parentMessageId` | `string` | No | Links to parent message | +| `delta` | `string` | No | Argument JSON fragment | + +```json +{ + "type": "TOOL_CALL_CHUNK", + "toolCallId": "tc-1", + "toolCallName": "searchDatabase", + "delta": "{\"query\":" +} +``` + +--- + +## State Management Events + +### STATE_SNAPSHOT + +Delivers a complete replacement of the agent's state. Frontend should discard existing state and use this snapshot. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"STATE_SNAPSHOT"` | Yes | | +| `snapshot` | `any` | Yes | Complete state object | + +```json +{ + "type": "STATE_SNAPSHOT", + "snapshot": { + "documents": [], + "currentStep": "planning", + "progress": 0 + } +} +``` + +### STATE_DELTA + +Delivers incremental state updates as RFC 6902 JSON Patch operations. Applied to current state using `fast-json-patch`. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"STATE_DELTA"` | Yes | | +| `delta` | `Array` | Yes | Array of JSON Patch operations | + +JSON Patch operations: +- `{ "op": "add", "path": "/key", "value": ... }` +- `{ "op": "replace", "path": "/key", "value": ... }` +- `{ "op": "remove", "path": "/key" }` +- `{ "op": "move", "path": "/to", "from": "/from" }` +- `{ "op": "copy", "path": "/to", "from": "/from" }` +- `{ "op": "test", "path": "/key", "value": ... }` + +```json +{ + "type": "STATE_DELTA", + "delta": [ + { "op": "replace", "path": "/currentStep", "value": "executing" }, + { "op": "replace", "path": "/progress", "value": 0.5 } + ] +} +``` + +### MESSAGES_SNAPSHOT + +Delivers a complete snapshot of the conversation message history. Uses edit-based merge: existing messages present in the snapshot are replaced, activity messages are preserved, messages not in the snapshot are removed, and new messages from the snapshot are appended. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"MESSAGES_SNAPSHOT"` | Yes | | +| `messages` | `Message[]` | Yes | Array of message objects | + +```json +{ + "type": "MESSAGES_SNAPSHOT", + "messages": [ + { "id": "m1", "role": "user", "content": "Hello" }, + { "id": "m2", "role": "assistant", "content": "Hi there!" } + ] +} +``` + +--- + +## Activity Events + +### ACTIVITY_SNAPSHOT + +Delivers a complete snapshot of an activity (structured progress update displayed between chat messages). + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"ACTIVITY_SNAPSHOT"` | Yes | | +| `messageId` | `string` | Yes | Activity message ID | +| `activityType` | `string` | Yes | Discriminator (e.g., `"PLAN"`, `"SEARCH"`, `"CODE"`) | +| `content` | `Record` | Yes | Structured activity data | +| `replace` | `boolean` | No | Defaults to `true`. If `false`, ignored when message exists | + +```json +{ + "type": "ACTIVITY_SNAPSHOT", + "messageId": "activity-1", + "activityType": "SEARCH", + "content": { + "query": "CopilotKit setup", + "results": [], + "status": "searching" + } +} +``` + +### ACTIVITY_DELTA + +Applies JSON Patch updates to an existing activity message's content. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"ACTIVITY_DELTA"` | Yes | | +| `messageId` | `string` | Yes | Must match an existing activity message | +| `activityType` | `string` | Yes | Activity discriminator | +| `patch` | `Array` | Yes | RFC 6902 JSON Patch operations | + +```json +{ + "type": "ACTIVITY_DELTA", + "messageId": "activity-1", + "activityType": "SEARCH", + "patch": [ + { "op": "replace", "path": "/status", "value": "complete" }, + { "op": "add", "path": "/results/0", "value": { "title": "Getting Started" } } + ] +} +``` + +--- + +## Reasoning Events + +### REASONING_START + +Marks the beginning of a reasoning process (chain-of-thought). + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"REASONING_START"` | Yes | | +| `messageId` | `string` | Yes | Reasoning context ID | + +```json +{ + "type": "REASONING_START", + "messageId": "reasoning-1" +} +``` + +### REASONING_MESSAGE_START + +Begins a streaming reasoning message (visible portion of chain-of-thought). + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"REASONING_MESSAGE_START"` | Yes | | +| `messageId` | `string` | Yes | Message ID | +| `role` | `"reasoning"` | Yes | Always `"reasoning"` | + +```json +{ + "type": "REASONING_MESSAGE_START", + "messageId": "reasoning-msg-1", + "role": "reasoning" +} +``` + +### REASONING_MESSAGE_CONTENT + +Delivers a chunk of reasoning text. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"REASONING_MESSAGE_CONTENT"` | Yes | | +| `messageId` | `string` | Yes | Must match `REASONING_MESSAGE_START.messageId` | +| `delta` | `string` | Yes | Reasoning text chunk (must be non-empty) | + +```json +{ + "type": "REASONING_MESSAGE_CONTENT", + "messageId": "reasoning-msg-1", + "delta": "Let me think about this..." +} +``` + +### REASONING_MESSAGE_END + +Signals a reasoning message is complete. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"REASONING_MESSAGE_END"` | Yes | | +| `messageId` | `string` | Yes | Must match `REASONING_MESSAGE_START.messageId` | + +```json +{ + "type": "REASONING_MESSAGE_END", + "messageId": "reasoning-msg-1" +} +``` + +### REASONING_MESSAGE_CHUNK (Convenience) + +Auto-expands into `REASONING_MESSAGE_START` / `CONTENT` / `END` via client transformer. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"REASONING_MESSAGE_CHUNK"` | Yes | | +| `messageId` | `string` | No | Required on first chunk | +| `delta` | `string` | No | Reasoning text chunk | + +```json +{ + "type": "REASONING_MESSAGE_CHUNK", + "messageId": "reasoning-msg-1", + "delta": "Analyzing the request..." +} +``` + +### REASONING_END + +Marks the end of a reasoning process. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"REASONING_END"` | Yes | | +| `messageId` | `string` | Yes | Must match `REASONING_START.messageId` | + +```json +{ + "type": "REASONING_END", + "messageId": "reasoning-1" +} +``` + +### REASONING_ENCRYPTED_VALUE + +Attaches encrypted chain-of-thought to a message or tool call. Used for zero-data-retention (ZDR) scenarios where reasoning must be preserved across turns but not exposed to the client. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"REASONING_ENCRYPTED_VALUE"` | Yes | | +| `subtype` | `"message" \| "tool-call"` | Yes | Entity type this reasoning belongs to | +| `entityId` | `string` | Yes | ID of the message or tool call | +| `encryptedValue` | `string` | Yes | Opaque encrypted content blob | + +```json +{ + "type": "REASONING_ENCRYPTED_VALUE", + "subtype": "message", + "entityId": "msg-1", + "encryptedValue": "eyJhbGciOiJSU0..." +} +``` + +--- + +## Custom / Extension Events + +### RAW + +Passthrough container for events from external systems. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"RAW"` | Yes | | +| `event` | `any` | Yes | Original event data | +| `source` | `string` | No | Source system identifier | + +```json +{ + "type": "RAW", + "event": { "vendor_type": "langchain_event", "data": {} }, + "source": "langchain" +} +``` + +### CUSTOM + +Application-specific extension events with named semantics. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | `"CUSTOM"` | Yes | | +| `name` | `string` | Yes | Custom event name | +| `value` | `any` | Yes | Event payload | + +```json +{ + "type": "CUSTOM", + "name": "citation", + "value": { "url": "https://example.com", "title": "Reference" } +} +``` + +--- + +## Deprecated Events (Remove in 1.0.0) + +These THINKING events are replaced by REASONING events: + +| Deprecated | Replacement | +|------------|-------------| +| `THINKING_START` | `REASONING_START` | +| `THINKING_END` | `REASONING_END` | +| `THINKING_TEXT_MESSAGE_START` | `REASONING_MESSAGE_START` | +| `THINKING_TEXT_MESSAGE_CONTENT` | `REASONING_MESSAGE_CONTENT` | +| `THINKING_TEXT_MESSAGE_END` | `REASONING_MESSAGE_END` | + +The client SDK includes `BackwardCompatibility_0_0_45` middleware that auto-converts these. + +--- + +## Transport Encoding + +### SSE (Server-Sent Events) + +Default transport. Content-Type: `text/event-stream`. + +Each event is encoded as: +``` +data: \n\n +``` + +The `@ag-ui/encoder` `EventEncoder` class produces this format: + +```typescript +import { EventEncoder } from "@ag-ui/encoder"; + +const encoder = new EventEncoder(); +const sseString = encoder.encode(event); // "data: {...}\n\n" +``` + +### Binary (Protobuf) + +Optional binary transport using `@ag-ui/proto`. Content-Type: `application/x-ag-ui`. + +Each message is length-prefixed: 4-byte big-endian uint32 length header followed by protobuf-encoded message bytes. + +The `EventEncoder` auto-detects format from the `Accept` header: + +```typescript +const encoder = new EventEncoder({ accept: req.headers.accept }); +encoder.getContentType(); // "text/event-stream" or "application/x-ag-ui" +encoder.encodeBinary(event); // Uint8Array (SSE bytes or protobuf) +``` + +--- + +## Type Definitions (RunAgentInput) + +The input payload sent to the agent on each run: + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `threadId` | `string` | Yes | Conversation thread ID | +| `runId` | `string` | Yes | Unique run ID | +| `parentRunId` | `string` | No | Parent run for branching | +| `state` | `any` | Yes | Current state | +| `messages` | `Message[]` | Yes | Conversation history | +| `tools` | `Tool[]` | Yes | Available tools | +| `context` | `Context[]` | Yes | Additional context | +| `forwardedProps` | `any` | Yes | Pass-through properties | + +### Message Types + +Messages are discriminated by `role`: + +| Role | Fields | Description | +|------|--------|-------------| +| `developer` | `id`, `role`, `content`, `name?`, `encryptedValue?` | Developer/system instructions | +| `system` | `id`, `role`, `content`, `name?`, `encryptedValue?` | System messages | +| `assistant` | `id`, `role`, `content?`, `toolCalls?`, `name?`, `encryptedValue?` | Agent responses | +| `user` | `id`, `role`, `content` (string or `InputContent[]`), `name?` | User messages (supports multimodal) | +| `tool` | `id`, `role`, `content`, `toolCallId`, `error?`, `encryptedValue?` | Tool results | +| `activity` | `id`, `role`, `activityType`, `content` (Record) | Activity progress | +| `reasoning` | `id`, `role`, `content`, `encryptedValue?` | Reasoning/thinking content | + +### Tool Definition + +```typescript +interface Tool { + name: string; // Tool name + description: string; // Human-readable description + parameters: any; // JSON Schema for parameters +} +``` + +### InputContent (Multimodal) + +User messages can contain mixed content: + +- `TextInputContent`: `{ type: "text", text: string }` +- `BinaryInputContent`: `{ type: "binary", mimeType: string, id?: string, url?: string, data?: string, filename?: string }` (requires at least one of `id`, `url`, or `data`) diff --git a/skills/copilotkit-agui/sources.md b/skills/copilotkit-agui/sources.md new file mode 100644 index 00000000000..2fc37e808e6 --- /dev/null +++ b/skills/copilotkit-agui/sources.md @@ -0,0 +1,49 @@ +# Sources + +Files read from the AG-UI and CopilotKit codebases to generate this skill's references. + +## protocol-spec.md + +- ag-ui/sdks/typescript/packages/core/src/events.ts +- ag-ui/sdks/typescript/packages/core/src/types.ts +- ag-ui/sdks/typescript/packages/core/src/capabilities.ts +- ag-ui/sdks/typescript/packages/core/src/event-factories.ts +- ag-ui/sdks/typescript/packages/core/src/index.ts +- ag-ui/sdks/typescript/packages/encoder/src/encoder.ts +- ag-ui/sdks/typescript/packages/encoder/src/media-type.ts +- ag-ui/docs/concepts/events.mdx +- ag-ui/docs/concepts/state.mdx +- ag-ui/docs/concepts/agents.mdx + +## building-agents.md + +- ag-ui/sdks/typescript/packages/core/src/events.ts +- ag-ui/sdks/typescript/packages/core/src/types.ts +- ag-ui/sdks/typescript/packages/core/src/event-factories.ts +- ag-ui/sdks/typescript/packages/encoder/src/encoder.ts +- ag-ui/sdks/typescript/packages/client/src/agent/agent.ts +- ag-ui/sdks/typescript/packages/client/src/agent/http.ts +- ag-ui/docs/concepts/agents.mdx + +## event-flow-diagrams.md + +- ag-ui/sdks/typescript/packages/core/src/events.ts +- ag-ui/sdks/typescript/packages/client/src/apply/default.ts +- ag-ui/docs/concepts/events.mdx +- ag-ui/docs/concepts/state.mdx + +## client-sdk.md + +- ag-ui/sdks/typescript/packages/client/src/agent/agent.ts +- ag-ui/sdks/typescript/packages/client/src/agent/http.ts +- ag-ui/sdks/typescript/packages/client/src/agent/types.ts +- ag-ui/sdks/typescript/packages/client/src/agent/subscriber.ts +- ag-ui/sdks/typescript/packages/client/src/apply/default.ts +- ag-ui/sdks/typescript/packages/client/src/middleware/middleware.ts +- ag-ui/sdks/typescript/packages/client/src/transform/sse.ts +- ag-ui/sdks/typescript/packages/client/src/transform/http.ts +- ag-ui/sdks/typescript/packages/client/src/run/http-request.ts +- ag-ui/sdks/typescript/packages/client/src/index.ts +- ag-ui/sdks/typescript/packages/client/src/chunks/transform.ts +- ag-ui/sdks/typescript/packages/client/src/verify/verify.ts +- ag-ui/docs/concepts/middleware.mdx diff --git a/skills/copilotkit-contribute/SKILL.md b/skills/copilotkit-contribute/SKILL.md new file mode 100644 index 00000000000..752ec1344c7 --- /dev/null +++ b/skills/copilotkit-contribute/SKILL.md @@ -0,0 +1,72 @@ +--- +name: copilotkit-contribute +description: > + Use when contributing to the CopilotKit open-source project — forking, + cloning, setting up the monorepo, creating branches, running tests, and + submitting pull requests against CopilotKit/CopilotKit. +version: 1.0.0 +--- + +# Contributing to CopilotKit + +> **Important:** CopilotKit's internal v2 packages use the `@copilotkit/*` namespace. The public API that users install is `@copilotkit/*`. When contributing, you work with `@copilotkit/*` source but users never see that namespace. + +## Live Documentation (MCP) + +This plugin includes an MCP server (`copilotkit-docs`) that provides `search-docs` and `search-code` tools for querying live CopilotKit documentation and source code. + +- **Claude Code:** Auto-configured by the plugin's `.mcp.json` -- no setup needed. +- **Codex:** Requires manual configuration. See the [copilotkit-debug skill](../copilotkit-debug/SKILL.md#mcp-setup) for setup instructions. + +## Workflow + +1. **Fork and clone** the CopilotKit/CopilotKit repository. +2. **Install dependencies** with `pnpm install` (requires pnpm v9.x and Node 20+). +3. **Build once** with `pnpm build` to bootstrap all packages. +4. **Create a branch** using the naming convention: `feat/-`, `fix/-`, or `docs/-`. +5. **Develop** with `pnpm dev` (watches all packages) or target a specific package with `nx run @copilotkit/:dev`. +6. **Write and run tests** with `nx run @copilotkit/:test`. All v2 packages use Vitest. +7. **Lint and format** with `pnpm run lint --fix && pnpm run format`. +8. **Commit** using conventional commit format: `(): ` (enforced by commitlint). +9. **Push and open a PR** against the `main` branch. CI builds all packages and publishes preview packages via pkg-pr-new. + +## Before Opening a PR + +- Reach out to the maintainers first for any significant work (file an issue or ask on Discord). +- Run `pnpm run test` to verify all tests pass. +- Run `pnpm run build` to verify the full build succeeds. +- Run `pnpm run check-prettier` to verify formatting. +- Ensure commit messages follow the `(): ` format. + +## Quick Reference + +| Task | Command | +|---|---| +| Install dependencies | `pnpm install` | +| Build all packages | `pnpm build` | +| Dev mode (all) | `pnpm dev` | +| Dev mode (v2 only) | `pnpm dev:next` | +| Run all tests | `pnpm run test` | +| Run v2 tests only | `pnpm test:next` | +| Run single package tests | `nx run @copilotkit/core:test` | +| Test with coverage | `pnpm run test:coverage` | +| Lint | `pnpm run lint` | +| Format | `pnpm run format` | +| Check formatting | `pnpm run check-prettier` | +| Type check | `pnpm run check-types` | +| Package quality checks | `pnpm run check:packages` | +| Dependency graph | `pnpm run graph` | + +## Key Architecture Points + +- V2 (`@copilotkit/*`) is the real implementation. V1 (`@copilotkit/*`) wraps V2. +- New features always go in V2 packages under `packages/v2/`. +- Communication between frontend and runtime uses the AG-UI protocol (SSE-based events). +- The monorepo uses Nx for task orchestration and pnpm workspaces. + +## Reference Documents + +- [Contribution Guide](references/contribution-guide.md) — full onboarding walkthrough +- [Repo Structure](references/repo-structure.md) — package layout and architecture +- [Testing Guide](references/testing-guide.md) — Vitest setup, running tests, coverage +- [PR Guidelines](references/pr-guidelines.md) — CI checks, review process, expectations diff --git a/skills/copilotkit-contribute/references/contribution-guide.md b/skills/copilotkit-contribute/references/contribution-guide.md new file mode 100644 index 00000000000..da2a07fb32b --- /dev/null +++ b/skills/copilotkit-contribute/references/contribution-guide.md @@ -0,0 +1,141 @@ +# Contribution Guide + +## Prerequisites + +- **Node.js** 20.x or later +- **pnpm** v9.x installed globally: `npm i -g pnpm@^9` +- **Git** configured with your GitHub account +- **Windows users:** Enable Developer Mode (Settings > System > For developers) for symlink support + +## Fork and Clone + +1. Fork [CopilotKit/CopilotKit](https://github.com/CopilotKit/CopilotKit) on GitHub. +2. Clone your fork: + +```bash +git clone https://github.com//CopilotKit.git +cd CopilotKit +``` + +3. Add the upstream remote: + +```bash +git remote add upstream https://github.com/CopilotKit/CopilotKit.git +``` + +4. Keep your fork up to date: + +```bash +git fetch upstream +git rebase upstream/main +``` + +## Initial Setup + +```bash +# Install all dependencies +pnpm install + +# Build all packages (required before first dev session) +pnpm build +``` + +The build step is necessary because packages depend on each other. Nx handles the dependency graph — `pnpm build` runs `nx run-many -t build --projects=packages/**` with correct ordering. + +## Branch Naming + +Use a group prefix with the issue number: + +| Type | Pattern | Example | +|---|---|---| +| Feature | `feat/-` | `feat/1234-add-voice-hook` | +| Bug fix | `fix/-` | `fix/5678-runtime-cors` | +| Documentation | `docs/-` | `docs/9012-api-reference` | + +```bash +git checkout -b feat/1234-my-feature +``` + +## Development + +Start all packages in watch/dev mode: + +```bash +# All packages +pnpm dev + +# V2 packages only +pnpm dev:next + +# V1 packages only (if working on v1 wrappers) +pnpm dev:classic + +# Single package +nx run @copilotkit/core:dev +``` + +## Commit Message Format + +CopilotKit enforces [conventional commits](https://www.conventionalcommits.org/) via commitlint (runs as a `commit-msg` hook through lefthook). The format is: + +``` +(): +``` + +**Header max length:** 120 characters. + +### Types + +| Type | Description | +|---|---| +| `feat` | A new feature | +| `fix` | A bug fix | +| `docs` | Documentation changes | +| `style` | Formatting, whitespace (no code logic change) | +| `refactor` | Code change that neither fixes a bug nor adds a feature | +| `perf` | Performance improvement | +| `test` | Adding or fixing tests | +| `chore` | Build process, tooling, dependencies | + +### Scope + +Use the package name without the namespace prefix: + +``` +fix(runtime): handle missing agent on init +feat(react): add useInterrupt hook +test(core): cover edge case in tool registry +``` + +## Pre-commit Hooks + +CopilotKit uses [lefthook](https://github.com/evilmartians/lefthook) for git hooks. On every commit, the following run in parallel: + +1. **check-binaries** — rejects binary files, build artifacts, oversized files (>1MB) +2. **sync-lockfile** — runs `pnpm i --lockfile-only` if package.json changed +3. **lint-fix** — runs `pnpm run lint --fix && pnpm run format` +4. **test-and-check-packages** — runs `pnpm run test && pnpm run check:packages` + +If hooks fail, fix the issue and commit again. Do not skip hooks with `--no-verify`. + +## Submitting a Pull Request + +1. Push your branch to your fork: + +```bash +git push origin feat/1234-my-feature +``` + +2. Open a PR against the `main` branch of CopilotKit/CopilotKit. +3. Fill in the PR template with: + - Description of the changes + - Related issue number + - Any questions or known limitations +4. Wait for CI to pass and a maintainer to review. +5. Address review feedback with additional commits. + +## Communication + +- **Before starting significant work:** File an [issue](https://github.com/CopilotKit/CopilotKit/issues) or reach out on [Discord](https://discord.com/invite/6dffbvGU3D). +- **Questions:** Use the [Discord support channel](https://discord.com/invite/6dffbvGU3D). +- **Documentation:** Visit [docs.copilotkit.ai](https://docs.copilotkit.ai/). diff --git a/skills/copilotkit-contribute/references/pr-guidelines.md b/skills/copilotkit-contribute/references/pr-guidelines.md new file mode 100644 index 00000000000..21c9dc37d08 --- /dev/null +++ b/skills/copilotkit-contribute/references/pr-guidelines.md @@ -0,0 +1,103 @@ +# Pull Request Guidelines + +## Before You Start + +Reach out to the maintainers before starting any significant work on new or existing features. File an issue or ask on [Discord](https://discord.com/invite/6dffbvGU3D). This prevents wasted effort on work that doesn't align with the project direction or is already in progress. + +## PR Checklist + +Before opening a PR, verify all of these pass locally: + +1. **Tests pass:** `pnpm run test` +2. **Build succeeds:** `pnpm build` +3. **Formatting clean:** `pnpm run check-prettier` +4. **Lint clean:** `pnpm run lint` +5. **Package quality:** `pnpm run check:packages` (runs publint + attw) +6. **Commit messages valid:** all commits follow `(): ` format + +## CI Pipeline + +Every push and pull request triggers the CI workflow which: + +1. Checks out the code +2. Installs pnpm and Node.js (version from `package.json`) +3. Runs `pnpm install` +4. Runs `pnpm run build` (builds all packages respecting Nx dependency graph) +5. Publishes preview packages via [pkg-pr-new](https://github.com/stackblitz-labs/pkg.pr.new) for every commit + +Preview packages are published from both `packages/v1/*` and `packages/v2/*`, allowing reviewers and users to test your changes before merge. + +## Pre-commit Hooks + +CopilotKit uses [lefthook](https://github.com/evilmartians/lefthook) to run pre-commit and commit-msg hooks automatically: + +### Pre-commit (parallel) + +| Hook | What it does | +|---|---| +| `check-binaries` | Rejects binary files, build artifacts, dSYM dirs, and files >1MB | +| `sync-lockfile` | Runs `pnpm i --lockfile-only` when package.json changes, auto-stages | +| `lint-fix` | Runs `pnpm run lint --fix && pnpm run format`, auto-stages fixes | +| `test-and-check-packages` | Runs `pnpm run test && pnpm run check:packages` | + +### Commit-msg + +| Hook | What it does | +|---|---| +| `commitlint` | Validates commit message against conventional commit format | + +If hooks fail, fix the issue and try again. Do not use `--no-verify` to bypass hooks. + +## Commit Message Format + +Enforced by commitlint with `@commitlint/config-conventional`: + +``` +(): +``` + +- **Header max length:** 120 characters +- **Subject case:** not enforced (rule disabled) + +### Valid types + +`feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `chore` + +### Scope + +Use the package directory name: `runtime`, `core`, `react`, `angular`, `react-core`, `react-ui`, `shared`, `agent`, etc. + +### Examples + +``` +feat(react): add useInterrupt hook for agent interrupts +fix(runtime): handle missing agent ID in request middleware +test(core): cover tool registry edge cases +docs(shared): update type documentation for EventType +chore(deps): bump vitest to 3.x +``` + +## PR Description + +Include in your PR: + +1. **What changed** — brief description of the changes +2. **Why** — the motivation or issue being addressed +3. **Issue reference** — link to the related GitHub issue (e.g., `Fixes #1234`) +4. **Testing** — how you verified the changes work +5. **Known limitations** — anything the reviewer should be aware of + +## Review Process + +1. A maintainer will review your PR. +2. CI must pass before review begins. +3. Reviewers may request changes — address feedback with additional commits. +4. Once approved and CI is green, a maintainer will merge the PR. + +## Tips + +- **Keep PRs focused.** One feature or fix per PR. Smaller PRs get reviewed faster. +- **Update tests.** If you change behavior, update or add tests to cover it. +- **Don't mix refactoring with features.** If you need to refactor something to enable your feature, submit the refactor as a separate PR first. +- **Rebase on main** before submitting if your branch is behind. Stale lockfiles are a common CI failure cause. +- **V2 first.** New features go in `packages/v2/`. Only add V1 wrappers if backward compatibility is needed. diff --git a/skills/copilotkit-contribute/references/repo-structure.md b/skills/copilotkit-contribute/references/repo-structure.md new file mode 100644 index 00000000000..95b97ccda24 --- /dev/null +++ b/skills/copilotkit-contribute/references/repo-structure.md @@ -0,0 +1,125 @@ +# Repository Structure + +## Overview + +CopilotKit is a pnpm monorepo managed by Nx. Packages live under `packages/`, examples under `examples/`, and a showcase app under `showcase/`. + +``` +CopilotKit/ + packages/ + v1/ # Public API packages (@copilotkit/*) + v2/ # Implementation packages (@copilotkit/*) + examples/ + v1/ # V1 example apps + v2/ # V2 example apps (React, Angular, Node, etc.) + showcase/ # Showcase app (shell + packages + scripts) +``` + +## V1-Wraps-V2 Architecture + +V2 (`@copilotkit/*`) is the real implementation. V1 (`@copilotkit/*`) is the public API that wraps V2 internally. New features always go in V2. If V1 compatibility is needed, create a thin re-export or wrapper in the corresponding V1 package. + +All layers communicate via the **AG-UI protocol** — an event-based standard streamed over SSE. + +``` +Frontend (React/Angular/Vanilla) -> Runtime (Express/Hono server) -> Agent (LangGraph/CrewAI/BuiltIn/Custom) +``` + +## V2 Packages (`packages/v2/`) + +| Directory | Package Name | Description | +|---|---|---| +| `shared` | `@copilotkit/shared` | Common utilities, types, and constants used across all other packages | +| `core` | `@copilotkit/core` | The `CopilotKitCore` orchestrator — manages agent registry, tool registry, context store, and event subscriptions. All framework packages wrap this. | +| `react` | `@copilotkit/react` | React hooks (`useAgent`, `useFrontendTool`, `useAgentContext`, etc.) and `CopilotKitProvider`. Thin wrappers that register/unregister with Core. | +| `angular` | `@copilotkit/angular` | Angular DI tokens, services, and signal-based state. Same concepts as React using Angular patterns. | +| `runtime` | `@copilotkit/runtime` | Server-side `CopilotRuntime` class. Receives HTTP requests, delegates to agents. Provides Express and Hono adapters. Contains `AgentRunner` abstraction. | +| `agent` | `@copilotkit/agent` | `BuiltInAgent` — a default agent implementation powered by the Vercel AI SDK. Used when developers don't bring their own agent framework. | +| `voice` | `@copilotkit/voice` | Voice input and transcription support. | +| `web-inspector` | `@copilotkit/web-inspector` | Debug console (Lit web component) for inspecting agent communication in development. | +| `sqlite-runner` | `@copilotkit/sqlite-runner` | `AgentRunner` implementation that persists thread state to SQLite instead of memory. | +| `demo-agents` | `@copilotkit/demo-agents` | Demo agent implementations for examples and testing. | +| `eslint-config` | `@copilotkit/eslint-config` | Shared ESLint configuration for v2 packages. | +| `typescript-config` | `@copilotkit/typescript-config` | Shared TypeScript configuration for v2 packages. | + +## V1 Packages (`packages/v1/`) + +| Directory | Package Name | Description | +|---|---|---| +| `react-core` | `@copilotkit/react-core` | Public `` provider and hooks. Internally delegates to V2 core. | +| `react-ui` | `@copilotkit/react-ui` | Chat UI components — `CopilotChat`, `CopilotPopup`, `CopilotSidebar`, `CopilotPanel`. | +| `react-textarea` | `@copilotkit/react-textarea` | `CopilotTextarea` component for AI-assisted text editing. | +| `shared` | `@copilotkit/shared` | Shared types and telemetry utilities. | +| `runtime` | `@copilotkit/runtime` | Server-side runtime with GraphQL server and LLM adapters. | +| `runtime-client-gql` | `@copilotkit/runtime-client-gql` | urql-based GraphQL client for frontend-to-runtime communication. | +| `sdk-js` | `@copilotkit/sdk-js` | Helpers for LangGraph/LangChain agent integration. | +| `a2ui-renderer` | `@copilotkit/a2ui-renderer` | AG-UI renderer for V1 compatibility. | +| `cli` | `copilotkit` | CopilotKit CLI tool. | +| `eslint-config-custom` | `eslint-config-custom` | Shared ESLint config for v1 packages. | +| `tailwind-config` | `tailwind-config` | Shared Tailwind CSS configuration. | +| `tsconfig` | `tsconfig` | Shared TypeScript configuration for v1 packages. | + +## Examples (`examples/`) + +``` +examples/ + v1/ # V1 example apps + v2/ + angular/ # Angular examples (storybook, demo, demo-server) + docs/ # Documentation site + node/ # Plain Node.js example + node-express/ # Express server example + react/ # React examples (storybook, demo) + interrupts-langraph/ # LangGraph interrupt handling example + next-pages-router/ # Next.js Pages Router example + canvas/ # Canvas examples + e2e/ # End-to-end test apps + integrations/ # Agent framework integration examples + showcases/ # Showcase demos +``` + +## Workspace Configuration + +**pnpm-workspace.yaml** defines the workspace packages: + +```yaml +packages: + - "packages/v1/*" + - "packages/v2/*" + - "examples/v1/*" + - "examples/v2/*" + - "examples/v2/*/apps/*" + - "examples/v2/react/*" + - "examples/v2/angular/*" + - "showcase/shell" + - "showcase/packages/*" + - "showcase/scripts" +``` + +**nx.json** configures task orchestration: + +- Default base branch: `main` +- Build parallelism: 14 +- Build outputs go to `{projectRoot}/dist/**` +- Test outputs go to `{projectRoot}/coverage/**` +- All builds depend on upstream package builds (`^build`) +- Named inputs separate `production` (no tests, no markdown) from `test` (source + test files) + +## Core Concepts + +### Request Lifecycle + +1. Frontend creates `CopilotKitCore` and fetches agent info from runtime +2. User sends message — POST to runtime with `RunAgentInput` payload +3. Runtime resolves agent, runs middleware, executes via `AgentRunner` +4. Agent emits AG-UI events streamed back over SSE +5. Frontend tool calls are executed locally in the browser, results sent back +6. Core updates message store, framework layer re-renders + +### Key Abstractions + +- **ProxiedAgent** — frontend representation of a remote agent; translates calls to HTTP + SSE +- **AgentRunner** — runtime-side thread state manager (InMemory or SQLite) +- **Tool Registration** — frontend tools (browser-side handlers) vs backend tools (server-side) +- **Context** — JSON-serializable app state sent with every agent run via `useAgentContext` +- **Middleware** — `beforeRequestMiddleware` / `afterRequestMiddleware` on `CopilotRuntime` diff --git a/skills/copilotkit-contribute/references/testing-guide.md b/skills/copilotkit-contribute/references/testing-guide.md new file mode 100644 index 00000000000..a6dc7a2cf79 --- /dev/null +++ b/skills/copilotkit-contribute/references/testing-guide.md @@ -0,0 +1,192 @@ +# Testing Guide + +## Test Framework + +CopilotKit uses **Vitest** for all unit and integration tests across both V1 and V2 packages. + +## Running Tests + +### All packages + +```bash +# Run all tests +pnpm run test + +# V2 packages only +pnpm test:next + +# V1 packages only +pnpm test:classic + +# Watch mode (all) +pnpm run test:watch + +# Watch mode (v2 only) +pnpm test:watch:next +``` + +### Single package + +```bash +# V2 packages use @copilotkit/ namespace +nx run @copilotkit/core:test +nx run @copilotkit/runtime:test +nx run @copilotkit/react:test +nx run @copilotkit/agent:test +nx run @copilotkit/shared:test +nx run @copilotkit/angular:test +nx run @copilotkit/web-inspector:test +nx run @copilotkit/demo-agents:test +nx run @copilotkit/sqlite-runner:test + +# V1 packages use @copilotkit/ namespace +nx run @copilotkit/react-core:test +nx run @copilotkit/runtime:test +nx run @copilotkit/react-ui:test +nx run @copilotkit/sdk-js:test +nx run @copilotkit/shared:test +nx run @copilotkit/react-textarea:test +nx run @copilotkit/runtime-client-gql:test +nx run @copilotkit/a2ui-renderer:test +``` + +## Coverage + +```bash +# All packages with coverage +pnpm run test:coverage + +# V2 only +pnpm test:coverage:next + +# V1 only +pnpm test:coverage:classic +``` + +Coverage reports are generated to `{package}/coverage/` in three formats: `text` (console), `lcov`, and `html`. + +Coverage configuration (from vitest.config): + +```js +coverage: { + reporter: ["text", "lcov", "html"], + include: ["src/**/*.ts"], + exclude: ["src/**/*.d.ts", "src/**/index.ts", "src/**/__tests__/**"], +} +``` + +## Vitest Configuration + +Each package has its own `vitest.config.mjs` (or `.mts`/`.ts`). Common settings across packages: + +### V2 Core / Runtime / Shared / SQLite-Runner + +```js +// vitest.config.mjs +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + environment: "node", + globals: true, + include: ["src/**/__tests__/**/*.{test,spec}.ts"], + reporters: [["default", { summary: false }]], + silent: true, + }, +}); +``` + +### V2 React (requires jsdom for React hooks) + +```js +// vitest.config.mjs +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + environment: "jsdom", + globalSetup: ["./src/__tests__/globalSetup.ts"], + setupFiles: ["./src/__tests__/setup.ts"], + include: ["**/__tests__/**/*.{test,spec}.{ts,tsx}"], + globals: true, + }, +}); +``` + +### V2 Runtime (disables telemetry during tests) + +```js +test: { + env: { + COPILOTKIT_TELEMETRY_DISABLED: "true", + }, + // ... +} +``` + +## Test File Locations and Naming + +Tests are colocated with source code in `__tests__/` directories: + +``` +packages/v2/core/src/ + __tests__/ + core-simple.test.ts + core-tool-simple.test.ts + core-edge-cases.test.ts + core-credentials.test.ts + core-headers.test.ts + core-context-injection.test.ts + test-utils.ts # Shared test utilities + core/ + __tests__/ + run-handler-schema.test.ts + run-handler-ensureObjectArgs.test.ts + run-handler-available.test.ts +``` + +V1 tests follow the same pattern but may also have top-level `tests/` directories: + +``` +packages/v1/runtime/tests/ + service-adapters/ + groq/groq-adapter-language-model.test.ts + shared/sdk-client-utils.test.ts +``` + +### Naming conventions + +- Test files: `*.test.ts` or `*.spec.ts` (`.tsx` for React component tests) +- Test utilities: `test-utils.ts` in `__tests__/` directories +- Setup files: `setup.ts`, `globalSetup.ts` in `__tests__/` directories + +## Test Utilities + +The primary test utility is at `packages/v2/core/src/__tests__/test-utils.ts`. It provides: + +- **`MockAgent`** — a mock implementation of the agent interface with: + - Configurable messages, state, errors, and delays + - Spied methods: `addMessages`, `addMessage`, `abortRun`, `clone` + - `runAgent()` tracking via `runAgentCalls` array + - Clone support (clones share parent tracking) + +```typescript +import { MockAgent, MockAgentOptions } from "../test-utils"; + +const agent = new MockAgent({ + messages: [], + newMessages: [/* messages returned by runAgent */], + agentId: "test-agent", + threadId: "test-thread", + state: { key: "value" }, + runAgentDelay: 100, // ms delay before runAgent resolves + error: new Error("boom"), // make runAgent reject +}); +``` + +## Tips + +- **Nx caches test results.** If source hasn't changed, `nx run @copilotkit/core:test` returns cached results instantly. Use `nx reset` to clear the cache if needed. +- **Globals are enabled.** You don't need to import `describe`, `it`, `expect`, `vi`, etc. — they're globally available via `globals: true`. +- **Silent mode is on.** Console output from tests is suppressed by default (`silent: true`). Remove it temporarily if you need to debug with `console.log`. +- **Build before testing.** Tests depend on upstream packages being built (`dependsOn: ["^build"]` in nx.json). Run `pnpm build` if you get import errors in tests. diff --git a/skills/copilotkit-contribute/sources.md b/skills/copilotkit-contribute/sources.md new file mode 100644 index 00000000000..5be9044916a --- /dev/null +++ b/skills/copilotkit-contribute/sources.md @@ -0,0 +1,36 @@ +# Sources + +Files and directories read from CopilotKit/CopilotKit to generate this skill's references. +Generated: 2026-03-28 + +## contribution-guide.md +- CONTRIBUTING.md (fork/clone instructions, branch naming conventions, PR submission process, communication channels) +- lefthook.yml (pre-commit hooks: check-binaries, sync-lockfile, lint-fix, test-and-check-packages; commit-msg hook: commitlint) +- package.json (pnpm scripts: build, dev, dev:next, dev:classic, test, lint, format, check-prettier, check:packages) +- .commitlintrc.json (conventional commit configuration, type/scope rules, header-max-length: 120) + +## repo-structure.md +- packages/v1/ (all v1 package directories: react-core, react-ui, react-textarea, shared, runtime, runtime-client-gql, sdk-js, a2ui-renderer, cli, eslint-config-custom, tailwind-config, tsconfig) +- packages/v2/ (all v2 package directories: shared, core, react, angular, runtime, agent, voice, web-inspector, sqlite-runner, demo-agents, eslint-config, typescript-config) +- examples/ (v1, v2, canvas, e2e, integrations, showcases directory structures) +- showcase/ (shell, packages, scripts) +- pnpm-workspace.yaml (workspace package definitions) +- nx.json (task orchestration config: build parallelism, dependency graph, named inputs, output caching) + +## testing-guide.md +- packages/v2/core/vitest.config.mjs (Vitest config: node environment, globals, include patterns, silent mode) +- packages/v2/react/vitest.config.mjs (Vitest config: jsdom environment, globalSetup, setupFiles) +- packages/v2/runtime/vitest.config.mjs (Vitest config: COPILOTKIT_TELEMETRY_DISABLED env var) +- packages/v2/core/src/__tests__/test-utils.ts (MockAgent implementation, MockAgentOptions interface) +- packages/v2/core/src/__tests__/ (test file examples: core-simple.test.ts, core-tool-simple.test.ts, etc.) +- packages/v2/core/src/core/__tests__/ (nested test examples: run-handler-schema.test.ts, etc.) +- packages/v1/runtime/tests/ (v1 test directory structure) +- package.json (test scripts: test, test:next, test:classic, test:watch, test:coverage) +- nx.json (test target configuration, coverage output paths, dependsOn: ^build) + +## pr-guidelines.md +- .github/workflows/ (CI workflow: checkout, pnpm install, build, pkg-pr-new preview publishing) +- lefthook.yml (pre-commit and commit-msg hook definitions) +- .commitlintrc.json (commitlint rules for conventional commit enforcement) +- package.json (quality scripts: test, build, check-prettier, lint, check:packages) +- CONTRIBUTING.md (PR template guidance, review process) diff --git a/skills/copilotkit-debug/SKILL.md b/skills/copilotkit-debug/SKILL.md new file mode 100644 index 00000000000..52c69417669 --- /dev/null +++ b/skills/copilotkit-debug/SKILL.md @@ -0,0 +1,119 @@ +--- +name: copilotkit-debug +description: "Use when diagnosing CopilotKit issues -- runtime connectivity failures, agent not responding, streaming errors, tool execution problems, transcription failures, version mismatches, and AG-UI event tracing." +version: 1.0.0 +--- + +# CopilotKit Debugging Skill + +## When to Use + +Invoke this skill when: +- The CopilotKit runtime is unreachable or returning errors +- Agents fail to connect, respond, or stream events +- Frontend tools are not executing or returning results +- Transcription (voice) is failing +- Version mismatch errors appear between packages +- AG-UI SSE events are malformed or missing +- CORS errors block browser requests to the runtime + +## Diagnostic Workflow + +### Step 1: Gather Information + +Before proposing any fix, collect: + +1. **Package versions** -- Run `npm ls @copilotkit/runtime @copilotkit/react @copilotkit/core @ag-ui/client` (or the v1 equivalents). Version mismatches between runtime and react packages are a common root cause. +2. **Runtime mode** -- Is this SSE mode (`CopilotSseRuntime`) or Intelligence mode (`CopilotIntelligenceRuntime`)? Check the runtime constructor. +3. **Transport configuration** -- What is `runtimeUrl` set to in `CopilotKitProvider`? Does it match the `basePath` in `createCopilotEndpoint`? +4. **Agent type** -- Is the agent a `BuiltInAgent`, `LangGraphAgent`, `A2AAgent`, or custom `AbstractAgent`? +5. **Error messages** -- Collect the exact error from browser console and server logs. CopilotKit uses structured error codes (see `references/error-patterns.md`). +6. **Browser network tab** -- Check the `/info` request (runtime discovery), the `/agent/:id/run` SSE stream, and any CORS preflight failures. + +### Step 2: Check Logs and Error Codes + +CopilotKit has three error code systems: + +- **V1 error codes** -- Legacy error codes from the v1 runtime layer (`@copilotkit/runtime`). Codes like `NETWORK_ERROR`, `AGENT_NOT_FOUND`, `API_NOT_FOUND`. Still surfaced in some contexts since `@copilotkit/*` packages wrap v2 internally. +- **V2 `CopilotKitCoreErrorCode`** -- Used by `@copilotkit/core`. Codes like `runtime_info_fetch_failed`, `agent_connect_failed`, `agent_run_failed`. +- **`TranscriptionErrorCode`** -- Used by both v1 and v2 for voice transcription. Codes like `service_not_configured`, `rate_limited`, `auth_failed`. + +Match the error code to the catalog in `references/error-patterns.md` for root cause and resolution. + +### Step 3: Trace AG-UI Events + +For streaming/agent issues, trace the AG-UI event flow: + +1. **RunStartedEvent** -- Confirms the agent run was initiated +2. **TextMessageStartEvent / TextMessageChunkEvent / TextMessageEndEvent** -- Text streaming +3. **ToolCallStartEvent / ToolCallArgsEvent / ToolCallEndEvent** -- Tool invocations +4. **ToolCallResultEvent** -- Tool results flowing back +5. **StateSnapshotEvent / StateDeltaEvent** -- Agent state synchronization +6. **ReasoningStartEvent / ReasoningMessageContentEvent / ReasoningMessageEndEvent** -- Reasoning tokens (can cause stalls, see issue #3323) +7. **RunFinishedEvent** -- Successful completion +8. **RunErrorEvent** -- Agent-level error + +Enable the CopilotKit Web Inspector (`@copilotkit/web-inspector`) to see events in real time. Or check the SSE stream directly in the browser Network tab -- each event is a `data:` line in the `text/event-stream` response. + +### Step 4: Identify Root Cause + +Use the reference documents to match symptoms to known issues: + +- **`references/runtime-debugging.md`** -- Connectivity, CORS, transport, SSE streaming +- **`references/agent-debugging.md`** -- Agent discovery, state sync, tool execution, AG-UI protocol +- **`references/error-patterns.md`** -- Complete error code catalog with resolutions +- **`references/quick-workflows.md`** -- Step-by-step diagnostic sequences for common scenarios + +### Step 5: Fix and Verify + +1. Apply the fix +2. Verify the `/info` endpoint returns the expected agent list +3. Confirm the SSE stream produces a complete event sequence (RunStarted through RunFinished) +4. Check the browser console for any remaining structured errors + +## Using mcp-docs for Live Documentation Lookups + +During debugging, use the `copilotkit-docs` MCP server to look up the latest CopilotKit documentation. This server provides two tools: `search-docs` (search documentation) and `search-code` (search source code examples). + +### MCP Setup + +**Claude Code:** The MCP server is auto-configured by the plugin's `.mcp.json` -- no manual setup needed. The agent can call the `search-docs` and `search-code` tools from the `copilotkit-docs` server directly. + +**Codex:** Add the following to your `.codex/config.toml`: + +```toml +[mcp_servers.copilotkit-docs] +type = "http" +url = "https://mcp.copilotkit.ai/mcp" +``` + +### Tool Usage + +The `search-docs` and `search-code` tools are invoked as MCP tool calls (not CLI commands). Examples of what to search for during debugging: + +``` +search-docs("AGENT_NOT_FOUND") +search-docs("CopilotRuntime configuration") +search-docs("AG-UI protocol events") +search-docs("troubleshooting common issues") +search-docs("CORS configuration copilotkit") +search-code("CopilotRuntime error handling") +``` + +The official troubleshooting docs are at: +- `https://docs.copilotkit.ai/troubleshooting/common-issues` +- `https://docs.copilotkit.ai/coagents/troubleshooting/common-issues` + +## Key File Locations in the CopilotKit Codebase + +| Component | Path | +|-----------|------| +| V1 Error classes & codes | `packages/v1/shared/src/utils/errors.ts` | +| V2 Core error codes | `packages/v2/core/src/core/core.ts` (`CopilotKitCoreErrorCode` enum) | +| V2 Transcription errors | `packages/v2/shared/src/transcription-errors.ts` | +| Runtime SSE response | `packages/v2/runtime/src/handlers/shared/sse-response.ts` | +| Runtime info endpoint | `packages/v2/runtime/src/handlers/get-runtime-info.ts` | +| Runtime CORS config | `packages/v2/runtime/src/endpoints/hono.ts` | +| Intelligence platform client | `packages/v2/runtime/src/intelligence-platform/client.ts` | +| Agent package (BuiltInAgent) | `packages/v2/agent/src/index.ts` | +| Web Inspector | `packages/v2/web-inspector/src/index.ts` | diff --git a/skills/copilotkit-debug/references/agent-debugging.md b/skills/copilotkit-debug/references/agent-debugging.md new file mode 100644 index 00000000000..d72f08df83d --- /dev/null +++ b/skills/copilotkit-debug/references/agent-debugging.md @@ -0,0 +1,284 @@ +# Agent Debugging Reference + +## Agent Types in CopilotKit v2 + +| Agent Type | Package | Description | +|------------|---------|-------------| +| `BuiltInAgent` | `@copilotkit/agent` | Uses Vercel AI SDK `streamText` with configurable model providers | +| `LangGraphAgent` | `@ag-ui/langgraph` | Wraps a LangGraph deployment (Python or JS) | +| `A2AAgent` | Varies | Agent-to-Agent protocol agent | +| Custom `AbstractAgent` | `@ag-ui/client` | Any class extending `AbstractAgent` with a `run()` returning `Observable` | + +## Agent Discovery Issues + +### Agent Not Found + +**Symptom**: `CopilotKitCoreErrorCode.agent_not_found` or `CopilotKitErrorCode.AGENT_NOT_FOUND` + +**Diagnostic steps**: + +1. Hit the `/info` endpoint to see registered agents: + ```bash + curl http://localhost:3001/api/copilotkit/info | jq .agents + ``` + +2. Compare the agent names in the response with the `agentId` prop: + ```tsx + + // or + const { run } = useAgent({ name: "myAgent" }); + ``` + +3. Check the runtime agent map -- keys must match exactly (case-sensitive): + ```ts + new CopilotRuntime({ + agents: { + myAgent: new BuiltInAgent({ /* ... */ }), // Key "myAgent" is the agent ID + }, + }); + ``` + +4. If using lazy agent loading (`agents: Promise<...>`), check that the promise resolves successfully. + +### Agent Constructor Failures + +If an agent throws during construction, the runtime may start without it: + +- **BuiltInAgent**: `resolveModel()` throws if the provider string is invalid (e.g., `"openai/"` without a model name, or `"unknown/model"`). +- **LangGraphAgent**: May fail if the LangGraph deployment URL is unreachable. +- **A2AAgent**: May fail if the A2A endpoint is misconfigured. + +## AG-UI Event Tracing + +### Event Flow for a Successful Run + +``` +RunStartedEvent + -> TextMessageStartEvent (messageId) + -> TextMessageChunkEvent (delta: "Hello") + -> TextMessageChunkEvent (delta: " world") + -> TextMessageEndEvent +RunFinishedEvent +``` + +### Event Flow with Tool Calls + +``` +RunStartedEvent + -> TextMessageStartEvent + -> TextMessageChunkEvent (delta: "Let me check...") + -> TextMessageEndEvent + -> ToolCallStartEvent (toolCallId, toolName) + -> ToolCallArgsEvent (delta: '{"query": "weather"}') + -> ToolCallEndEvent + -> ToolCallResultEvent (result: '{"temp": 72}') + -> TextMessageStartEvent + -> TextMessageChunkEvent (delta: "The temperature is 72F") + -> TextMessageEndEvent +RunFinishedEvent +``` + +### Event Flow with Errors + +``` +RunStartedEvent + -> RunErrorEvent (message: "...") // Non-fatal, run continues + -> TextMessageStartEvent + -> ... +RunFinishedEvent +``` + +Or for fatal errors: +``` +RunStartedEvent + -> RunErrorEvent (message: "...") // Fatal + // Stream ends without RunFinishedEvent +``` + +### Event Flow with State Sync + +``` +RunStartedEvent + -> StateSnapshotEvent (snapshot: {...}) // Full state + -> StateDeltaEvent (delta: [{op: "replace", path: "/count", value: 5}]) + -> TextMessageStartEvent + -> ... +RunFinishedEvent +``` + +### Event Flow with Reasoning (Anthropic Extended Thinking) + +``` +RunStartedEvent + -> ReasoningStartEvent + -> ReasoningMessageStartEvent + -> ReasoningMessageContentEvent (delta: "thinking...") + -> ReasoningMessageEndEvent + -> ReasoningEndEvent + -> TextMessageStartEvent + -> TextMessageChunkEvent + -> TextMessageEndEvent +RunFinishedEvent +``` + +**Known issue**: Reasoning events can cause stalls if the client-side event handler does not consume them properly (issue #3323). + +## State Synchronization Issues + +### State Not Updating on Frontend + +**Symptom**: Agent emits `StateSnapshotEvent` or `StateDeltaEvent` but the React component does not re-render. + +**Diagnostic steps**: + +1. Verify the agent is emitting state events -- check the SSE stream in the Network tab. +2. If using `useFrontendTool` with state, ensure the state shape matches what the component expects. +3. For LangGraph agents: verify `copilotkit_emit_state` events are reaching the frontend (see Python SDK event prefix mismatch, issue #3519). + +### Context Not Reaching Agents + +**Symptom**: Agent does not receive application context set via `useAgentContext` or similar hooks. + +**Diagnostic steps**: + +1. Context is sent as `forwardedProps` in the AG-UI `RunAgentInput`. Check the request body to `/agent/:id/run`. +2. For Mastra agents: context propagation through the middleware chain may not work correctly (issue #3426). +3. Verify that `useAgentContext` is called inside the `CopilotKitProvider` tree and before the agent runs. + +## Tool Execution Issues + +### Frontend Tool Not Found + +**Error code**: `tool_not_found` + +The agent called a tool name that does not match any registered frontend tool. + +**Diagnostic steps**: + +1. List registered tools by checking the AG-UI `Tool[]` array in the request to `/agent/:id/run`. +2. Ensure `useFrontendTool` is registered with the exact tool name (case-sensitive). +3. The tool must be registered BEFORE the agent run starts -- if it is registered lazily after mount, a race condition can occur. + +### Tool Arguments Parse Failed + +**Error code**: `tool_argument_parse_failed` + +The LLM generated arguments that do not match the tool's parameter schema. + +**Diagnostic steps**: + +1. Check the `ToolCallArgsEvent` in the SSE stream -- the `delta` field contains the raw JSON. +2. Validate the JSON against the tool's schema (Zod or JSON Schema). +3. This is usually an LLM issue -- consider improving the tool description or parameter descriptions. +4. For Zod schema validation issues in backend actions, see issue #3198. + +### Tool Handler Threw an Error + +**Error code**: `tool_handler_failed` + +The tool's `execute` function threw an exception. + +**Diagnostic steps**: + +1. Check the browser console for the error. +2. The `onError` callback in `CopilotChat` or `CopilotKitProvider` receives the error with context. +3. Wrap the tool handler in try/catch for better error reporting. + +### Tool Call Succeeds But Agent Does Not Continue + +**Symptom**: The tool returns a result but the agent does not produce a follow-up message. + +**Diagnostic steps**: + +1. Check that `ToolCallResultEvent` was emitted in the SSE stream after the tool completed. +2. For Human-in-the-Loop tools: the `runId` may change after HITL resolve (issue #3456), breaking the continuation. +3. For mixed frontend/backend tools: OpenAI may reject the request if tool definitions conflict (issue #3424). + +## BuiltInAgent-Specific Issues + +### Model Resolution Failures + +`BuiltInAgent` uses `resolveModel()` to convert string identifiers to Vercel AI SDK `LanguageModel` instances. + +Supported formats: +- `"openai/gpt-5"`, `"openai/gpt-4o"`, `"openai/o3-mini"` +- `"anthropic/claude-sonnet-4.5"`, `"anthropic/claude-opus-4"` +- `"google/gemini-2.5-pro"`, `"google/gemini-2.5-flash"` +- `"vertex/gemini-2.5-pro"` (uses Google Vertex AI) + +Common errors: +- `Invalid model string "..."` -- Missing provider prefix or model name +- `Unknown provider "..." in "..."` -- Unsupported provider (only openai, anthropic, google, vertex) +- Missing API key -- `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, or `GOOGLE_API_KEY` not set in environment + +### MCP Client Integration + +`BuiltInAgent` supports MCP (Model Context Protocol) clients: + +```ts +new BuiltInAgent({ + model: "openai/gpt-4o", + mcpClients: [ + { type: "http", url: "http://localhost:8080" }, + { type: "sse", url: "http://localhost:8081/sse", headers: { "Authorization": "Bearer ..." } }, + ], +}); +``` + +MCP debugging: +- `type: "http"` uses `StreamableHTTPClientTransport` +- `type: "sse"` uses `SSEClientTransport` +- If the MCP server is unreachable, the agent may fail silently or throw during tool discovery +- Check the MCP server logs for incoming connection attempts + +## LangGraph Agent Issues + +### Python SDK Event Name Mismatch + +The CopilotKit Python SDK (v0.1.83) dispatches custom events with a `"copilotkit_"` prefix, but `ag-ui-langgraph` expects event names without that prefix. This causes `copilotkit_emit_message`, `copilotkit_emit_state`, and `copilotkit_emit_tool_call` to be silently dropped (issue #3519). + +### LangGraph JS Template Outdated + +The official LangGraph JS template may be outdated and incompatible with current CopilotKit versions (issue #3231). Check for the latest template version. + +## Intelligence Mode Specific Issues + +### Thread Operations + +Intelligence mode uses the `CopilotKitIntelligence` client to manage threads: + +- **409 Conflict on createThread**: Another request created the thread between get and create. Handled automatically by `getOrCreateThread`. +- **404 on getThread**: Thread does not exist. The client will create a new one. +- **Auth failures (401)**: Invalid `apiKey` or `tenantId` in the Intelligence configuration. + +### WebSocket Connection Issues + +Intelligence mode uses WebSocket for real-time events: + +- Runner WebSocket: `{wsUrl}/runner` -- used by the runtime to communicate with the Intelligence platform +- Client WebSocket: `{wsUrl}/client` -- used by the frontend for real-time thread updates + +If WebSocket connections fail: +1. Check that the `wsUrl` is correct (should start with `wss://`) +2. Verify the API key and tenant ID +3. Check for WebSocket-blocking proxies or firewalls +4. The URLs are auto-derived from the base `wsUrl` -- `/runner` and `/client` suffixes are appended automatically + +## Web Inspector + +The CopilotKit Web Inspector (`@copilotkit/web-inspector`) provides real-time visibility into: + +- AG-UI events as they flow +- Error events with error codes +- Agent state snapshots +- Tool call lifecycle + +Enable it during development: +```tsx +import { CopilotKitWebInspector } from "@copilotkit/web-inspector"; + + + + + +``` diff --git a/skills/copilotkit-debug/references/error-patterns.md b/skills/copilotkit-debug/references/error-patterns.md new file mode 100644 index 00000000000..9a6c9acfab6 --- /dev/null +++ b/skills/copilotkit-debug/references/error-patterns.md @@ -0,0 +1,268 @@ +# CopilotKit Error Pattern Catalog + +## V1 Error Codes (`CopilotKitErrorCode`) + +Legacy error codes from the v1 runtime layer. These still surface in `@copilotkit/*` packages since they wrap v2 internally. Defined in `packages/v1/shared/src/utils/errors.ts`. + +### NETWORK_ERROR +- **HTTP Status**: 503 +- **Severity**: CRITICAL (banner) +- **Cause**: Server unreachable, DNS failure, connection timeout, SSL/TLS issues +- **Resolution**: Verify the runtime server is running and accessible. Check `runtimeUrl` in `CopilotKitProvider`. Common sub-causes: + - `ECONNREFUSED` -- Server not running on the expected port + - `ENOTFOUND` -- DNS cannot resolve the hostname + - `ETIMEDOUT` -- Server overloaded or network issues +- **Docs**: https://docs.copilotkit.ai/troubleshooting/common-issues#i-am-getting-a-network-errors--api-not-found + +### NOT_FOUND +- **HTTP Status**: 404 +- **Severity**: CRITICAL (banner) +- **Cause**: The runtime URL returns 404. Wrong basePath or the server is not serving CopilotKit at that path. +- **Resolution**: Ensure `basePath` in `createCopilotEndpoint()` matches the `runtimeUrl` in the provider. +- **Docs**: https://docs.copilotkit.ai/troubleshooting/common-issues#i-am-getting-a-network-errors--api-not-found + +### AGENT_NOT_FOUND +- **HTTP Status**: 500 +- **Severity**: CRITICAL (banner) +- **Cause**: The requested agent name does not exist in the runtime's agent registry. +- **Resolution**: Verify the agent name matches between `CopilotChat agentId` and the runtime's `agents` map. The error message lists available agents. +- **Docs**: https://docs.copilotkit.ai/coagents/troubleshooting/common-issues#i-am-getting-agent-not-found-error + +### API_NOT_FOUND +- **HTTP Status**: 404 +- **Severity**: CRITICAL (banner) +- **Cause**: The CopilotKit API endpoint itself cannot be discovered. Usually a routing/basePath mismatch. +- **Resolution**: Check that the runtime's Hono/Express app is mounted at the correct path. The error includes the URL that failed. +- **Docs**: https://docs.copilotkit.ai/troubleshooting/common-issues#i-am-getting-a-network-errors--api-not-found + +### REMOTE_ENDPOINT_NOT_FOUND +- **HTTP Status**: 404 +- **Severity**: CRITICAL (banner) +- **Cause**: A remote endpoint specified in the runtime configuration cannot be contacted. +- **Resolution**: Verify the remote endpoint URL is correct and the service is running. Check firewall/network rules. +- **Docs**: https://docs.copilotkit.ai/troubleshooting/common-issues#i-am-getting-copilotkits-remote-endpoint-not-found-error + +### AUTHENTICATION_ERROR +- **HTTP Status**: 401 +- **Severity**: CRITICAL (banner) +- **Cause**: Authentication failed when contacting the runtime or a remote service. +- **Resolution**: Check API keys, tokens, and authentication headers. +- **Docs**: https://docs.copilotkit.ai/troubleshooting/common-issues#authentication-errors + +### VERSION_MISMATCH +- **HTTP Status**: 400 +- **Severity**: INFO (dev only) +- **Cause**: `@copilotkit/*` packages are on different versions. +- **Resolution**: Ensure all `@copilotkit/*` packages are the same version. Run `npm ls @copilotkit/runtime @copilotkit/react`. + +### CONFIGURATION_ERROR +- **HTTP Status**: 400 +- **Severity**: WARNING (banner) +- **Cause**: Invalid runtime or provider configuration. +- **Resolution**: Review the CopilotRuntime and CopilotKitProvider configuration. + +### MISSING_PUBLIC_API_KEY_ERROR +- **HTTP Status**: 400 +- **Severity**: CRITICAL (banner) +- **Cause**: The `publicApiKey` prop is missing from `CopilotKitProvider` when using CopilotKit Cloud. +- **Resolution**: Add `publicApiKey` to the provider, or switch to self-hosted mode with `runtimeUrl`. + +### UPGRADE_REQUIRED_ERROR +- **HTTP Status**: 402 +- **Severity**: WARNING (banner) +- **Cause**: The current plan does not support the requested feature. +- **Resolution**: Upgrade the CopilotKit plan or remove the feature flag. + +### MISUSE +- **HTTP Status**: 400 +- **Severity**: WARNING (dev only) +- **Cause**: Incorrect API usage detected at development time (e.g., using a hook outside its provider). +- **Resolution**: Follow the error message guidance -- typically a component is being used outside the required provider. + +### UNKNOWN +- **HTTP Status**: 500 +- **Severity**: CRITICAL (toast) +- **Cause**: Unclassified server error. +- **Resolution**: Check server logs for the underlying exception. + +--- + +## V1 Error Classes + +All defined in `packages/v1/shared/src/utils/errors.ts`: + +| Class | Extends | When Thrown | +|-------|---------|------------| +| `CopilotKitError` | `GraphQLError` | Base class for all structured errors | +| `CopilotKitMisuseError` | `CopilotKitError` | Wrong usage of components/hooks | +| `CopilotKitVersionMismatchError` | `CopilotKitError` | Package version incompatibility | +| `CopilotKitApiDiscoveryError` | `CopilotKitError` | Runtime endpoint not found (404, routing) | +| `CopilotKitRemoteEndpointDiscoveryError` | `CopilotKitApiDiscoveryError` | Remote agent endpoint unreachable | +| `CopilotKitAgentDiscoveryError` | `CopilotKitError` | Named agent not in registry | +| `CopilotKitLowLevelError` | `CopilotKitError` | Pre-HTTP errors (DNS, connection refused) | +| `ResolvedCopilotKitError` | `CopilotKitError` | HTTP error responses (status-code based) | +| `ConfigurationError` | `CopilotKitError` | Invalid configuration | +| `MissingPublicApiKeyError` | `ConfigurationError` | Cloud mode without API key | +| `UpgradeRequiredError` | `ConfigurationError` | Plan limitation | + +--- + +## V2 Error Codes (`CopilotKitCoreErrorCode`) + +Used by `@copilotkit/core`. Defined in `packages/v2/core/src/core/core.ts`. These are emitted via the `onError` subscriber callback. + +### runtime_info_fetch_failed +- **Cause**: The `/info` endpoint returned an error or was unreachable. +- **Resolution**: Verify `runtimeUrl` points to a running CopilotRuntime. Check CORS if cross-origin. The `/info` endpoint must return agent metadata and runtime version. + +### agent_connect_failed +- **Cause**: WebSocket or HTTP connection to the agent failed during the connect phase. +- **Resolution**: For Intelligence mode, verify the WebSocket URL (`wsUrl`) is correct. For SSE mode, check that the agent exists in the runtime. + +### agent_run_failed +- **Cause**: The agent run threw an exception before completing. +- **Resolution**: Check server-side logs for the agent execution error. Common causes: missing API keys for the LLM provider, invalid model configuration. + +### agent_run_failed_event +- **Cause**: The AG-UI stream contained a `RunFailedEvent` (the agent explicitly signaled failure). +- **Resolution**: The event payload contains the failure reason. Check the agent's implementation for error handling. + +### agent_run_error_event +- **Cause**: The AG-UI stream contained a `RunErrorEvent` (non-fatal error during the run). +- **Resolution**: Check the error message in the event. May be transient -- the agent might recover. + +### tool_argument_parse_failed +- **Cause**: The JSON arguments for a frontend tool call could not be parsed. +- **Resolution**: Check the tool's parameter schema. The LLM may have generated malformed JSON. + +### tool_handler_failed +- **Cause**: A frontend tool's `execute` handler threw an exception. +- **Resolution**: Check the tool's handler code. The error is caught and reported via `onError`. + +### tool_not_found +- **Cause**: The agent called a tool that is not registered in the frontend. +- **Resolution**: Ensure `useFrontendTool` is registered with the correct name before the agent runs. + +### agent_not_found +- **Cause**: The `agentId` passed to `CopilotChat` or `useAgent` does not match any agent in the runtime. +- **Resolution**: Check the runtime's `/info` endpoint to see available agents. Match the `agentId` prop. + +### transcription_failed +- **Cause**: Generic transcription failure. +- **Resolution**: See TranscriptionErrorCode section below for specific sub-codes. + +### transcription_service_not_configured +- **Cause**: Voice transcription requested but no `transcriptionService` configured in the runtime. +- **Resolution**: Add a transcription service to the runtime constructor. + +### transcription_invalid_audio +- **Cause**: Audio format not supported by the transcription provider. +- **Resolution**: Check supported audio formats (typically webm, wav, mp3). + +### transcription_rate_limited +- **Cause**: Transcription provider rate limit exceeded. +- **Resolution**: Wait and retry. Consider caching or reducing request frequency. + +### transcription_auth_failed +- **Cause**: Authentication with the transcription provider failed. +- **Resolution**: Check the transcription API key configuration. + +### transcription_network_error +- **Cause**: Network error during transcription API call. +- **Resolution**: Check connectivity to the transcription provider. + +--- + +## Transcription Error Codes (`TranscriptionErrorCode`) + +Used by `@copilotkit/shared` and `@copilotkit/react`. Defined in `packages/v2/shared/src/transcription-errors.ts`. + +| Code | Retryable | Description | +|------|-----------|-------------| +| `service_not_configured` | No | No transcription service in runtime | +| `invalid_audio_format` | No | Unsupported audio format | +| `audio_too_long` | No | Audio file exceeds maximum duration | +| `audio_too_short` | No | Audio too short to transcribe | +| `rate_limited` | Yes | Provider rate limit hit | +| `auth_failed` | No | Provider authentication failed | +| `provider_error` | Yes | Provider returned an error | +| `network_error` | Yes | Network failure during transcription | +| `invalid_request` | No | Malformed request to transcription endpoint | + +--- + +## Intelligence Platform Error (`PlatformRequestError`) + +Used by `@copilotkit/runtime` for Intelligence mode. Defined in `packages/v2/runtime/src/intelligence-platform/client.ts`. + +| Status | Meaning | +|--------|---------| +| 404 | Thread not found | +| 409 | Thread already exists (race condition -- handled automatically by `getOrCreateThread`) | +| 401 | Invalid API key or tenant ID | +| 500 | Platform server error | + +--- + +## Common GitHub-Reported Issues + +These are frequently reported bugs from the CopilotKit issue tracker: + +### Event Name Prefix Mismatch (Python SDK + ag-ui-langgraph) +- **Issue**: [#3519](https://github.com/CopilotKit/CopilotKit/issues/3519) +- **Symptom**: `copilotkit_emit_message`, `copilotkit_emit_state`, `copilotkit_emit_tool_call` never reach the frontend +- **Cause**: Python SDK dispatches events with `"copilotkit_"` prefix but `ag-ui-langgraph` expects names without the prefix +- **Resolution**: Update `ag-ui-langgraph` or patch the event name mapping + +### Tool Call Failing Silently +- **Issue**: [#3510](https://github.com/CopilotKit/CopilotKit/issues/3510) +- **Symptom**: `defineTool` tool calls fail without error or response +- **Resolution**: Check tool parameter schema validation and network responses + +### Reasoning Events Cause Agent Stall +- **Issue**: [#3323](https://github.com/CopilotKit/CopilotKit/issues/3323) +- **Symptom**: Agent stalls permanently after Anthropic reasoning/thinking tokens +- **Cause**: `REASONING_*` events in the AG-UI SSE stream are not handled correctly +- **Resolution**: Update to a version with reasoning event handling fixes + +### HITL Frontend Tool Not Executing After Confirmation +- **Issue**: [#3442](https://github.com/CopilotKit/CopilotKit/issues/3442) +- **Symptom**: `useFrontendTool` with `renderAndWaitForResponse` does not execute after user confirms +- **Resolution**: Check the HITL flow implementation and `runId` consistency (related: #3456) + +### Authorization Header Not Passed to A2A Agents +- **Issue**: [#3170](https://github.com/CopilotKit/CopilotKit/issues/3170) +- **Symptom**: Auth headers from the client do not reach agents using A2A protocol +- **Resolution**: Verify header forwarding configuration in runtime middleware + +### LangChainAdapter Regression ("Unknown provider undefined") +- **Issue**: [#3217](https://github.com/CopilotKit/CopilotKit/issues/3217) +- **Symptom**: `LangChainAdapter` throws "Unknown provider undefined" in v1.50.0+ +- **Cause**: Custom adapters without `provider`/`model` properties hit a code path that assumes they exist +- **Resolution**: Migrate to v2 `BuiltInAgent` or add `.provider`/`.model` to the adapter + +### Mixed Frontend and Backend Tool Execution Fails +- **Issue**: [#3424](https://github.com/CopilotKit/CopilotKit/issues/3424) +- **Symptom**: OpenAI `BadRequestError` when mixing frontend and backend tools with LangGraph +- **Resolution**: Check tool registration and ensure tools are not duplicated across frontend and backend + +### Context Not Updated with Mastra Integration +- **Issue**: [#3426](https://github.com/CopilotKit/CopilotKit/issues/3426) +- **Symptom**: Context state does not propagate to Mastra agents +- **Resolution**: Verify context is being passed through the runtime middleware chain + +### Subscribe Null Reference in A2A/A2UI +- **Issue**: [#3429](https://github.com/CopilotKit/CopilotKit/issues/3429) +- **Symptom**: `Cannot read properties of null (reading 'subscribe')` during A2A integration +- **Resolution**: Check agent lifecycle and ensure proper initialization order + +### IME Input Cleared on Mobile (v2) +- **Issue**: [#3318](https://github.com/CopilotKit/CopilotKit/issues/3318) +- **Symptom**: Typing with IME on mobile devices clears input in CopilotChat +- **Resolution**: Known v2 issue with controlled input handling during IME composition + +### Message ID Collision with OpenAI-Compatible Providers +- **Issue**: [#3410](https://github.com/CopilotKit/CopilotKit/issues/3410) +- **Symptom**: All messages share the same ID when using `@ai-sdk/openai-compatible` +- **Cause**: Default message ID from the compatible provider is not unique +- **Resolution**: Update to a patched version or use the native OpenAI provider diff --git a/skills/copilotkit-debug/references/quick-workflows.md b/skills/copilotkit-debug/references/quick-workflows.md new file mode 100644 index 00000000000..c31c55c945e --- /dev/null +++ b/skills/copilotkit-debug/references/quick-workflows.md @@ -0,0 +1,284 @@ +# Quick Diagnostic Workflows + +## Workflow: "Runtime Not Connecting" + +The client shows a connection error, banner error, or the chat never loads. + +### Step 1: Verify the runtime is running + +```bash +curl -v http://localhost:3001/api/copilotkit/info +``` + +- **No response / connection refused** -> The server is not running. Start it. +- **404** -> The basePath is wrong. Check `createCopilotEndpoint({ basePath })` vs the URL you are hitting. +- **500** -> The agent loading failed. Check server logs for the error. +- **200 with JSON** -> Runtime is up. Proceed to step 2. + +### Step 2: Check the client configuration + +```tsx + +``` + +- Does `runtimeUrl` match the runtime's basePath exactly? +- If cross-origin (e.g., runtime on port 3001, app on port 3000), is CORS configured? +- If using a proxy (Next.js rewrites, nginx), does the proxy preserve the full path? + +### Step 3: Check browser network tab + +1. Look for the GET request to `/info` +2. If it is blocked by CORS, you will see a preflight OPTIONS failure +3. If it returns an error, the error body contains the `CopilotKitErrorCode` + +### Step 4: Check package versions + +```bash +npm ls @copilotkit/runtime @copilotkit/react @copilotkit/core @ag-ui/client +``` + +All `@copilotkit/*` packages should be the same version. Mismatches cause `VERSION_MISMATCH` errors. + +### Step 5: Check CORS (if cross-origin) + +Default CORS allows all origins without credentials. If you need credentials: + +```ts +createCopilotEndpoint({ + runtime, + basePath: "/api/copilotkit", + cors: { + origin: "https://your-frontend.com", + credentials: true, + }, +}); +``` + +And on the client: +```tsx + +``` + +--- + +## Workflow: "Agent Not Responding" + +The chat connects but messages are never answered, or the agent returns an error. + +### Step 1: Verify agent is registered + +```bash +curl http://localhost:3001/api/copilotkit/info | jq '.agents' +``` + +Check that the agent name matches the `agentId` prop in `CopilotChat` or `useAgent`. + +### Step 2: Check the SSE stream + +1. Open browser DevTools > Network tab +2. Send a message in the chat +3. Find the POST to `/agent/:agentId/run` +4. Check the response: + - **404** -> Agent not found in runtime + - **500** -> Server error during agent execution + - **200 with empty body** -> Agent started but produced no events + - **200 with events** -> Check the events (step 3) + +### Step 3: Inspect the event stream + +Look at the SSE events in the response: + +- **Only `RunStartedEvent` then nothing** -> Agent is stalled. Check server logs. Common causes: + - Missing LLM API key (agent cannot call the model) + - Agent waiting for a tool result that never comes + - Reasoning event stall (Anthropic models, issue #3323) + +- **`RunErrorEvent` present** -> Read the error message. Common causes: + - LLM API returned an error (rate limit, invalid key, model not found) + - Agent code threw an exception + +- **`RunFinishedEvent` without text messages** -> Agent completed but produced no output. Check the agent's prompt and logic. + +### Step 4: Check LLM API key + +For `BuiltInAgent`, verify the environment variable: + +| Provider | Environment Variable | +|----------|---------------------| +| OpenAI | `OPENAI_API_KEY` | +| Anthropic | `ANTHROPIC_API_KEY` | +| Google | `GOOGLE_API_KEY` | +| Vertex | Application Default Credentials | + +### Step 5: Check the agent's model string + +```ts +new BuiltInAgent({ + model: "openai/gpt-4o", // Must be "provider/model-name" +}); +``` + +Invalid model strings throw `Error: Invalid model string "..."` or `Error: Unknown provider "..."`. + +### Step 6: Check server-side logs + +The SSE response handler logs errors with full stack traces: +``` +Error running agent: +Error stack: +Error details: { name, message, cause } +``` + +--- + +## Workflow: "Streaming Failures" + +The agent starts responding but the stream cuts off, duplicates events, or corrupts messages. + +### Step 1: Check for premature stream termination + +1. Look at the SSE response in the Network tab +2. Does it end with `RunFinishedEvent`? If not: + - **Connection closed mid-stream** -> Hosting platform timeout (Vercel: 30s default, Railway: 5min). Consider using Intelligence mode for long-running agents. + - **Error in the stream** -> Check for `RunErrorEvent` before the cutoff + - **Client navigated away** -> Expected behavior, the `abort` signal cleaned up the stream + +### Step 2: Check for event ordering issues + +Events must follow a logical sequence: +- `TextMessageStart` before `TextMessageChunk` before `TextMessageEnd` +- `ToolCallStart` before `ToolCallArgs` before `ToolCallEnd` +- `RunStarted` at the beginning, `RunFinished` at the end + +If events are out of order, the issue is in the agent's Observable implementation. + +### Step 3: Check for duplicate events + +If the same message appears multiple times: +- **Message ID collision** -> Check issue #3410 (OpenAI-compatible providers reusing IDs) +- **Agent re-running** -> The `runId` changed mid-conversation. Check for HITL issues (issue #3456). + +### Step 4: Check for message corruption + +If message content is garbled or mixed: +- **Model-specific issue** -> DeepSeek and some models produce malformed streaming chunks (issue #3351) +- **Encoding issue** -> Verify the SSE response has `Content-Type: text/event-stream` and is UTF-8 + +### Step 5: Check hosting platform limits + +| Platform | Default SSE Timeout | Notes | +|----------|-------------------|-------| +| Vercel (Serverless) | 30s (Hobby), 60s (Pro) | Use Edge Runtime or Intelligence mode | +| Vercel (Edge) | 30s | Better but still limited | +| Railway | 5 min | Usually sufficient | +| Render | 5 min | Usually sufficient | +| Self-hosted | No limit | Depends on reverse proxy config | + +For long agent runs, consider: +- Intelligence mode (persisted threads, WebSocket updates) +- Increasing the platform timeout if possible +- Breaking the agent work into smaller runs + +--- + +## Workflow: "Frontend Tool Not Working" + +A frontend tool registered with `useFrontendTool` is not being called or not returning results. + +### Step 1: Verify tool registration + +Check that the tool is registered before the agent runs: +```tsx +useFrontendTool({ + name: "get_weather", // Must match exactly what the agent calls + description: "Get weather", + parameters: z.object({ city: z.string() }), + execute: async ({ city }) => { /* ... */ }, +}); +``` + +### Step 2: Check the SSE stream for tool events + +Look for `ToolCallStartEvent` in the SSE stream: +- **Not present** -> The agent decided not to call the tool. Check the tool description. +- **Present but no `ToolCallResultEvent`** -> The frontend did not respond. Check: + - Is the component with `useFrontendTool` mounted? + - Did the `execute` handler throw? (Check `tool_handler_failed` error) + - Is the tool name an exact match (case-sensitive)? + +### Step 3: Check tool argument parsing + +If `tool_argument_parse_failed` error appears: +- The LLM generated arguments that do not match the Zod/JSON schema +- Check `ToolCallArgsEvent` for the raw arguments +- Consider relaxing the schema or improving parameter descriptions + +### Step 4: Check HITL tool flow + +For `renderAndWaitForResponse` tools: +- The tool renders UI and waits for user input +- If the tool does not execute after user confirmation, check issue #3442 +- The `runId` may change after HITL resolve (issue #3456) + +--- + +## Workflow: "Transcription Not Working" + +Voice input fails or produces errors. + +### Step 1: Check transcription service configuration + +```ts +const runtime = new CopilotRuntime({ + agents: { /* ... */ }, + transcriptionService: myTranscriptionService, // Must be provided +}); +``` + +If not configured, the error code is `service_not_configured` (HTTP 503). + +### Step 2: Check the `/info` response + +```bash +curl http://localhost:3001/api/copilotkit/info | jq '.audioFileTranscriptionEnabled' +``` + +Should be `true`. If `false`, the transcription service is not configured. + +### Step 3: Check browser microphone permissions + +- The browser must grant microphone access +- `AudioRecorderError: "Microphone permission denied"` -> User denied permission +- `AudioRecorderError: "No microphone found"` -> No microphone hardware detected + +### Step 4: Check transcription provider credentials + +- `auth_failed` -> API key is invalid or expired +- `rate_limited` -> Too many requests, wait and retry +- `provider_error` -> Provider-side issue, check provider status page + +### Step 5: Check audio format + +- `invalid_audio_format` -> Browser sends unsupported format +- `audio_too_long` / `audio_too_short` -> Recording duration out of bounds + +--- + +## Escalation Path + +If the issue is unresolved after following these workflows: + +1. **Check the CopilotKit GitHub Issues**: Search https://github.com/CopilotKit/CopilotKit/issues for your error message or symptom. + +2. **Enable the Web Inspector**: Add `` to capture detailed event traces. + +3. **Collect a diagnostic bundle**: + - Package versions (`npm ls @copilotkit/*`) + - Runtime `/info` response + - SSE stream capture (copy from Network tab) + - Server-side error logs + - Browser console errors + +4. **File a GitHub issue**: https://github.com/CopilotKit/CopilotKit/issues/new with the diagnostic bundle. + +5. **Reach out to the CopilotKit team**: Book time with the CopilotKit team via their Discord (https://discord.gg/copilotkit) or contact support for urgent production issues. diff --git a/skills/copilotkit-debug/references/runtime-debugging.md b/skills/copilotkit-debug/references/runtime-debugging.md new file mode 100644 index 00000000000..df8dad9b545 --- /dev/null +++ b/skills/copilotkit-debug/references/runtime-debugging.md @@ -0,0 +1,230 @@ +# Runtime Debugging Reference + +## Runtime Architecture + +CopilotKit v2 runtime (`@copilotkit/runtime`) runs as a Hono HTTP server. It exposes these endpoints under the configured `basePath`: + +| Endpoint | Method | Purpose | +|----------|--------|---------| +| `/info` | GET | Runtime discovery -- returns version, agent list, capabilities | +| `/agent/:agentId/run` | POST | Start an agent run, returns SSE event stream | +| `/agent/:agentId/connect` | POST | Connect to an existing agent run (Intelligence mode) | +| `/agent/:agentId/stop` | POST | Stop a running agent | +| `/transcribe` | POST | Audio transcription | +| `/threads` | GET/POST/PATCH/DELETE | Thread management (Intelligence mode only) | + +## Runtime Modes + +### SSE Mode (`"sse"`) +- Default mode. Agent runs are ephemeral. +- Each `/agent/:id/run` request creates a new run and streams AG-UI events as SSE. +- Uses `InMemoryAgentRunner` by default. +- No thread persistence -- state lives only for the duration of the SSE connection. + +### Intelligence Mode (`"intelligence"`) +- Requires `CopilotKitIntelligence` configuration with `apiUrl`, `wsUrl`, `apiKey`, `tenantId`. +- Agent runs are durable -- threads are persisted on the Intelligence platform. +- Uses `IntelligenceAgentRunner` which coordinates via WebSocket. +- Supports thread listing, archiving, deletion, and real-time updates. +- Requires `identifyUser` callback to resolve authenticated users. + +## Connectivity Debugging + +### "Runtime not found" / 404 Errors + +1. **Verify the runtime is running**: Hit the `/info` endpoint directly: + ```bash + curl http://localhost:3001/api/copilotkit/info + ``` + Expected response: JSON with `version`, `agents`, `mode` fields. + +2. **Check basePath alignment**: The `basePath` in `createCopilotEndpoint()` must match the `runtimeUrl` in `CopilotKitProvider`: + ```ts + // Server + createCopilotEndpoint({ runtime, basePath: "/api/copilotkit" }); + + // Client + + ``` + +3. **Check the Hono app mounting**: If using a framework adapter (Next.js, Express), ensure the Hono app is mounted at the right path. The framework's route path combined with `basePath` must form the full URL. + +4. **Proxy/reverse proxy issues**: If running behind nginx, Vercel, or similar, ensure the proxy passes the full path and does not strip the prefix. + +### Connection Refused (ECONNREFUSED) + +- The runtime server is not running on the expected host:port. +- Check `process.env.PORT` or the server's listen configuration. +- If using Docker, ensure the port is exposed and the container is running. + +### DNS Resolution Failed (ENOTFOUND) + +- The hostname in `runtimeUrl` cannot be resolved. +- Check for typos in the URL. +- If using service discovery (Kubernetes, Docker Compose), verify the service name is correct. + +### Timeout (ETIMEDOUT) + +- Server is reachable but not responding in time. +- Check server load and resource limits. +- Increase timeout if the agent's first response takes a while (large model, cold start). + +## CORS Debugging + +### Default CORS Behavior + +When no `cors` option is provided to `createCopilotEndpoint`, the runtime defaults to: +- `origin: "*"` (all origins allowed) +- `credentials: false` +- All standard HTTP methods allowed +- All headers allowed + +### CORS with Credentials (HTTP-only Cookies) + +When using HTTP-only cookies for authentication, you must configure CORS explicitly: + +```ts +createCopilotEndpoint({ + runtime, + basePath: "/api/copilotkit", + cors: { + origin: "https://myapp.com", // Must be explicit, not "*" + credentials: true, + }, +}); +``` + +On the client side, enable credentials: +```tsx + +``` + +### Common CORS Errors + +| Browser Error | Cause | Fix | +|---------------|-------|-----| +| "No 'Access-Control-Allow-Origin' header" | Runtime not sending CORS headers | Verify `createCopilotEndpoint` is handling the request (not a 404 from another handler) | +| "Credential is not supported if origin is '*'" | `credentials: true` with wildcard origin | Set an explicit `origin` in the CORS config | +| "Method PUT is not allowed" | Preflight failure | Ensure the runtime's CORS allows the method (default config allows all) | +| CORS error only in production | Different origins in dev vs prod | Update the `origin` config for the production domain | + +### Diagnosing CORS Issues + +1. Open browser DevTools Network tab +2. Look for a failed OPTIONS (preflight) request to the runtime URL +3. Check the response headers -- `Access-Control-Allow-Origin`, `Access-Control-Allow-Credentials`, `Access-Control-Allow-Headers` +4. If no OPTIONS request appears, the browser may be making a "simple request" that still fails on the response headers + +## SSE Streaming Debugging + +### How SSE Works in CopilotKit + +The `/agent/:agentId/run` endpoint returns an SSE response: +- Content-Type: `text/event-stream` +- Cache-Control: `no-cache` +- Connection: `keep-alive` + +Events are encoded using `@ag-ui/encoder` (the `EventEncoder` class). Each event is a `data:` line in SSE format. + +### Stream Never Starts + +- **Agent not found**: The agent ID in the URL does not match any registered agent. Check the `/info` endpoint. +- **Middleware blocking**: A `beforeRequestMiddleware` might be throwing or returning an error response before the agent runs. +- **Agent constructor failure**: The agent's initialization might throw (e.g., missing API key). Check server-side logs. + +### Stream Starts but Hangs + +- **Agent waiting for tool result**: If the agent calls a frontend tool and the frontend does not respond, the stream will appear hung. Check that frontend tools are registered and responding. +- **Reasoning event stall**: Anthropic models with reasoning/thinking tokens can cause stalls if the event handler does not properly process `REASONING_*` events (issue #3323). +- **Backpressure**: If the client reads slowly, the `TransformStream` writer may block. This is rare with SSE but possible with very high event rates. + +### Stream Ends Prematurely + +- **Client disconnect**: If the browser tab is closed or the network drops, the `request.signal` aborts and the subscription is cleaned up. +- **Agent error**: An uncaught exception in the agent terminates the observable. Check for `RunErrorEvent` before the stream closes. +- **Server timeout**: Some hosting platforms (Vercel, Railway) have response timeouts. Long-running agent interactions may hit these limits. + +### Debugging SSE in the Browser + +1. Open DevTools > Network tab +2. Find the POST request to `/agent/:id/run` +3. Click the "EventStream" tab (Chrome) or check the Response tab for raw SSE data +4. Each event should be formatted as: + ``` + data: {"type":"RunStarted","runId":"..."} + + data: {"type":"TextMessageStart","messageId":"..."} + + data: {"type":"TextMessageChunk","delta":"Hello"} + ``` +5. If events stop flowing, the issue is server-side (agent stalled or errored) + +## Runtime Info Endpoint Debugging + +The `/info` endpoint is the first request the client makes. If it fails, no agent interaction is possible. + +### Expected Response Shape + +```json +{ + "version": "1.52.0", + "agents": { + "myAgent": { + "name": "myAgent", + "description": "My agent description", + "className": "BuiltInAgent" + } + }, + "audioFileTranscriptionEnabled": false, + "mode": "sse", + "a2uiEnabled": false +} +``` + +For Intelligence mode, the response also includes: +```json +{ + "intelligence": { + "wsUrl": "wss://api.copilotkit.ai/client" + } +} +``` + +### Common `/info` Failures + +- **500 error**: The `agents` promise rejected (lazy agent loading failed). Check the agents factory function. +- **404 error**: Wrong basePath or the runtime is not mounted at the expected URL. +- **CORS error**: The preflight for `/info` failed. See CORS section above. + +## Custom Headers and Authentication + +### Passing Headers from Client to Runtime + +```tsx + +``` + +Headers are sent with every request to the runtime, including `/info`, `/agent/:id/run`, etc. + +### Accessing Headers in Middleware + +```ts +const runtime = new CopilotRuntime({ + agents: { /* ... */ }, + beforeRequestMiddleware: async ({ request }) => { + const auth = request.headers.get("Authorization"); + // Validate auth, modify request, or throw to reject + return request; + }, +}); +``` + +### Header Forwarding to Agents + +Headers from the client are available in the runtime middleware but are NOT automatically forwarded to remote agents (A2A). This is a known limitation (issue #3170 and #3425). To forward headers, use middleware to inject them into the agent configuration. diff --git a/skills/copilotkit-debug/sources.md b/skills/copilotkit-debug/sources.md new file mode 100644 index 00000000000..f383a2e2830 --- /dev/null +++ b/skills/copilotkit-debug/sources.md @@ -0,0 +1,35 @@ +# Sources + +Files and directories read from CopilotKit/CopilotKit to generate this skill's references. +Generated: 2026-03-28 + +## error-patterns.md +- packages/v1/shared/src/utils/errors.ts (CopilotKitErrorCode enum, all v1 error classes: CopilotKitError, CopilotKitMisuseError, CopilotKitVersionMismatchError, CopilotKitApiDiscoveryError, CopilotKitRemoteEndpointDiscoveryError, CopilotKitAgentDiscoveryError, CopilotKitLowLevelError, ResolvedCopilotKitError, ConfigurationError, MissingPublicApiKeyError, UpgradeRequiredError) +- packages/v2/core/src/core/core.ts (CopilotKitCoreErrorCode enum: runtime_info_fetch_failed, agent_connect_failed, agent_run_failed, tool_argument_parse_failed, tool_handler_failed, tool_not_found, agent_not_found, transcription error codes) +- packages/v2/shared/src/transcription-errors.ts (TranscriptionErrorCode enum) +- packages/v2/runtime/src/intelligence-platform/client.ts (PlatformRequestError, HTTP status codes 404/409/401/500) +- GitHub issues: #3519, #3510, #3323, #3442, #3170, #3217, #3424, #3426, #3429, #3318, #3410 + +## runtime-debugging.md +- packages/v2/runtime/src/ (CopilotRuntime, endpoint factories, route definitions, SSE streaming, /info endpoint response shape) +- packages/v2/runtime/src/endpoints/ (CORS configuration, Hono middleware, Express middleware) +- packages/v2/runtime/src/intelligence-platform/ (CopilotKitIntelligence, IntelligenceAgentRunner, WebSocket URLs) +- packages/v2/runtime/src/runner/ (InMemoryAgentRunner, AgentRunner abstract class) +- packages/v2/react/src/ (CopilotKitProvider props: runtimeUrl, credentials, headers) +- GitHub issues: #3170, #3425 + +## agent-debugging.md +- packages/v2/agent/src/ (BuiltInAgent, resolveModel, model string formats, MCP client configuration) +- packages/v2/runtime/src/ (AgentRunner, agent registry, /info endpoint agent discovery) +- packages/v2/core/src/ (CopilotKitCoreErrorCode, tool registry, onError subscriber) +- packages/v2/react/src/ (useFrontendTool, useAgent, CopilotChat agentId prop) +- packages/v2/web-inspector/src/ (CopilotKitWebInspector component) +- GitHub issues: #3323, #3519, #3231, #3456, #3424, #3426, #3198 + +## quick-workflows.md +- packages/v2/runtime/src/ (endpoint route structure, /info endpoint, CORS defaults, SSE event flow) +- packages/v2/agent/src/ (BuiltInAgent model string format, environment variable conventions) +- packages/v2/core/src/ (error codes referenced in diagnostic steps) +- packages/v2/react/src/ (CopilotKitProvider props, useFrontendTool registration, CopilotChat) +- packages/v2/shared/src/ (TranscriptionErrorCode, transcription service configuration) +- packages/v2/web-inspector/src/ (CopilotKitWebInspector for escalation) diff --git a/skills/copilotkit-develop/SKILL.md b/skills/copilotkit-develop/SKILL.md new file mode 100644 index 00000000000..2891f0a234c --- /dev/null +++ b/skills/copilotkit-develop/SKILL.md @@ -0,0 +1,182 @@ +--- +name: copilotkit-develop +description: "Use when building AI-powered features with CopilotKit v2 -- adding chat interfaces, registering frontend tools, sharing application context with agents, handling agent interrupts, and working with the CopilotKit runtime." +version: 1.0.0 +--- + +# CopilotKit v2 Development Skill + +## Live Documentation (MCP) + +This plugin includes an MCP server (`copilotkit-docs`) that provides `search-docs` and `search-code` tools for querying live CopilotKit documentation and source code. + +- **Claude Code:** Auto-configured by the plugin's `.mcp.json` -- no setup needed. +- **Codex:** Requires manual configuration. See the [copilotkit-debug skill](../copilotkit-debug/SKILL.md#mcp-setup) for setup instructions. + +## Architecture Overview + +CopilotKit v2 is built on the AG-UI protocol (`@ag-ui/client` / `@ag-ui/core`). The stack has three layers: + +1. **Runtime** (`@copilotkit/runtime`) -- Server-side. Hosts agents, handles SSE/Intelligence transport, middleware, transcription. +2. **Core** (`@copilotkit/core`) -- Shared state management, tool registry, suggestion engine. Not imported directly by apps. +3. **React** (`@copilotkit/react`) -- Provider, chat components, hooks. Re-exports everything from `@ag-ui/client` so apps need only one import. + +## Workflow + +### 1. Set Up the Runtime (Server) + +Create a `CopilotRuntime` (or the explicit `CopilotSseRuntime` / `CopilotIntelligenceRuntime`) and expose it via `createCopilotEndpoint` (Hono) or `createCopilotEndpointExpress` (Express). + +```ts +import { CopilotRuntime, createCopilotEndpoint } from "@copilotkit/runtime"; +import { LangGraphAgent } from "@ag-ui/langgraph"; + +const runtime = new CopilotRuntime({ + agents: { + myAgent: new LangGraphAgent({ /* ... */ }), + }, +}); + +const app = createCopilotEndpoint({ + runtime, + basePath: "/api/copilotkit", +}); +``` + +### 2. Wrap Your App with the Provider (Client) + +```tsx +import { CopilotKitProvider } from "@copilotkit/react"; + +function App() { + return ( + + + + ); +} +``` + +### 3. Add a Chat UI + +Use ``, ``, or ``: + +```tsx +import { CopilotChat } from "@copilotkit/react"; + +function ChatPage() { + return ; +} +``` + +### 4. Register Frontend Tools + +Let the agent call functions in the browser: + +```tsx +import { useFrontendTool } from "@copilotkit/react"; +import { z } from "zod"; + +useFrontendTool({ + name: "highlightCell", + description: "Highlight a spreadsheet cell", + parameters: z.object({ row: z.number(), col: z.number() }), + handler: async ({ row, col }) => { + highlightCell(row, col); + return "done"; + }, +}); +``` + +### 5. Share Application Context + +Provide runtime data to the agent: + +```tsx +import { useAgentContext } from "@copilotkit/react"; + +useAgentContext({ + description: "The user's current shopping cart", + value: cart, // any JSON-serializable value +}); +``` + +### 6. Handle Agent Interrupts + +When an agent pauses for human input: + +```tsx +import { useInterrupt } from "@copilotkit/react"; + +useInterrupt({ + render: ({ event, resolve }) => ( +
+

{event.value.question}

+ +
+ ), +}); +``` + +### 7. Render Tool Calls in Chat + +Show custom UI when tools execute: + +```tsx +import { useRenderTool } from "@copilotkit/react"; +import { z } from "zod"; + +useRenderTool({ + name: "searchDocs", + parameters: z.object({ query: z.string() }), + render: ({ status, parameters, result }) => { + if (status === "executing") return Searching {parameters.query}...; + if (status === "complete") return ; + return
Preparing...
; + }, +}, []); +``` + +## Quick Reference: Hooks + +| Hook | Purpose | +|------|---------| +| `useFrontendTool` | Register a tool the agent can call in the browser | +| `useComponent` | Register a React component as a chat-rendered tool (convenience wrapper around `useFrontendTool`) | +| `useAgentContext` | Share JSON-serializable application state with the agent | +| `useAgent` | Get the `AbstractAgent` instance for an agent ID; subscribe to message/state/run-status changes | +| `useInterrupt` | Handle `on_interrupt` events from agents with render + optional handler/filter | +| `useHumanInTheLoop` | Register a tool that pauses execution until the user responds via a rendered UI | +| `useRenderTool` | Register a renderer for tool calls (by name or wildcard `"*"`) | +| `useDefaultRenderTool` | Register a wildcard `"*"` renderer using the built-in expandable card UI | +| `useRenderToolCall` | Internal hook returning a function to resolve the correct renderer for a given tool call | +| `useRenderActivityMessage` | Internal hook for rendering activity messages by type | +| `useRenderCustomMessages` | Internal hook for rendering custom message decorators | +| `useSuggestions` | Read the current suggestion list and control reload/clear | +| `useConfigureSuggestions` | Register static or dynamic (LLM-generated) suggestion configs | +| `useThreads` | List, rename, archive, and delete Intelligence platform threads | + +## Quick Reference: Components + +| Component | Purpose | +|-----------|---------| +| `CopilotKitProvider` | Root provider -- configures runtime URL, headers, agents, error handler | +| `CopilotChat` | Full chat interface connected to an agent (inline layout) | +| `CopilotPopup` | Chat in a floating popup with toggle button | +| `CopilotSidebar` | Chat in a collapsible sidebar with toggle button | +| `CopilotChatView` | Headless chat view with slots for message view, input, scroll, suggestions | +| `CopilotChatInput` | Chat input textarea with send/stop/transcribe controls | +| `CopilotChatMessageView` | Renders the message list | +| `CopilotChatSuggestionView` | Renders suggestion pills | + +## Quick Reference: Runtime + +| Export | Purpose | +|--------|---------| +| `CopilotRuntime` | Auto-detecting runtime (delegates to SSE or Intelligence) | +| `CopilotSseRuntime` | Explicit SSE-mode runtime | +| `CopilotIntelligenceRuntime` | Intelligence-mode runtime with durable threads | +| `createCopilotEndpoint` | Create a Hono app with all CopilotKit routes | +| `createCopilotEndpointExpress` | Create an Express router with all CopilotKit routes | +| `CopilotKitIntelligence` | Intelligence platform client configuration | + diff --git a/skills/copilotkit-develop/eval.yaml b/skills/copilotkit-develop/eval.yaml new file mode 100644 index 00000000000..17c9fd6ebb1 --- /dev/null +++ b/skills/copilotkit-develop/eval.yaml @@ -0,0 +1,198 @@ +version: "1" + +defaults: + agent: claude + provider: docker + trials: 3 + timeout: 300 + threshold: 0.8 + docker: + base: node:20-slim + setup: | + apt-get update && apt-get install -y git jq + +tasks: + - name: add-chat-interface + description: "Add a chat interface to an existing CopilotKit project" + instruction: "Add a CopilotChat component to this existing CopilotKit project. The chat should appear as a sidebar." + graders: + - type: deterministic + check: file_contains + path: "src/app/page.tsx" + pattern: "CopilotSidebar" + - type: deterministic + check: file_contains + path: "src/app/page.tsx" + pattern: "CopilotKitProvider" + - type: llm_rubric + prompt: "Is CopilotSidebar properly imported from @copilotkit/react and rendered within a CopilotKitProvider that has a runtimeUrl configured?" + + - name: add-popup-chat + description: "Add a floating popup chat to a CopilotKit project" + instruction: "Add a CopilotPopup chat interface to this project. It should be open by default and have a custom header title of 'AI Assistant'." + graders: + - type: deterministic + check: file_contains + path: "src/" + pattern: "CopilotPopup" + - type: llm_rubric + prompt: "Does the code use CopilotPopup from @copilotkit/react with defaultOpen={true} and a custom labels prop setting modalHeaderTitle to 'AI Assistant'?" + + - name: register-frontend-tool + description: "Register a frontend tool for the AI to use" + instruction: "Add a frontend tool called 'setTheme' that lets the AI change the app theme between 'light' and 'dark'. The tool should accept a 'theme' parameter validated with Zod." + graders: + - type: deterministic + check: file_contains + path: "src/" + pattern: "useFrontendTool" + - type: deterministic + check: file_contains + path: "src/" + pattern: "setTheme" + - type: llm_rubric + prompt: "Does the code correctly use useFrontendTool with name 'setTheme', a Zod schema defining a 'theme' parameter constrained to 'light' or 'dark', a description, and a handler function?" + + - name: share-agent-context + description: "Share application state with the agent using useAgentContext" + instruction: "Use useAgentContext to share the current user's profile and shopping cart data with the AI agent so it can reference them in conversation." + graders: + - type: deterministic + check: file_contains + path: "src/" + pattern: "useAgentContext" + - type: llm_rubric + prompt: "Does the code call useAgentContext with a description and a JSON-serializable value containing user profile and cart data? Is the context properly structured so the agent understands what it represents?" + + - name: handle-agent-interrupt + description: "Handle an agent interrupt with a confirmation UI" + instruction: "Add interrupt handling so when the agent triggers an interrupt, the user sees a confirmation dialog with 'Approve' and 'Reject' buttons that resolve the interrupt." + graders: + - type: deterministic + check: file_contains + path: "src/" + pattern: "useInterrupt" + - type: llm_rubric + prompt: "Does the code use useInterrupt with a render function that displays the interrupt event, provides both Approve and Reject buttons, and calls resolve() with the user's decision?" + + - name: render-tool-calls + description: "Add custom rendering for tool calls in the chat" + instruction: "Use useRenderTool to show a custom UI when the 'searchDocs' tool executes. Show a spinner during execution and formatted results on completion." + graders: + - type: deterministic + check: file_contains + path: "src/" + pattern: "useRenderTool" + - type: deterministic + check: file_contains + path: "src/" + pattern: "searchDocs" + - type: llm_rubric + prompt: "Does the code register a renderer for 'searchDocs' using useRenderTool with a Zod parameters schema, and handle all three statuses (inProgress, executing, complete) with appropriate UI for each?" + + - name: setup-runtime-nextjs + description: "Set up a CopilotKit runtime endpoint in Next.js" + instruction: "Create a Next.js API route at app/api/copilotkit/[[...path]]/route.ts that sets up a CopilotRuntime with a LangGraphAgent and exposes it via createCopilotEndpoint." + graders: + - type: deterministic + check: file_exists + path: "app/api/copilotkit/[[...path]]/route.ts" + - type: deterministic + check: file_contains + path: "app/api/copilotkit/[[...path]]/route.ts" + pattern: "CopilotRuntime" + - type: deterministic + check: file_contains + path: "app/api/copilotkit/[[...path]]/route.ts" + pattern: "createCopilotEndpoint" + - type: llm_rubric + prompt: "Does the route file create a CopilotRuntime with agents, call createCopilotEndpoint with the correct basePath, and export GET and POST handlers from app.fetch?" + + - name: setup-runtime-express + description: "Set up a CopilotKit runtime with Express" + instruction: "Create an Express server that sets up a CopilotRuntime and mounts it using createCopilotEndpointExpress at /api/copilotkit." + graders: + - type: deterministic + check: file_contains + path: "src/" + pattern: "createCopilotEndpointExpress" + - type: deterministic + check: file_contains + path: "src/" + pattern: "CopilotRuntime" + - type: llm_rubric + prompt: "Does the code create an Express app, instantiate CopilotRuntime with agents, and mount createCopilotEndpointExpress with basePath '/api/copilotkit' using app.use()?" + + - name: human-in-the-loop + description: "Add a human-in-the-loop tool for agent approval workflows" + instruction: "Register a human-in-the-loop tool called 'approveOrder' that pauses agent execution and shows the order details with approve/reject options until the user responds." + graders: + - type: deterministic + check: file_contains + path: "src/" + pattern: "useHumanInTheLoop" + - type: deterministic + check: file_contains + path: "src/" + pattern: "approveOrder" + - type: llm_rubric + prompt: "Does the code use useHumanInTheLoop with name 'approveOrder', a parameters schema, and a render function that handles the 'executing' status with a respond callback for approve/reject?" + + - name: customize-chat-labels + description: "Customize chat UI text and labels" + instruction: "Customize the CopilotChat labels to change the input placeholder to 'Ask me about your data...', the welcome message to 'Welcome! I can help analyze your data.', and the header title to 'Data Assistant'." + graders: + - type: deterministic + check: file_contains + path: "src/" + pattern: "labels" + - type: llm_rubric + prompt: "Does the code pass a labels prop to CopilotChat with chatInputPlaceholder, welcomeMessageText, and modalHeaderTitle set to the specified custom values?" + + - name: configure-suggestions + description: "Set up AI-generated chat suggestions" + instruction: "Configure dynamic LLM-generated suggestions for the chat that suggest follow-up questions about the user's data. Show up to 3 suggestions, available after the first message." + graders: + - type: deterministic + check: file_contains + path: "src/" + pattern: "useConfigureSuggestions" + - type: llm_rubric + prompt: "Does the code call useConfigureSuggestions with instructions, maxSuggestions set to 3, and available set to 'after-first-message'?" + + - name: register-component-tool + description: "Register a React component as a visual tool in chat" + instruction: "Use useComponent to register a 'dataChart' component that the agent can invoke to display a bar chart in the chat. It should accept a title and data array as parameters." + graders: + - type: deterministic + check: file_contains + path: "src/" + pattern: "useComponent" + - type: deterministic + check: file_contains + path: "src/" + pattern: "dataChart" + - type: llm_rubric + prompt: "Does the code use useComponent with name 'dataChart', a parameters schema with title and data fields, and a render function that displays a chart component?" + + - name: add-middleware + description: "Add request middleware to the CopilotKit runtime" + instruction: "Add beforeRequestMiddleware to the CopilotRuntime that logs incoming requests and validates an API key from the Authorization header." + graders: + - type: deterministic + check: file_contains + path: "src/" + pattern: "beforeRequestMiddleware" + - type: llm_rubric + prompt: "Does the code configure beforeRequestMiddleware on CopilotRuntime that accesses the request, logs the path, and checks the Authorization header for a valid API key?" + + - name: error-handling + description: "Set up error handling at provider and chat levels" + instruction: "Configure error handling for CopilotKit at both the provider level (global error logging) and the chat level (showing a toast notification to the user)." + graders: + - type: deterministic + check: file_contains + path: "src/" + pattern: "onError" + - type: llm_rubric + prompt: "Does the code set onError on both CopilotKitProvider (for global error logging) and CopilotChat (for user-facing notifications), with both handlers receiving error, code, and context?" diff --git a/skills/copilotkit-develop/references/api-surface.md b/skills/copilotkit-develop/references/api-surface.md new file mode 100644 index 00000000000..2aa27341fcb --- /dev/null +++ b/skills/copilotkit-develop/references/api-surface.md @@ -0,0 +1,489 @@ +# CopilotKit v2 Public API Reference + +Package imports: `@copilotkit/react`, `@copilotkit/runtime`, `@copilotkit/core`. + +Note: `@copilotkit/react` re-exports everything from `@ag-ui/client` (which itself re-exports `@ag-ui/core`), so applications typically only need `@copilotkit/react` and `@copilotkit/runtime`. + +--- + +## Hooks (`@copilotkit/react`) + +### useFrontendTool + +```ts +function useFrontendTool>( + tool: ReactFrontendTool, + deps?: ReadonlyArray, +): void; +``` + +Registers a tool that the agent can invoke in the browser. The tool object has these fields: + +- `name: string` -- Tool name (must be unique per agentId scope). +- `description?: string` -- Human/model-readable description. +- `parameters?: StandardSchemaV1` -- Schema for tool arguments (Zod, Valibot, ArkType, etc.). +- `handler?: (args: T, context: FrontendToolHandlerContext) => Promise` -- Function called when the agent invokes the tool. +- `render?: React.ComponentType<...>` -- Optional inline renderer for the tool call in chat. +- `agentId?: string` -- Constrain to a specific agent. +- `available?: boolean` -- Toggle visibility without unregistering. Defaults to `true`. +- `followUp?: boolean` -- Whether the agent should follow up after tool execution. + +Re-registers when `tool.name`, `tool.available`, or any value in `deps` changes. + +--- + +### useComponent + +```ts +function useComponent( + config: { + name: string; + description?: string; + parameters?: TSchema; + render: ComponentType>; + agentId?: string; + }, + deps?: ReadonlyArray, +): void; +``` + +Convenience wrapper around `useFrontendTool`. Registers a React component as a visual tool in chat. The model is told to use the tool to "display the component." Render props are inferred from the `parameters` schema. + +--- + +### useAgentContext + +```ts +function useAgentContext(context: AgentContextInput): void; + +interface AgentContextInput { + description: string; + value: JsonSerializable; // string | number | boolean | null | array | object +} +``` + +Shares application state with the agent. The `value` is serialized to JSON and registered as context. Context is removed on unmount. + +--- + +### useAgent + +```ts +function useAgent(props?: UseAgentProps): { agent: AbstractAgent }; + +interface UseAgentProps { + agentId?: string; + updates?: UseAgentUpdate[]; +} + +enum UseAgentUpdate { + OnMessagesChanged = "OnMessagesChanged", + OnStateChanged = "OnStateChanged", + OnRunStatusChanged = "OnRunStatusChanged", +} +``` + +Returns the `AbstractAgent` instance for the given `agentId` (defaults to `"default"`). Subscribes to the specified update categories to trigger re-renders. By default subscribes to all three. + +While the runtime is connecting, returns a provisional `ProxiedCopilotRuntimeAgent` to prevent crashes. + +--- + +### useInterrupt + +```ts +function useInterrupt( + config: UseInterruptConfig, +): React.ReactElement | null | void; + +interface UseInterruptConfig { + render: (props: InterruptRenderProps) => React.ReactElement; + handler?: (props: InterruptHandlerProps) => TResult | PromiseLike; + enabled?: (event: InterruptEvent) => boolean; + agentId?: string; + renderInChat?: TRenderInChat; // default: true +} + +interface InterruptEvent { + name: string; + value: TValue; +} + +interface InterruptRenderProps { + event: InterruptEvent; + result: TResult; + resolve: (response: unknown) => void; +} +``` + +Handles agent `on_interrupt` events. When `renderInChat` is `true` (default), the element is published into `` and the hook returns `void`. When `false`, it returns the element for manual placement. Call `resolve()` from your render to resume the agent. + +--- + +### useHumanInTheLoop + +```ts +function useHumanInTheLoop>( + tool: ReactHumanInTheLoop, + deps?: ReadonlyArray, +): void; +``` + +Registers a tool that pauses agent execution until the user responds. The `render` component receives a `respond` callback during the `"executing"` phase. Built on top of `useFrontendTool` with a promise-based handler. + +```ts +type ReactHumanInTheLoop = Omit, "handler"> & { + render: React.ComponentType< + | { status: "inProgress"; args: Partial; respond: undefined } + | { status: "executing"; args: T; respond: (result: unknown) => Promise } + | { status: "complete"; args: T; result: string; respond: undefined } + >; +}; +``` + +--- + +### useRenderTool + +```ts +// Named tool renderer with typed parameters +function useRenderTool( + config: { + name: string; + parameters: S; + render: (props: RenderToolProps) => React.ReactElement; + agentId?: string; + }, + deps?: ReadonlyArray, +): void; + +// Wildcard renderer (fallback for unregistered tools) +function useRenderTool( + config: { + name: "*"; + render: (props: any) => React.ReactElement; + agentId?: string; + }, + deps?: ReadonlyArray, +): void; + +type RenderToolProps = + | { name: string; parameters: Partial>; status: "inProgress"; result: undefined } + | { name: string; parameters: InferSchemaOutput; status: "executing"; result: undefined } + | { name: string; parameters: InferSchemaOutput; status: "complete"; result: string }; +``` + +Registers a visual renderer for tool calls in the chat. Renderers are deduplicated by `agentId:name`. The renderer is intentionally NOT removed on unmount so historical tool calls can still render. + +--- + +### useDefaultRenderTool + +```ts +function useDefaultRenderTool( + config?: { render?: (props: DefaultRenderProps) => React.ReactElement }, + deps?: ReadonlyArray, +): void; +``` + +Registers a wildcard `"*"` renderer via `useRenderTool`. With no arguments, uses the built-in expandable card UI showing tool name, status badge, arguments, and result. + +--- + +### useSuggestions + +```ts +function useSuggestions(options?: { agentId?: string }): UseSuggestionsResult; + +interface UseSuggestionsResult { + suggestions: Suggestion[]; + reloadSuggestions: () => void; + clearSuggestions: () => void; + isLoading: boolean; +} + +type Suggestion = { + title: string; + message: string; + isLoading: boolean; +}; +``` + +Reads the current suggestion list for an agent. Subscribes to real-time updates. + +--- + +### useConfigureSuggestions + +```ts +function useConfigureSuggestions( + config: SuggestionsConfigInput | null | undefined, + deps?: ReadonlyArray, +): void; +``` + +Registers a suggestion configuration. Two modes: + +**Dynamic** (LLM-generated): +```ts +{ + instructions: "Suggest follow-up questions about the data", + minSuggestions?: number, // default 1 + maxSuggestions?: number, // default 3 + available?: "before-first-message" | "after-first-message" | "always" | "disabled", + providerAgentId?: string, + consumerAgentId?: string, // default "*" +} +``` + +**Static**: +```ts +{ + suggestions: [{ title: "...", message: "..." }], + available?: SuggestionAvailability, + consumerAgentId?: string, +} +``` + +--- + +### useThreads + +```ts +function useThreads(input: UseThreadsInput): UseThreadsResult; + +interface UseThreadsInput { + userId: string; + agentId: string; +} + +interface UseThreadsResult { + threads: Thread[]; + isLoading: boolean; + error: Error | null; + renameThread: (threadId: string, name: string) => Promise; + archiveThread: (threadId: string) => Promise; + deleteThread: (threadId: string) => Promise; +} + +interface Thread { + id: string; + name?: string; + createdAt: string; + updatedAt: string; +} +``` + +Lists and manages Intelligence platform threads. Uses a realtime WebSocket subscription when available. + +--- + +### useRenderToolCall (internal) + +```ts +function useRenderToolCall(): (props: { + toolCall: ToolCall; + toolMessage?: ToolMessage; +}) => React.ReactElement | null; +``` + +Returns a function that resolves the correct renderer for a tool call. Priority: exact name match (prefer agent-scoped) > wildcard `"*"`. + +--- + +### useRenderActivityMessage (internal) + +```ts +function useRenderActivityMessage(): { + renderActivityMessage: (message: ActivityMessage) => React.ReactElement | null; + findRenderer: (activityType: string) => ReactActivityMessageRenderer | null; +}; +``` + +Resolves and renders activity messages by type. Matches by `activityType` with agent-scoping, falls back to wildcard `"*"`. + +--- + +### useRenderCustomMessages (internal) + +Returns a function to render custom message decorators at `"before"` or `"after"` positions relative to each message. + +--- + +## Components (`@copilotkit/react`) + +### CopilotKitProvider + +```tsx + + credentials?: RequestCredentials + publicApiKey?: string // alias: publicLicenseKey + properties?: Record + agents__unsafe_dev_only?: Record + selfManagedAgents?: Record + renderToolCalls?: ReactToolCallRenderer[] + renderActivityMessages?: ReactActivityMessageRenderer[] + renderCustomMessages?: ReactCustomMessageRenderer[] + frontendTools?: ReactFrontendTool[] + humanInTheLoop?: ReactHumanInTheLoop[] + showDevConsole?: boolean | "auto" + useSingleEndpoint?: boolean + onError?: (event: { error: Error; code: CopilotKitCoreErrorCode; context: Record }) => void + a2ui?: { theme?: A2UITheme } +> + {children} + +``` + +Root provider. Configures the runtime connection, registers static tool renderers and tools, and provides the CopilotKit context to all descendant hooks and components. + +--- + +### CopilotChat + +```tsx + + chatView?: SlotValue + onError?: (event: { error: Error; code: CopilotKitCoreErrorCode; context: Record }) => void + // Plus all CopilotChatViewProps (messageView, input, suggestionView, welcomeScreen, etc.) +/> +``` + +Full chat interface. Connects to the agent on mount, handles message submission, suggestion selection, stop, and audio transcription. + +--- + +### CopilotPopup + +```tsx + +``` + +Chat in a floating popup with a toggle button. + +--- + +### CopilotSidebar + +```tsx + +``` + +Chat in a collapsible sidebar panel. + +--- + +### CopilotChatView + +Headless chat view with a slot-based architecture. Accepts slots for `messageView`, `scrollView`, `input`, `suggestionView`, and `welcomeScreen`. Also exposes sub-components: `CopilotChatView.ScrollView`, `CopilotChatView.Feather`, `CopilotChatView.WelcomeScreen`, `CopilotChatView.WelcomeMessage`, `CopilotChatView.ScrollToBottomButton`. + +--- + +### Other Chat Sub-Components + +- `CopilotChatInput` -- Textarea with send, stop, and transcription controls. +- `CopilotChatMessageView` -- Renders the message list. +- `CopilotChatAssistantMessage` -- Single assistant message bubble. +- `CopilotChatUserMessage` -- Single user message bubble. +- `CopilotChatReasoningMessage` -- Reasoning/thinking message display. +- `CopilotChatSuggestionView` -- Renders suggestion pills. +- `CopilotChatSuggestionPill` -- Individual suggestion pill. +- `CopilotChatToolCallsView` -- Renders tool call results in a message. +- `CopilotChatToggleButton` -- Open/close toggle for popup/sidebar. +- `CopilotModalHeader` -- Header bar for popup/sidebar modals. +- `CopilotPopupView` -- Popup layout wrapper. +- `CopilotSidebarView` -- Sidebar layout wrapper. +- `CopilotKitInspector` -- Dev console overlay (controlled by `showDevConsole`). +- `MCPAppsActivityRenderer` -- Built-in renderer for MCP Apps activity messages. +- `WildcardToolCallRender` -- Built-in wildcard tool call renderer component. + +--- + +## Types (`@copilotkit/react`) + +### ReactFrontendTool + +```ts +type ReactFrontendTool = FrontendTool & { + render?: ReactToolCallRenderer["render"]; +}; +``` + +### ReactToolCallRenderer + +```ts +interface ReactToolCallRenderer { + name: string; + args: StandardSchemaV1; + agentId?: string; + render: React.ComponentType< + | { name: string; args: Partial; status: "inProgress"; result: undefined } + | { name: string; args: T; status: "executing"; result: undefined } + | { name: string; args: T; status: "complete"; result: string } + >; +} +``` + +### ReactHumanInTheLoop + +See `useHumanInTheLoop` above. + +### ReactActivityMessageRenderer + +```ts +interface ReactActivityMessageRenderer { + activityType: string; // or "*" for wildcard + agentId?: string; + content: StandardSchemaV1; + render: React.ComponentType<{ + activityType: string; + content: TActivityContent; + message: ActivityMessage; + agent: AbstractAgent | undefined; + }>; +} +``` + +### ToolCallStatus + +```ts +enum ToolCallStatus { + InProgress = "inProgress", + Executing = "executing", + Complete = "complete", +} +``` + +### FrontendToolHandlerContext + +```ts +type FrontendToolHandlerContext = { + toolCall: ToolCall; + agent: AbstractAgent; +}; +``` + +--- + +## Runtime (`@copilotkit/runtime`) + +See [runtime-api.md](./runtime-api.md) for full runtime reference. diff --git a/skills/copilotkit-develop/references/chat-customization.md b/skills/copilotkit-develop/references/chat-customization.md new file mode 100644 index 00000000000..4fa35da84c9 --- /dev/null +++ b/skills/copilotkit-develop/references/chat-customization.md @@ -0,0 +1,240 @@ +# CopilotChat Customization Reference + +## CopilotChat Props + +`CopilotChat` is the primary chat component. It wraps `CopilotChatView` and handles agent connection, message submission, suggestions, stop, and audio transcription. + +```tsx +interface CopilotChatProps { + // Agent configuration + agentId?: string; // Agent to connect to. Default: "default" + threadId?: string; // Thread ID. Auto-generated UUID if omitted. + + // Labels and text customization + labels?: Partial; + + // Layout override + chatView?: SlotValue; + + // Error handling (scoped to this chat's agent) + onError?: (event: { + error: Error; + code: CopilotKitCoreErrorCode; + context: Record; + }) => void; + + // All CopilotChatViewProps are also accepted (see below) +} +``` + +## CopilotChatView Props (Layout Slots) + +`CopilotChatView` uses a slot-based architecture. Each slot can be: +- Omitted (uses the default component) +- A props object (merges with the default component) +- A custom React component (replaces the default) + +```tsx +interface CopilotChatViewProps { + // Slot overrides + messageView?: SlotValue; + scrollView?: SlotValue; + input?: SlotValue; + suggestionView?: SlotValue; + + // Welcome screen: true (default), false (disabled), or custom component + welcomeScreen?: SlotValue> | boolean; + + // Data (usually provided by CopilotChat, not set directly) + messages?: Message[]; + isRunning?: boolean; + suggestions?: Suggestion[]; + autoScroll?: boolean; // Default: true + + // Input behavior (usually provided by CopilotChat) + onSubmitMessage?: (value: string) => void; + onStop?: () => void; + inputMode?: "input" | "transcribe" | "processing"; + inputValue?: string; + onInputChange?: (value: string) => void; + + // Transcription handlers + onStartTranscribe?: () => void; + onCancelTranscribe?: () => void; + onFinishTranscribe?: () => void; + onFinishTranscribeWithAudio?: (audioBlob: Blob) => Promise; + + // Standard HTML div props + className?: string; + // ...rest HTMLAttributes +} +``` + +## Labels (Text Customization) + +All user-visible text can be customized via the `labels` prop: + +```tsx +const CopilotChatDefaultLabels = { + chatInputPlaceholder: "Type a message...", + chatInputToolbarStartTranscribeButtonLabel: "Transcribe", + chatInputToolbarCancelTranscribeButtonLabel: "Cancel", + chatInputToolbarFinishTranscribeButtonLabel: "Finish", + chatInputToolbarAddButtonLabel: "Add photos or files", + chatInputToolbarToolsButtonLabel: "Tools", + assistantMessageToolbarCopyCodeLabel: "Copy", + assistantMessageToolbarCopyCodeCopiedLabel: "Copied", + assistantMessageToolbarCopyMessageLabel: "Copy", + assistantMessageToolbarThumbsUpLabel: "Good response", + assistantMessageToolbarThumbsDownLabel: "Bad response", + assistantMessageToolbarReadAloudLabel: "Read aloud", + assistantMessageToolbarRegenerateLabel: "Regenerate", + userMessageToolbarCopyMessageLabel: "Copy", + userMessageToolbarEditMessageLabel: "Edit", + chatDisclaimerText: "AI can make mistakes. Please verify important information.", + chatToggleOpenLabel: "Open chat", + chatToggleCloseLabel: "Close chat", + modalHeaderTitle: "CopilotKit Chat", + welcomeMessageText: "How can I help you today?", +}; +``` + +Example: + +```tsx + +``` + +## CopilotPopup Props + +```tsx +interface CopilotPopupProps extends CopilotChatProps { + header?: SlotValue; // Custom header component + toggleButton?: SlotValue; // Custom toggle button + defaultOpen?: boolean; // Start open? Default: true + width?: number | string; // Popup width + height?: number | string; // Popup height + clickOutsideToClose?: boolean; // Close on outside click +} +``` + +## CopilotSidebar Props + +```tsx +interface CopilotSidebarProps extends CopilotChatProps { + header?: SlotValue; // Custom header component + toggleButton?: SlotValue; // Custom toggle button + defaultOpen?: boolean; // Start open? Default: true + width?: number | string; // Sidebar width +} +``` + +## Styling + +CopilotKit v2 uses Tailwind CSS with a `cpk:` prefix namespace. All internal classes use this prefix to avoid conflicts with your application's styles. + +### CSS Data Attributes + +The chat container exposes data attributes for CSS targeting: + +- `[data-copilotkit]` -- Present on the root chat element. +- `[data-testid="copilot-chat"]` -- The main chat container. +- `[data-copilot-running="true"]` -- While the agent is running. +- `[data-testid="copilot-welcome-screen"]` -- The welcome screen container. +- `[data-sidebar-chat]` -- On sidebar layout wrapper. +- `[data-popup-chat]` -- On popup layout wrapper. + +### Dark Mode + +The components support dark mode through Tailwind's `dark:` variant. All internal components include `cpk:dark:` color variants. Enable dark mode by adding the `dark` class to a parent element per Tailwind convention. + +### Slot-Based Customization + +Every visual sub-component is a "slot" that can be replaced or extended: + +```tsx +// Override the input component with custom props + + +// Replace the input entirely + + +// Override welcome screen + ( +
+

Hello!

+ {input} + {suggestionView} +
+ )} +/> + +// Disable welcome screen + +``` + +### CopilotChatView Sub-Components + +These can be used directly when building fully custom layouts: + +- `CopilotChatView.ScrollView` -- Scroll container with auto-scroll (uses `use-stick-to-bottom`). +- `CopilotChatView.ScrollToBottomButton` -- Floating "scroll to bottom" button. +- `CopilotChatView.Feather` -- Bottom gradient overlay. +- `CopilotChatView.WelcomeScreen` -- Default welcome layout. +- `CopilotChatView.WelcomeMessage` -- Welcome heading text. + +### CopilotChatInput Slots + +The input component has its own slots: + +- `textArea` -- The textarea element. +- `sendButton` -- Send/stop button. +- `startTranscribeButton` -- Microphone button. +- `cancelTranscribeButton` -- Cancel recording button. +- `finishTranscribeButton` -- Finish recording button. +- `addMenuButton` -- File attachment button. +- `audioRecorder` -- Audio recording component. +- `disclaimer` -- Disclaimer text below the input. + +## System Prompt / Agent Context + +CopilotKit v2 does not have a `systemPrompt` prop on the chat component. Instead, context is provided to agents through: + +1. **`useAgentContext`** -- Share structured application data. +2. **Agent configuration** -- System prompts are configured on the agent itself (server-side), not on the React chat component. + +## Error Handling + +Errors can be handled at two levels: + +```tsx +// Provider-level: catches all errors + { + console.error("CopilotKit error:", code, error.message); + }} +> + {/* Chat-level: catches errors for this specific agent */} + { + showToast(`Agent error: ${error.message}`); + }} + /> + +``` + +The chat-level `onError` fires in addition to (not instead of) the provider-level handler. It only receives errors whose `context.agentId` matches the chat's agent. diff --git a/skills/copilotkit-develop/references/runtime-api.md b/skills/copilotkit-develop/references/runtime-api.md new file mode 100644 index 00000000000..1ea8eef854a --- /dev/null +++ b/skills/copilotkit-develop/references/runtime-api.md @@ -0,0 +1,347 @@ +# CopilotKit v2 Runtime API Reference + +Package: `@copilotkit/runtime` + +--- + +## Runtime Classes + +### CopilotRuntime + +Compatibility shim that auto-detects the mode based on whether `intelligence` is provided. Delegates to `CopilotSseRuntime` or `CopilotIntelligenceRuntime`. + +```ts +import { CopilotRuntime } from "@copilotkit/runtime"; + +const runtime = new CopilotRuntime({ + agents: { myAgent: new LangGraphAgent({ ... }) }, + // If intelligence is provided, uses Intelligence mode; otherwise SSE mode +}); +``` + +### CopilotSseRuntime + +Explicit SSE-mode runtime. Agents run in-memory via `InMemoryAgentRunner`. + +```ts +import { CopilotSseRuntime } from "@copilotkit/runtime"; + +const runtime = new CopilotSseRuntime({ + agents: { myAgent: agent }, + runner?: AgentRunner, // default: InMemoryAgentRunner +}); +``` + +### CopilotIntelligenceRuntime + +Intelligence-mode runtime with durable threads, realtime events, and persistent state. + +```ts +import { + CopilotIntelligenceRuntime, + CopilotKitIntelligence, +} from "@copilotkit/runtime"; + +const runtime = new CopilotIntelligenceRuntime({ + agents: { myAgent: agent }, + intelligence: new CopilotKitIntelligence({ ... }), + identifyUser: async (request) => ({ id: getUserIdFromRequest(request) }), + generateThreadNames?: boolean, // default: true +}); +``` + +--- + +## Runtime Options + +All runtime constructors accept these base options: + +```ts +interface BaseCopilotRuntimeOptions { + // Map of available agents. Can be a promise for lazy loading. + agents: MaybePromise>; + + // Optional transcription service for audio processing + transcriptionService?: TranscriptionService; + + // Middleware hooks + beforeRequestMiddleware?: BeforeRequestMiddleware; + afterRequestMiddleware?: AfterRequestMiddleware; + + // Auto-apply A2UI middleware to agents + a2ui?: { + agents?: string[]; // Limit to specific agents; omit for all + // ... A2UIMiddlewareConfig from @ag-ui/a2ui-middleware + }; + + // Auto-apply MCP Apps middleware + mcpApps?: { + servers: McpAppsServerConfig[]; + }; +} +``` + +### McpAppsServerConfig + +```ts +type McpAppsServerConfig = MCPClientConfig & { + agentId?: string; // Bind to specific agent; omit for all agents +}; +``` + +--- + +## Endpoint Factories + +### createCopilotEndpoint (Hono) + +```ts +import { createCopilotEndpoint } from "@copilotkit/runtime"; + +const app = createCopilotEndpoint({ + runtime: CopilotRuntimeLike, + basePath: string, + cors?: { + origin: string | string[] | ((origin: string) => string | null); + credentials?: boolean; + }, +}); +``` + +Returns a Hono app instance with all CopilotKit routes mounted under `basePath`. + +### createCopilotEndpointExpress (Express) + +```ts +import { createCopilotEndpointExpress } from "@copilotkit/runtime"; + +const router = createCopilotEndpointExpress({ + runtime: CopilotRuntimeLike, + basePath: string, +}); + +// Use in Express app: +app.use(router); +``` + +Returns an Express `Router` with all CopilotKit routes mounted under `basePath`. + +--- + +## HTTP Routes + +Both endpoint factories create these routes under `basePath`: + +| Method | Path | Description | +|--------|------|-------------| +| `GET` | `/info` | Runtime info: available agents, mode, capabilities | +| `POST` | `/agent/:agentId/run` | Run an agent (SSE stream response) | +| `POST` | `/agent/:agentId/connect` | Connect to an agent (initial handshake for existing threads) | +| `POST` | `/agent/:agentId/stop/:threadId` | Stop a running agent | +| `POST` | `/transcribe` | Transcribe audio file | +| `GET` | `/threads` | List threads (Intelligence mode) | +| `POST` | `/threads/subscribe` | Subscribe to thread updates (Intelligence mode) | +| `PATCH` | `/threads/:threadId` | Update thread metadata | +| `POST` | `/threads/:threadId/archive` | Archive a thread | +| `DELETE` | `/threads/:threadId` | Permanently delete a thread | + +--- + +## Middleware + +### BeforeRequestMiddleware + +Called before each request handler. Can modify or replace the request. + +```ts +type BeforeRequestMiddleware = (params: { + runtime: CopilotRuntimeLike; + request: Request; + path: string; +}) => MaybePromise; +``` + +If a `Request` is returned, it replaces the original request for the handler. + +### AfterRequestMiddleware + +Called after each request handler. Receives the response and parsed SSE messages. + +```ts +type AfterRequestMiddleware = (params: { + runtime: CopilotRuntimeLike; + response: Response; + path: string; + messages?: Message[]; // Reconstructed from SSE stream + threadId?: string; // From RUN_STARTED event + runId?: string; // From RUN_STARTED event +}) => MaybePromise; +``` + +### Example + +```ts +const runtime = new CopilotRuntime({ + agents: { myAgent: agent }, + beforeRequestMiddleware: async ({ request, path }) => { + console.log(`Incoming request to ${path}`); + // Optionally return a modified Request + }, + afterRequestMiddleware: async ({ response, path, threadId, messages }) => { + console.log(`Response from ${path}, thread: ${threadId}, ${messages?.length} messages`); + }, +}); +``` + +--- + +## Intelligence Platform + +### CopilotKitIntelligence + +Client for the CopilotKit Intelligence platform (durable threads, realtime WebSocket). + +```ts +import { CopilotKitIntelligence } from "@copilotkit/runtime"; + +const intelligence = new CopilotKitIntelligence({ + // Configuration for the Intelligence platform + // (API keys, URLs, etc.) +}); +``` + +### identifyUser + +Required for Intelligence mode. Resolves the authenticated user from the incoming request. + +```ts +type IdentifyUserCallback = (request: Request) => MaybePromise<{ id: string }>; +``` + +### Thread Management Types + +```ts +interface CreateThreadRequest { /* platform-specific */ } +interface ThreadSummary { /* id, name, timestamps */ } +interface ListThreadsResponse { /* thread list */ } +interface UpdateThreadRequest { /* name updates */ } +interface SubscribeToThreadsRequest { /* WebSocket subscription params */ } +interface SubscribeToThreadsResponse { /* realtime thread updates */ } +``` + +--- + +## Agent Runners + +### AgentRunner (abstract) + +Base class for executing agents. Custom runners can be implemented for custom execution environments. + +### InMemoryAgentRunner + +Default runner for SSE mode. Runs agents in the Node.js process. + +### IntelligenceAgentRunner + +Runner for Intelligence mode. Delegates execution to the Intelligence platform via WebSocket. + +--- + +## Transcription Service + +### TranscriptionService (abstract) + +```ts +interface TranscribeFileOptions { + audioFile: File; + mimeType?: string; + size?: number; +} + +abstract class TranscriptionService { + abstract transcribeFile(options: TranscribeFileOptions): Promise; +} +``` + +Implement this class to provide audio-to-text transcription. The runtime exposes it via the `/transcribe` endpoint. + +--- + +## CORS Configuration + +The Hono endpoint factory accepts explicit CORS configuration: + +```ts +createCopilotEndpoint({ + runtime, + basePath: "/api/copilotkit", + cors: { + origin: "https://myapp.com", // or array, or function + credentials: true, // for HTTP-only cookies + }, +}); +``` + +When `credentials` is `true`, `origin` must be explicitly specified (cannot be `"*"`). + +The Express endpoint factory uses `cors({ origin: "*" })` by default. Override by wrapping or configuring the Express cors middleware separately. + +--- + +## Complete Example: Next.js API Route (using Hono) + +```ts +// app/api/copilotkit/[[...path]]/route.ts +import { CopilotRuntime, createCopilotEndpoint } from "@copilotkit/runtime"; +import { LangGraphAgent } from "@ag-ui/langgraph"; + +const runtime = new CopilotRuntime({ + agents: { + researcher: new LangGraphAgent({ + graphId: "researcher", + url: process.env.LANGGRAPH_URL!, + }), + }, +}); + +const app = createCopilotEndpoint({ + runtime, + basePath: "/api/copilotkit", +}); + +export const GET = app.fetch; +export const POST = app.fetch; +export const PATCH = app.fetch; +export const DELETE = app.fetch; +``` + +## Complete Example: Express + +```ts +import express from "express"; +import { + CopilotRuntime, + createCopilotEndpointExpress, +} from "@copilotkit/runtime"; +import { LangGraphAgent } from "@ag-ui/langgraph"; + +const app = express(); + +const runtime = new CopilotRuntime({ + agents: { + researcher: new LangGraphAgent({ + graphId: "researcher", + url: process.env.LANGGRAPH_URL!, + }), + }, +}); + +app.use( + createCopilotEndpointExpress({ + runtime, + basePath: "/api/copilotkit", + }), +); + +app.listen(3000); +``` diff --git a/skills/copilotkit-develop/sources.md b/skills/copilotkit-develop/sources.md new file mode 100644 index 00000000000..d5595560e3e --- /dev/null +++ b/skills/copilotkit-develop/sources.md @@ -0,0 +1,66 @@ +# Sources + +Files read from the CopilotKit codebase to generate this skill's references. + +## api-surface.md + +- packages/v2/react/src/hooks/use-frontend-tool.tsx +- packages/v2/react/src/hooks/use-component.tsx +- packages/v2/react/src/hooks/use-agent-context.tsx +- packages/v2/react/src/hooks/use-agent.tsx +- packages/v2/react/src/hooks/use-interrupt.tsx +- packages/v2/react/src/hooks/use-human-in-the-loop.tsx +- packages/v2/react/src/hooks/use-render-tool.tsx +- packages/v2/react/src/hooks/use-default-render-tool.tsx +- packages/v2/react/src/hooks/use-suggestions.tsx +- packages/v2/react/src/hooks/use-configure-suggestions.tsx +- packages/v2/react/src/hooks/use-threads.tsx +- packages/v2/react/src/hooks/use-render-tool-call.tsx +- packages/v2/react/src/hooks/use-render-activity-message.tsx +- packages/v2/react/src/hooks/use-render-custom-messages.tsx +- packages/v2/react/src/providers/CopilotKitProvider.tsx +- packages/v2/react/src/components/chat/copilot-chat.tsx +- packages/v2/react/src/components/chat/copilot-popup.tsx +- packages/v2/react/src/components/chat/copilot-sidebar.tsx +- packages/v2/react/src/components/chat/copilot-chat-view.tsx +- packages/v2/react/src/components/chat/copilot-chat-input.tsx +- packages/v2/react/src/components/chat/copilot-chat-message-view.tsx +- packages/v2/react/src/components/chat/copilot-chat-suggestion-view.tsx +- packages/v2/react/src/components/chat/copilot-chat-toggle-button.tsx +- packages/v2/react/src/components/chat/copilot-modal-header.tsx +- packages/v2/react/src/components/chat/copilot-popup-view.tsx +- packages/v2/react/src/components/chat/copilot-sidebar-view.tsx +- packages/v2/react/src/components/CopilotKitInspector.tsx +- packages/v2/react/src/types/frontend-tool.ts +- packages/v2/react/src/types/tool-call-renderer.ts +- packages/v2/react/src/types/activity-message-renderer.ts +- packages/v2/core/src/types/tool-call.ts + +## chat-customization.md + +- packages/v2/react/src/components/chat/copilot-chat.tsx +- packages/v2/react/src/components/chat/copilot-chat-view.tsx +- packages/v2/react/src/components/chat/copilot-chat-input.tsx +- packages/v2/react/src/components/chat/copilot-popup.tsx +- packages/v2/react/src/components/chat/copilot-sidebar.tsx +- packages/v2/react/src/components/chat/copilot-popup-view.tsx +- packages/v2/react/src/components/chat/copilot-sidebar-view.tsx +- packages/v2/react/src/components/chat/copilot-chat-message-view.tsx +- packages/v2/react/src/components/chat/copilot-chat-suggestion-view.tsx +- packages/v2/react/src/components/chat/copilot-chat-toggle-button.tsx +- packages/v2/react/src/components/chat/copilot-modal-header.tsx +- packages/v2/react/src/types/labels.ts +- packages/v2/react/src/types/slot.ts + +## runtime-api.md + +- packages/v2/runtime/src/runtime.ts +- packages/v2/runtime/src/endpoints/hono.ts +- packages/v2/runtime/src/endpoints/express.ts +- packages/v2/runtime/src/types/runtime-options.ts +- packages/v2/runtime/src/types/middleware.ts +- packages/v2/runtime/src/runner/agent-runner.ts +- packages/v2/runtime/src/runner/in-memory.ts +- packages/v2/runtime/src/runner/intelligence.ts +- packages/v2/runtime/src/intelligence-platform/client.ts +- packages/v2/runtime/src/transcription/transcription-service.ts diff --git a/skills/copilotkit-integrations/SKILL.md b/skills/copilotkit-integrations/SKILL.md new file mode 100644 index 00000000000..3ddce76e859 --- /dev/null +++ b/skills/copilotkit-integrations/SKILL.md @@ -0,0 +1,182 @@ +--- +name: copilotkit-integrations +description: "Use when wiring an external agent framework (LangGraph, CrewAI, PydanticAI, Mastra, ADK, LlamaIndex, Agno, Strands, Microsoft Agent Framework, or others) into a CopilotKit application via the AG-UI protocol." +version: 1.0.0 +--- + +# CopilotKit Integrations + +## Live Documentation (MCP) + +This plugin includes an MCP server (`copilotkit-docs`) that provides `search-docs` and `search-code` tools for querying live CopilotKit documentation and source code. Useful for looking up framework-specific integration details. + +- **Claude Code:** Auto-configured by the plugin's `.mcp.json` -- no setup needed. +- **Codex:** Requires manual configuration. See the [copilotkit-debug skill](../copilotkit-debug/SKILL.md#mcp-setup) for setup instructions. + +## Overview + +CopilotKit connects to external agent frameworks through the **AG-UI (Agent-UI) protocol** -- a streaming protocol that enables bidirectional communication between a frontend CopilotKit application and a backend agent. Every integration follows the same architectural pattern: + +1. **Agent server** -- your agent framework runs as an HTTP server (usually FastAPI/uvicorn for Python, or an Express/Next.js route for JS/TS) +2. **AG-UI adapter** -- a framework-specific adapter translates between the agent's native interface and the AG-UI wire protocol +3. **CopilotKit runtime** -- the Next.js API route creates a `CopilotRuntime` that connects to the agent via an AG-UI client class +4. **Frontend** -- React components use `useAgent`, `useFrontendTool`, `useRenderToolCall`, and `useHumanInTheLoop` to interact with the agent + +## Supported Integrations + +| Framework | Language | AG-UI Client (route.ts) | AG-UI Server Adapter | Agent Port | +|-----------|----------|------------------------|---------------------|------------| +| LangGraph (Python, self-hosted) | Python | `LangGraphHttpAgent` from `@copilotkit/runtime/langgraph` | `ag-ui-langgraph` (`add_langgraph_fastapi_endpoint`) | 8123 | +| LangGraph (Python, LangGraph Platform) | Python | `LangGraphAgent` from `@copilotkit/runtime/langgraph` | LangGraph Platform (managed) | varies | +| LangGraph (JS) | TypeScript | `LangGraphAgent` from `@copilotkit/runtime/langgraph` | Built into `@copilotkit/sdk-js/langgraph` | 8123 | +| CrewAI Flows | Python | `HttpAgent` from `@ag-ui/client` | `ag-ui-crewai` (`add_crewai_flow_fastapi_endpoint`) | 8000 | +| CrewAI Crews | Python | `CrewAIAgent` from `@ag-ui/crewai` | `ag-ui-crewai` (`add_crewai_crew_fastapi_endpoint`) | 8000 | +| PydanticAI | Python | `HttpAgent` from `@ag-ui/client` | `pydantic-ai-slim[ag-ui]` (`agent.to_ag_ui()`) | 8000 | +| Mastra | TypeScript | `MastraAgent` from `@ag-ui/mastra` | Built into `@ag-ui/mastra` | Next.js dev server | +| Google ADK | Python | `HttpAgent` from `@ag-ui/client` | `ag-ui-adk` (`add_adk_fastapi_endpoint`) | 8000 | +| LlamaIndex | Python | `LlamaIndexAgent` from `@ag-ui/llamaindex` | `llama-index-protocols-ag-ui` (`get_ag_ui_workflow_router`) | 9000 | +| Agno | Python | `HttpAgent` from `@ag-ui/client` | `agno` (built-in `AgentOS` with `AGUI` interface) | 8000 | +| Strands | Python | `HttpAgent` from `@ag-ui/client` | `ag_ui_strands` (`create_strands_app`) | 8000 | +| Microsoft Agent Framework (Python) | Python | `HttpAgent` from `@ag-ui/client` | `agent-framework-ag-ui` (`add_agent_framework_fastapi_endpoint`) | 8000 | +| Microsoft Agent Framework (.NET) | C# | `HttpAgent` from `@ag-ui/client` | `Microsoft.Agents.AI.Hosting.AGUI.AspNetCore` (`MapAGUI`) | 8000 | +| A2A Middleware | Python + TS | `A2AMiddlewareAgent` from `@ag-ui/a2a-middleware` | Per-agent (mixed frameworks) | 9000-9002 | +| MCP Apps | TypeScript | `BuiltInAgent` with `MCPAppsMiddleware` | N/A (middleware on BuiltInAgent) | 3108 | + +## Decision Tree + +Use this to pick the right integration: + +``` +Is your agent written in TypeScript/JavaScript? + YES --> Is it a Mastra agent? + YES --> Use Mastra integration (references/integrations/mastra.md) + NO --> Is it a LangGraph JS agent? + YES --> Use LangGraph JS integration (references/integrations/langgraph.md, JS section) + NO --> Use BuiltInAgent with MCP Apps middleware or HttpAgent + NO (Python or .NET) --> + Which framework? + LangGraph --> references/integrations/langgraph.md + CrewAI --> references/integrations/crewai.md + PydanticAI --> references/integrations/pydantic-ai.md + Google ADK --> references/integrations/adk.md + LlamaIndex --> references/integrations/llamaindex.md + Agno --> references/integrations/agno.md + Strands --> references/integrations/strands.md + MS Agent Fw --> references/integrations/ms-agent-framework.md + Multiple agents (A2A) --> references/integrations/a2a.md +``` + +## Common AG-UI Protocol Patterns + +Every integration shares these patterns on the frontend side. + +### CopilotKit Provider (layout.tsx) + +```tsx +import { CopilotKitProvider } from "@copilotkit/react"; +import "@copilotkit/react/styles.css"; + +export default function RootLayout({ children }) { + return ( + + {children} + + ); +} +``` + +The `agent` prop must match the key used in `CopilotRuntime({ agents: { my_agent_name: ... } })`. + +### API Route Pattern (route.ts) + +All integrations create a Next.js API route at `src/app/api/copilotkit/route.ts`: + +```tsx +import { + CopilotRuntime, + ExperimentalEmptyAdapter, + copilotRuntimeNextJSAppRouterEndpoint, +} from "@copilotkit/runtime"; +import { NextRequest } from "next/server"; +// Import the appropriate agent class for your framework + +const runtime = new CopilotRuntime({ + agents: { + my_agent: new SomeAgentClass({ url: "http://localhost:8000/" }), + }, +}); + +export const POST = async (req: NextRequest) => { + const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({ + runtime, + serviceAdapter: new ExperimentalEmptyAdapter(), + endpoint: "/api/copilotkit", + }); + return handleRequest(req); +}; +``` + +### Shared State (useAgent) + +```tsx +const { state, setState } = useAgent<{ proverbs: string[] }>({ + name: "my_agent", + initialState: { proverbs: [] }, +}); +``` + +### Frontend Tools (useFrontendTool) + +```tsx +useFrontendTool({ + name: "setThemeColor", + parameters: [ + { name: "themeColor", description: "Hex color value", required: true }, + ], + handler({ themeColor }) { + setThemeColor(themeColor); + }, +}); +``` + +### Generative UI (useRenderToolCall) + +```tsx +useRenderToolCall({ + name: "get_weather", + description: "Get weather for a location.", + parameters: [{ name: "location", type: "string", required: true }], + render: ({ args }) => , +}, []); +``` + +### Human in the Loop (useHumanInTheLoop) + +```tsx +useHumanInTheLoop({ + name: "go_to_moon", + description: "Go to the moon on request.", + render: ({ respond, status }) => ( + + ), +}, []); +``` + +## Agent-Side State Management + +On the agent side, shared state is managed differently per framework, but the protocol is the same -- agents emit `STATE_SNAPSHOT` events to update the frontend. See each integration guide for framework-specific patterns. + +## Key Packages + +Frontend (all integrations): +- `@copilotkit/react` -- hooks (`useAgent`, `useFrontendTool`, `useRenderToolCall`, `useHumanInTheLoop`) and UI components (`CopilotSidebar`, `CopilotPopup`) +- `@copilotkit/runtime` -- server runtime (`CopilotRuntime`, `ExperimentalEmptyAdapter`) + +AG-UI client classes (choose one per integration): +- `@copilotkit/runtime/langgraph` -- `LangGraphAgent`, `LangGraphHttpAgent` +- `@ag-ui/client` -- `HttpAgent` (generic, works with any AG-UI server) +- `@ag-ui/crewai` -- `CrewAIAgent` +- `@ag-ui/mastra` -- `MastraAgent` +- `@ag-ui/llamaindex` -- `LlamaIndexAgent` +- `@ag-ui/a2a-middleware` -- `A2AMiddlewareAgent` +- `@ag-ui/mcp-apps-middleware` -- `MCPAppsMiddleware` diff --git a/skills/copilotkit-integrations/references/integrations/a2a.md b/skills/copilotkit-integrations/references/integrations/a2a.md new file mode 100644 index 00000000000..e3691e40444 --- /dev/null +++ b/skills/copilotkit-integrations/references/integrations/a2a.md @@ -0,0 +1,167 @@ +# A2A (Agent-to-Agent) Integration + +CopilotKit supports multi-agent architectures via two A2A patterns: **A2A Middleware** (orchestrating multiple agents from different frameworks) and **A2A + A2UI** (agents that render UI components declaratively). + +## A2A Middleware + +The A2A Middleware pattern enables a frontend to communicate with multiple specialized agents built with different frameworks. An orchestrator coordinates the agents, and the middleware injects a `send_message_to_a2a_agent` tool. + +### Architecture + +``` +Next.js UI (CopilotKit) + | AG-UI Protocol +A2A Middleware + | A2A Protocol + +---> Research Agent (LangGraph, port 9001) + +---> Analysis Agent (ADK, port 9002) + ^ + | +Orchestrator (ADK, port 9000) +``` + +### Prerequisites + +- Node.js 18+ +- Python 3.10+ +- Google API key + OpenAI API key + +### Next.js Route (app/api/copilotkit/route.ts) + +```typescript +import { + CopilotRuntime, + ExperimentalEmptyAdapter, + copilotRuntimeNextJSAppRouterEndpoint, +} from "@copilotkit/runtime"; +import { HttpAgent } from "@ag-ui/client"; +import { A2AMiddlewareAgent } from "@ag-ui/a2a-middleware"; +import { NextRequest } from "next/server"; + +export async function POST(request: NextRequest) { + const researchAgentUrl = process.env.RESEARCH_AGENT_URL || "http://localhost:9001"; + const analysisAgentUrl = process.env.ANALYSIS_AGENT_URL || "http://localhost:9002"; + const orchestratorUrl = process.env.ORCHESTRATOR_URL || "http://localhost:9000"; + + // Connect to orchestrator via AG-UI Protocol + const orchestrationAgent = new HttpAgent({ url: orchestratorUrl }); + + // A2A Middleware wraps orchestrator and injects send_message_to_a2a_agent tool + const a2aMiddlewareAgent = new A2AMiddlewareAgent({ + description: "Research assistant with 2 specialized agents", + agentUrls: [researchAgentUrl, analysisAgentUrl], + orchestrationAgent, + instructions: ` + You are a research assistant that orchestrates between 2 specialized agents. + - Research Agent (LangGraph): Gathers and summarizes information + - Analysis Agent (ADK): Analyzes research findings + + When the user asks to research a topic: + 1. Research Agent - gather information + 2. Analysis Agent - analyze the findings + 3. Present the complete research and analysis + `, + }); + + const runtime = new CopilotRuntime({ + agents: { + a2a_chat: a2aMiddlewareAgent, + }, + }); + + const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({ + runtime, + serviceAdapter: new ExperimentalEmptyAdapter(), + endpoint: "/api/copilotkit", + }); + return handleRequest(request); +} +``` + +Key patterns: +- `A2AMiddlewareAgent` from `@ag-ui/a2a-middleware` wraps the orchestrator +- `agentUrls` lists all A2A-compatible agent endpoints +- `orchestrationAgent` is the main agent that receives requests from the UI +- `instructions` guide the orchestrator on how to use the specialized agents +- The middleware automatically injects the `send_message_to_a2a_agent` tool + +### Adding New Agents + +1. Create a new Python agent implementing the A2A protocol +2. Register its URL in `agentUrls` +3. Update the middleware `instructions` to describe the new agent +4. Add a dev script to `package.json` + +--- + +## A2A + A2UI + +A2UI (Agent-to-UI) enables agents to render UI components declaratively. The agent defines UI components in its prompt, and CopilotKit renders them. + +### Prerequisites + +- Python 3.12+ +- Node.js 20+ +- Gemini API key + +### Key Difference from Standard Integrations + +In A2A + A2UI, most of the UI is generated by the agent rather than defined in React components. The agent sends declarative component descriptions (calendars, inboxes, forms, etc.) which are rendered by CopilotKit's A2UI renderer. + +### Frontend + +The main `page.tsx` is minimal -- the agent drives the UI: + +```tsx +// Most UI comes from the agent via A2UI declarative components +// To see/edit the components, look in agent/prompt_builder.py +// Generate new components with the A2UI Composer: https://a2ui-editor.ag-ui.com +``` + +### Resources + +- [A2UI + CopilotKit Documentation](https://docs.copilotkit.ai/a2a) +- [A2UI Specification](https://a2ui.org) +- [A2UI Composer](https://a2ui-editor.ag-ui.com) -- visual tool for creating A2UI components + +--- + +## MCP Apps + +MCP Apps integrate Model Context Protocol servers as middleware on a `BuiltInAgent`: + +```typescript +import { BuiltInAgent } from "@copilotkit/runtime/v2"; +import { MCPAppsMiddleware } from "@ag-ui/mcp-apps-middleware"; + +const middlewares = [ + new MCPAppsMiddleware({ + mcpServers: [ + { + type: "http", + url: "http://localhost:3108/mcp", + serverId: "threejs", + }, + ], + }), +]; + +const agent = new BuiltInAgent({ + model: "openai/gpt-4o", + prompt: "You are a helpful assistant.", +}); + +for (const middleware of middlewares) { + agent.use(middleware); +} + +const runtime = new CopilotRuntime({ + agents: { default: agent }, +}); +``` + +Key patterns: +- Uses `BuiltInAgent` from `@copilotkit/runtime/v2` (not an external agent) +- `MCPAppsMiddleware` adds MCP server tools to the agent +- Multiple MCP servers can be added in the `mcpServers` array +- Each server needs `type`, `url`, and `serverId` diff --git a/skills/copilotkit-integrations/references/integrations/adk.md b/skills/copilotkit-integrations/references/integrations/adk.md new file mode 100644 index 00000000000..c7dc135f224 --- /dev/null +++ b/skills/copilotkit-integrations/references/integrations/adk.md @@ -0,0 +1,156 @@ +# Google ADK Integration + +Google's Agent Development Kit (ADK) integrates with CopilotKit via the `ag-ui-adk` adapter. The agent runs as a FastAPI server. + +## Prerequisites + +- Python 3.12+ +- Node.js 18+ +- Google Makersuite API key (from https://makersuite.google.com/app/apikey) + +## Python Dependencies + +```toml +[project] +dependencies = [ + "fastapi", + "uvicorn[standard]", + "python-dotenv", + "pydantic", + "google-adk", + "google-genai", + "ag-ui-adk", +] +``` + +## Agent Definition (agent/main.py) + +ADK uses `LlmAgent` with callbacks for state management: + +```python +import json +from typing import Dict, Optional + +from ag_ui_adk import ADKAgent, add_adk_fastapi_endpoint +from dotenv import load_dotenv +from fastapi import FastAPI +from google.adk.agents import LlmAgent +from google.adk.agents.callback_context import CallbackContext +from google.adk.models.llm_request import LlmRequest +from google.adk.models.llm_response import LlmResponse +from google.adk.tools import ToolContext +from google.genai import types +from pydantic import BaseModel, Field + +load_dotenv() + +class ProverbsState(BaseModel): + proverbs: list[str] = Field(default_factory=list) + +# Tools use ToolContext.state for shared state +def set_proverbs(tool_context: ToolContext, new_proverbs: list[str]) -> Dict[str, str]: + """Set the list of proverbs.""" + tool_context.state["proverbs"] = new_proverbs + return {"status": "success", "message": "Proverbs updated successfully"} + +def get_weather(tool_context: ToolContext, location: str) -> Dict[str, str]: + """Get the weather for a given location.""" + return {"status": "success", "message": f"The weather in {location} is sunny."} + +# Callback to initialize state +def on_before_agent(callback_context: CallbackContext): + if "proverbs" not in callback_context.state: + callback_context.state["proverbs"] = [] + return None + +# Callback to inject state into system prompt +def before_model_modifier( + callback_context: CallbackContext, llm_request: LlmRequest +) -> Optional[LlmResponse]: + if callback_context.agent_name == "ProverbsAgent": + proverbs_json = json.dumps(callback_context.state.get("proverbs", []), indent=2) + prefix = f"""You are a helpful assistant for maintaining a list of proverbs. + Current state: {proverbs_json} + Use the set_proverbs tool to update the list.""" + + original_instruction = llm_request.config.system_instruction or types.Content( + role="system", parts=[] + ) + if not isinstance(original_instruction, types.Content): + original_instruction = types.Content( + role="system", parts=[types.Part(text=str(original_instruction))] + ) + if original_instruction.parts: + original_instruction.parts[0].text = prefix + (original_instruction.parts[0].text or "") + llm_request.config.system_instruction = original_instruction + return None + +def simple_after_model_modifier( + callback_context: CallbackContext, llm_response: LlmResponse +) -> Optional[LlmResponse]: + """Stop consecutive tool calling -- lets the agent yield control back.""" + return None + +proverbs_agent = LlmAgent( + name="ProverbsAgent", + model="gemini-2.5-flash", + instruction="...", # Agent instructions + tools=[set_proverbs, get_weather], + before_agent_callback=on_before_agent, + before_model_callback=before_model_modifier, + after_model_callback=simple_after_model_modifier, +) + +# Wrap with AG-UI adapter +adk_proverbs_agent = ADKAgent( + adk_agent=proverbs_agent, + user_id="demo_user", + session_timeout_seconds=3600, + use_in_memory_services=True, +) + +app = FastAPI() +add_adk_fastapi_endpoint(app, adk_proverbs_agent, path="/") +``` + +Key patterns: +- State lives in `ToolContext.state` / `CallbackContext.state` (dict-based) +- Use `before_agent_callback` to initialize state +- Use `before_model_callback` to inject current state into the system prompt +- Wrap the ADK agent with `ADKAgent` from `ag-ui-adk`, then use `add_adk_fastapi_endpoint` to mount it +- ADK uses Gemini models by default (`gemini-2.5-flash`) + +## Next.js Route (src/app/api/copilotkit/route.ts) + +```typescript +import { + CopilotRuntime, + ExperimentalEmptyAdapter, + copilotRuntimeNextJSAppRouterEndpoint, +} from "@copilotkit/runtime"; +import { HttpAgent } from "@ag-ui/client"; +import { NextRequest } from "next/server"; + +const runtime = new CopilotRuntime({ + agents: { + my_agent: new HttpAgent({ url: "http://localhost:8000/" }), + }, +}); + +export const POST = async (req: NextRequest) => { + const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({ + runtime, + serviceAdapter: new ExperimentalEmptyAdapter(), + endpoint: "/api/copilotkit", + }); + return handleRequest(req); +}; +``` + +ADK uses the generic `HttpAgent` from `@ag-ui/client`. + +## Environment + +```bash +export GOOGLE_API_KEY="your-google-api-key-here" +``` diff --git a/skills/copilotkit-integrations/references/integrations/agno.md b/skills/copilotkit-integrations/references/integrations/agno.md new file mode 100644 index 00000000000..88835d178f5 --- /dev/null +++ b/skills/copilotkit-integrations/references/integrations/agno.md @@ -0,0 +1,112 @@ +# Agno Integration + +Agno is a Python agent framework with built-in AG-UI support via `AgentOS`. The integration is straightforward -- Agno's `AGUI` interface handles the AG-UI protocol natively. + +## Prerequisites + +- Python 3.12+ +- Node.js 20+ +- OpenAI API key + +## Python Dependencies + +```toml +[project] +dependencies = [ + "agno>=1.7.8", + "openai>=1.88.0", + "yfinance>=0.2.63", + "fastapi>=0.115.13", + "uvicorn>=0.34.3", + "ag-ui-protocol>=0.1.8", + "python-dotenv>=1.0.0", +] +``` + +## Agent Definition (agent/src/agent.py) + +```python +from agno.agent.agent import Agent +from agno.models.openai import OpenAIChat +from agno.tools.yfinance import YFinanceTools +from .tools.backend import get_weather +from .tools.frontend import add_proverb, set_theme_color + +agent = Agent( + model=OpenAIChat(id="gpt-4o"), + tools=[ + # Backend tools -- executed on the server + YFinanceTools(), + get_weather, + # Frontend tools -- executed on the client + add_proverb, + set_theme_color, + ], + description="You are a demonstrative agent for Agno and CopilotKit's integration.", + instructions="Format your response using markdown and use tables to display data where possible.", +) +``` + +Key patterns: +- Agno has built-in tool collections like `YFinanceTools()` for financial data +- Frontend and backend tools are mixed in the same `tools` list -- the distinction is handled by the AG-UI adapter + +## Server (agent/main.py) + +```python +import dotenv +from agno.os import AgentOS +from agno.os.interfaces.agui import AGUI +from src.agent import agent + +dotenv.load_dotenv() + +# Build AgentOS with the AGUI interface +agent_os = AgentOS(agents=[agent], interfaces=[AGUI(agent=agent)]) +app = agent_os.get_app() + +if __name__ == "__main__": + agent_os.serve(app="main:app", port=8000, reload=True) +``` + +Key patterns: +- `AgentOS` is Agno's application container -- it manages agents and interfaces +- `AGUI(agent=agent)` registers the AG-UI interface for the agent +- `agent_os.get_app()` returns a FastAPI/ASGI app +- The AG-UI endpoint is served at `/agui` by default + +## Next.js Route (src/app/api/copilotkit/route.ts) + +```typescript +import { + CopilotRuntime, + ExperimentalEmptyAdapter, + copilotRuntimeNextJSAppRouterEndpoint, +} from "@copilotkit/runtime"; +import { HttpAgent } from "@ag-ui/client"; +import { NextRequest } from "next/server"; + +const runtime = new CopilotRuntime({ + agents: { + agno_agent: new HttpAgent({ url: "http://localhost:8000/agui" }), + }, +}); + +export const POST = async (req: NextRequest) => { + const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({ + runtime, + serviceAdapter: new ExperimentalEmptyAdapter(), + endpoint: "/api/copilotkit", + }); + return handleRequest(req); +}; +``` + +Note the URL path is `/agui` -- this is where Agno's `AGUI` interface mounts. + +## Environment + +```bash +export OPENAI_API_KEY="your-openai-api-key-here" +# Or create agent/.env with the key +``` diff --git a/skills/copilotkit-integrations/references/integrations/crewai.md b/skills/copilotkit-integrations/references/integrations/crewai.md new file mode 100644 index 00000000000..34daaa6f38d --- /dev/null +++ b/skills/copilotkit-integrations/references/integrations/crewai.md @@ -0,0 +1,221 @@ +# CrewAI Integration + +CopilotKit supports two CrewAI patterns: **Crews** (multi-agent task pipelines) and **Flows** (single-agent chat with tool calling). Both run as Python FastAPI servers connected via AG-UI. + +## CrewAI Flows + +Flows use the `crewai.flow.flow` module for a single conversational agent with tool calling, following the ReAct pattern. + +### Prerequisites + +- Python 3.10+ +- Node.js 18+ +- `uv` for Python dependency management +- OpenAI API key + +### Agent Definition (agent/src/agent.py) + +```python +import json +from ag_ui_crewai.sdk import CopilotKitState, copilotkit_stream +from crewai.flow.flow import Flow, listen, router, start +from litellm import completion + +class AgentState(CopilotKitState): + proverbs: list[str] = [] + +GET_WEATHER_TOOL = { + "type": "function", + "function": { + "name": "get_weather", + "description": "Get the current weather in a given location", + "parameters": { + "type": "object", + "properties": { + "location": {"type": "string", "description": "The city and state"} + }, + "required": ["location"], + }, + }, +} + +tools = [GET_WEATHER_TOOL] + +tool_handlers = { + "get_weather": lambda args: f"The weather for {args['location']} is 70 degrees." +} + +class SampleAgentFlow(Flow[AgentState]): + + @start() + @listen("route_follow_up") + async def start_flow(self): + pass + + @router(start_flow) + async def chat(self): + system_prompt = f"You are a helpful assistant. The current proverbs are {self.state.proverbs}." + + # Wrap completion in copilotkit_stream for streaming support + response = await copilotkit_stream( + completion( + model="openai/gpt-4o", + messages=[ + {"role": "system", "content": system_prompt}, + *self.state.messages, + ], + # Bind both CopilotKit frontend actions AND backend tools + tools=[*self.state.copilotkit.actions, GET_WEATHER_TOOL], + parallel_tool_calls=False, + stream=True, + ) + ) + + message = response.choices[0].message + self.state.messages.append(message) + + if message.get("tool_calls"): + tool_call = message["tool_calls"][0] + tool_call_name = tool_call["function"]["name"] + + # If it's a CopilotKit frontend action, return to end (CopilotKit handles it) + if tool_call_name in [ + action["function"]["name"] for action in self.state.copilotkit.actions + ]: + return "route_end" + + # Otherwise handle the backend tool call + handler = tool_handlers[tool_call_name] + result = handler(json.loads(tool_call["function"]["arguments"])) + self.state.messages.append( + {"role": "tool", "content": result, "tool_call_id": tool_call["id"]} + ) + return "route_follow_up" + + return "route_end" + + @listen("route_end") + async def end(self): + pass +``` + +Key patterns: +- Extend `CopilotKitState` from `ag_ui_crewai.sdk` for shared state +- Use `copilotkit_stream()` to wrap `litellm.completion()` for AG-UI streaming +- Frontend actions come from `self.state.copilotkit.actions` -- bind them alongside backend tools +- Route frontend tool calls to `route_end` so CopilotKit handles them client-side +- Route backend tool calls to `route_follow_up` for the next iteration + +### FastAPI Server (agent/server.py) + +```python +from fastapi import FastAPI +from ag_ui_crewai.endpoint import add_crewai_flow_fastapi_endpoint +from src.agent import SampleAgentFlow + +app = FastAPI() +add_crewai_flow_fastapi_endpoint(app, SampleAgentFlow(), "/") +``` + +### Next.js Route (src/app/api/copilotkit/route.ts) + +```typescript +import { + CopilotRuntime, + ExperimentalEmptyAdapter, + copilotRuntimeNextJSAppRouterEndpoint, +} from "@copilotkit/runtime"; +import { HttpAgent } from "@ag-ui/client"; +import { NextRequest } from "next/server"; + +const runtime = new CopilotRuntime({ + agents: { + sample_agent: new HttpAgent({ url: "http://localhost:8000/" }), + }, +}); + +export const POST = async (req: NextRequest) => { + const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({ + runtime, + serviceAdapter: new ExperimentalEmptyAdapter(), + endpoint: "/api/copilotkit", + }); + return handleRequest(req); +}; +``` + +CrewAI Flows use the generic `HttpAgent` from `@ag-ui/client`. + +--- + +## CrewAI Crews + +Crews are multi-agent pipelines with defined roles, tasks, and processes. + +### Agent Definition + +CrewAI Crews use YAML-configured agents and tasks via the `@CrewBase` decorator: + +```python +from crewai import Agent, Crew, Process, Task +from crewai.project import CrewBase, agent, crew, task + +@CrewBase +class LatestAiDevelopment(): + """LatestAiDevelopment crew""" + name: str = "LatestAiDevelopment" + + @agent + def researcher(self) -> Agent: + return Agent(config=self.agents_config['researcher'], verbose=True) + + @agent + def reporting_analyst(self) -> Agent: + return Agent(config=self.agents_config['reporting_analyst'], verbose=True) + + @task + def research_task(self) -> Task: + return Task(config=self.tasks_config['research_task']) + + @task + def reporting_task(self) -> Task: + return Task(config=self.tasks_config['reporting_task'], output_file='report.md') + + @crew + def crew(self) -> Crew: + return Crew( + name=self.name, + agents=self.agents, + tasks=self.tasks, + process=Process.sequential, + verbose=True, + chat_llm="gpt-4o", + ) +``` + +### FastAPI Server + +```python +from fastapi import FastAPI +from ag_ui_crewai.endpoint import add_crewai_crew_fastapi_endpoint +from src.latest_ai_development.crew import LatestAiDevelopment + +app = FastAPI() +add_crewai_crew_fastapi_endpoint(app, LatestAiDevelopment(), "/") +``` + +Note the different function: `add_crewai_crew_fastapi_endpoint` vs `add_crewai_flow_fastapi_endpoint`. + +### Next.js Route + +```typescript +import { CrewAIAgent } from "@ag-ui/crewai"; + +const runtime = new CopilotRuntime({ + agents: { + starterAgent: new CrewAIAgent({ url: "http://localhost:8000/" }), + }, +}); +``` + +CrewAI Crews use `CrewAIAgent` from `@ag-ui/crewai` (not `HttpAgent`). diff --git a/skills/copilotkit-integrations/references/integrations/langgraph.md b/skills/copilotkit-integrations/references/integrations/langgraph.md new file mode 100644 index 00000000000..3e5c039ad68 --- /dev/null +++ b/skills/copilotkit-integrations/references/integrations/langgraph.md @@ -0,0 +1,276 @@ +# LangGraph Integration + +CopilotKit supports LangGraph in three configurations: Python with self-hosted FastAPI, Python with LangGraph Platform, and JavaScript/TypeScript. All use the AG-UI protocol. + +## Python (Self-Hosted FastAPI) + +This is the `langgraph-fastapi` example pattern. You run the LangGraph agent as a standalone FastAPI server and connect via `LangGraphHttpAgent`. + +### Prerequisites + +- Python 3.10+ +- Node.js 18+ +- OpenAI API key +- `poetry` or `uv` for Python dependency management + +### Python Dependencies + +```toml +# pyproject.toml +[project] +dependencies = [ + "copilotkit==0.1.74", + "langchain==1.0.1", + "langchain-openai==1.0.1", + "langgraph==1.0.1", + "fastapi==0.115.12", + "uvicorn>=0.38.0", + "python-dotenv>=1.0.0", + "ag-ui-langgraph==0.0.22", + "pydantic>=2.0.0,<3.0.0", +] +``` + +### Agent Definition (agent/src/agent.py) + +The agent extends `CopilotKitState` for shared state and uses the standard ReAct pattern: + +```python +from copilotkit import CopilotKitState +from langchain.tools import tool +from langchain_core.messages import SystemMessage +from langchain_core.runnables import RunnableConfig +from langchain_openai import ChatOpenAI +from langgraph.checkpoint.memory import MemorySaver +from langgraph.graph import StateGraph +from langgraph.prebuilt import ToolNode +from langgraph.types import Command +from typing_extensions import Literal +from src.util import should_route_to_tool_node + +class AgentState(CopilotKitState): + proverbs: list[str] + +@tool +def get_weather(location: str): + """Get the weather for a given location.""" + return f"The weather for {location} is 70 degrees." + +tools = [get_weather] + +async def chat_node( + state: AgentState, config: RunnableConfig +) -> Command[Literal["tool_node", "__end__"]]: + model = ChatOpenAI(model="gpt-4o") + # Bind both frontend (CopilotKit) actions and backend tools + fe_tools = state.get("copilotkit", {}).get("actions", []) + model_with_tools = model.bind_tools([*fe_tools, *tools]) + + system_message = SystemMessage( + content=f"You are a helpful assistant. The current proverbs are {state.get('proverbs', [])}." + ) + response = await model_with_tools.ainvoke( + [system_message, *state["messages"]], config, + ) + + tool_calls = response.tool_calls + if tool_calls and should_route_to_tool_node(tool_calls, fe_tools): + return Command(goto="tool_node", update={"messages": response}) + return Command(goto="__end__", update={"messages": response}) + +workflow = StateGraph(AgentState) +workflow.add_node("chat_node", chat_node) +workflow.add_node("tool_node", ToolNode(tools=tools)) +workflow.add_edge("tool_node", "chat_node") +workflow.set_entry_point("chat_node") + +graph = workflow.compile(checkpointer=MemorySaver()) +``` + +Key pattern: `CopilotKitState` provides the `copilotkit` field containing `actions` (frontend tools). You must bind both frontend actions and backend tools to the model, then route frontend tool calls back to CopilotKit (not the ToolNode). + +### FastAPI Server (agent/main.py) + +```python +from fastapi import FastAPI +from copilotkit import LangGraphAGUIAgent +from ag_ui_langgraph import add_langgraph_fastapi_endpoint +from src.agent import graph + +app = FastAPI() + +add_langgraph_fastapi_endpoint( + app=app, + agent=LangGraphAGUIAgent( + name="sample_agent", + description="An example agent.", + graph=graph, + ), + path="/", +) +``` + +### Next.js Route (src/app/api/copilotkit/route.ts) + +```typescript +import { + CopilotRuntime, + ExperimentalEmptyAdapter, + copilotRuntimeNextJSAppRouterEndpoint, +} from "@copilotkit/runtime"; +import { LangGraphHttpAgent } from "@copilotkit/runtime/langgraph"; +import { NextRequest } from "next/server"; + +const runtime = new CopilotRuntime({ + agents: { + sample_agent: new LangGraphHttpAgent({ + url: process.env.AGENT_URL || "http://localhost:8123", + }), + }, +}); + +export const POST = async (req: NextRequest) => { + const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({ + runtime, + serviceAdapter: new ExperimentalEmptyAdapter(), + endpoint: "/api/copilotkit", + }); + return handleRequest(req); +}; +``` + +Use `LangGraphHttpAgent` (from `@copilotkit/runtime/langgraph`) for self-hosted agents. The default port is 8123. + +--- + +## Python (LangGraph Platform / Monorepo) + +This is the `langgraph-python` example pattern. Uses `LangGraphAgent` which connects to a LangGraph deployment (local or cloud). + +### Next.js Route + +```typescript +import { LangGraphAgent } from "@copilotkit/runtime/langgraph"; + +const defaultAgent = new LangGraphAgent({ + deploymentUrl: process.env.LANGGRAPH_DEPLOYMENT_URL || "http://localhost:8123", + graphId: "sample_agent", + langsmithApiKey: process.env.LANGSMITH_API_KEY || "", +}); + +const runtime = new CopilotRuntime({ + agents: { default: defaultAgent }, + a2ui: { injectA2UITool: true }, + mcpApps: { + servers: [ + { + type: "http", + url: process.env.MCP_SERVER_URL || "https://mcp.excalidraw.com", + serverId: "example_mcp_app", + }, + ], + }, +}); +``` + +Key difference from self-hosted: `LangGraphAgent` uses `deploymentUrl` and `graphId` (and optionally `langsmithApiKey`), while `LangGraphHttpAgent` uses a plain `url`. + +--- + +## JavaScript / TypeScript + +This is the `langgraph-js` example pattern. The agent is a TypeScript LangGraph graph running in a separate Node.js process. + +### Agent Definition (apps/agent/src/agent.ts) + +```typescript +import { z } from "zod"; +import { tool } from "@langchain/core/tools"; +import { ToolNode } from "@langchain/langgraph/prebuilt"; +import { AIMessage, SystemMessage } from "@langchain/core/messages"; +import { MemorySaver, START, StateGraph } from "@langchain/langgraph"; +import { ChatOpenAI } from "@langchain/openai"; +import { + convertActionsToDynamicStructuredTools, + CopilotKitStateAnnotation, +} from "@copilotkit/sdk-js/langgraph"; +import { Annotation } from "@langchain/langgraph"; + +const AgentStateAnnotation = Annotation.Root({ + ...CopilotKitStateAnnotation.spec, + proverbs: Annotation, +}); + +export type AgentState = typeof AgentStateAnnotation.State; + +const getWeather = tool( + (args) => `The weather for ${args.location} is 70 degrees.`, + { + name: "getWeather", + description: "Get the weather for a given location.", + schema: z.object({ location: z.string() }), + }, +); + +const tools = [getWeather]; + +async function chat_node(state: AgentState, config) { + const model = new ChatOpenAI({ temperature: 0, model: "gpt-4o" }); + const modelWithTools = model.bindTools!([ + ...convertActionsToDynamicStructuredTools(state.copilotkit?.actions ?? []), + ...tools, + ]); + + const systemMessage = new SystemMessage({ + content: `You are a helpful assistant. The current proverbs are ${JSON.stringify(state.proverbs)}.`, + }); + const response = await modelWithTools.invoke( + [systemMessage, ...state.messages], config, + ); + return { messages: response }; +} + +function shouldContinue({ messages, copilotkit }: AgentState) { + const lastMessage = messages[messages.length - 1] as AIMessage; + if (lastMessage.tool_calls?.length) { + const actions = copilotkit?.actions; + const toolCallName = lastMessage.tool_calls![0].name; + if (!actions || actions.every((action) => action.name !== toolCallName)) { + return "tool_node"; + } + } + return "__end__"; +} + +const workflow = new StateGraph(AgentStateAnnotation) + .addNode("chat_node", chat_node) + .addNode("tool_node", new ToolNode(tools)) + .addEdge(START, "chat_node") + .addEdge("tool_node", "chat_node") + .addConditionalEdges("chat_node", shouldContinue); + +export const graph = workflow.compile({ checkpointer: new MemorySaver() }); +``` + +Key JS-specific patterns: +- Use `CopilotKitStateAnnotation` from `@copilotkit/sdk-js/langgraph` to include CopilotKit state +- Use `convertActionsToDynamicStructuredTools()` to convert frontend actions to LangChain tools +- Check `copilotkit.actions` to determine whether a tool call should route to `tool_node` (backend) or `__end__` (frontend) + +### Next.js Route + +Same as the Platform pattern -- uses `LangGraphAgent` with `deploymentUrl` and `graphId`. + +## Monorepo Structure (JS) + +The JS variant uses a Turborepo monorepo: + +``` +apps/ + web/ # Next.js frontend + agent/ # LangGraph agent (Node.js) +pnpm-workspace.yaml +turbo.json +``` + +Run `pnpm dev` to start both apps via Turborepo. diff --git a/skills/copilotkit-integrations/references/integrations/llamaindex.md b/skills/copilotkit-integrations/references/integrations/llamaindex.md new file mode 100644 index 00000000000..3984c97575e --- /dev/null +++ b/skills/copilotkit-integrations/references/integrations/llamaindex.md @@ -0,0 +1,128 @@ +# LlamaIndex Integration + +LlamaIndex integrates with CopilotKit via the `llama-index-protocols-ag-ui` package, which provides a FastAPI router for AG-UI-compatible workflows. + +## Prerequisites + +- Python 3.9+ (< 3.14) +- Node.js 18+ +- `uv` for Python dependency management +- OpenAI API key + +## Python Dependencies + +```toml +[project] +dependencies = [ + "llama-index-core>=0.14,<0.15", + "llama-index-llms-openai>=0.5.0,<0.6.0", + "llama-index-protocols-ag-ui>=0.2.2", + "uvicorn>=0.27.0", + "fastapi>=0.100.0", + "python-dotenv>=1.0.0", +] +``` + +## Agent Definition (agent/src/agent.py) + +LlamaIndex uses `get_ag_ui_workflow_router` to create a FastAPI router with frontend and backend tools: + +```python +from typing import Annotated +from llama_index.llms.openai import OpenAI +from llama_index.protocols.ag_ui.router import get_ag_ui_workflow_router + +# Frontend tool -- executed on the client, agent just sees the return string +def change_theme_color( + theme_color: Annotated[str, "The hex color value. i.e. '#123456'"], +) -> str: + """Change the background color of the chat.""" + return f"Changing background to {theme_color}" + +async def add_proverb( + proverb: Annotated[str, "The proverb to add. Make it witty, short and concise."], +) -> str: + """Add a proverb to the list of proverbs.""" + return f"Added proverb: {proverb}" + +# Backend tool -- executed on the server +async def get_weather( + location: Annotated[str, "The location to get the weather for."], +) -> str: + """Get the weather for a given location.""" + return f"The weather in {location} is sunny and 70 degrees." + +agentic_chat_router = get_ag_ui_workflow_router( + llm=OpenAI(model="gpt-4.1"), + frontend_tools=[change_theme_color, add_proverb], + backend_tools=[get_weather], + system_prompt="You are a helpful assistant that can add proverbs, get weather, and change the background color.", + initial_state={ + "proverbs": ["CopilotKit may be new, but its the best thing since sliced bread."], + }, +) +``` + +Key patterns: +- `get_ag_ui_workflow_router()` creates a complete FastAPI router with AG-UI support +- Tools are split into `frontend_tools` (executed client-side, agent sees the return string as a placeholder) and `backend_tools` (executed server-side) +- `initial_state` sets the starting shared state +- Tools use Python type annotations (`Annotated[str, "description"]`) for parameter descriptions -- no separate schema definitions needed + +## FastAPI Server (agent/main.py) + +```python +import uvicorn +from dotenv import load_dotenv +from fastapi import FastAPI +from src.agent import agentic_chat_router + +app = FastAPI() +app.include_router(agentic_chat_router) + +def main(): + load_dotenv() + uvicorn.run("main:app", host="127.0.0.1", port=9000, reload=True) + +if __name__ == "__main__": + main() +``` + +Note: LlamaIndex defaults to port **9000** (not 8000). + +## Next.js Route (src/app/api/copilotkit/route.ts) + +```typescript +import { + CopilotRuntime, + ExperimentalEmptyAdapter, + copilotRuntimeNextJSAppRouterEndpoint, +} from "@copilotkit/runtime"; +import { LlamaIndexAgent } from "@ag-ui/llamaindex"; +import { NextRequest } from "next/server"; + +export async function POST(request: NextRequest) { + const runtime = new CopilotRuntime({ + agents: { + sample_agent: new LlamaIndexAgent({ + url: "http://127.0.0.1:9000/run", + }), + }, + }); + + const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({ + runtime, + serviceAdapter: new ExperimentalEmptyAdapter(), + endpoint: `/api/copilotkit`, + }); + return handleRequest(request); +} +``` + +LlamaIndex uses `LlamaIndexAgent` from `@ag-ui/llamaindex`. Note the URL path is `/run` (appended to the base URL). + +## Environment + +```bash +export OPENAI_API_KEY="your-openai-api-key-here" +``` diff --git a/skills/copilotkit-integrations/references/integrations/mastra.md b/skills/copilotkit-integrations/references/integrations/mastra.md new file mode 100644 index 00000000000..c4e45f3ff22 --- /dev/null +++ b/skills/copilotkit-integrations/references/integrations/mastra.md @@ -0,0 +1,149 @@ +# Mastra Integration + +Mastra is a TypeScript-native agent framework. The CopilotKit integration runs entirely in Node.js -- no separate Python server needed. The agent runs within the Next.js process via Mastra's dev server. + +## Prerequisites + +- Node.js 18+ +- OpenAI API key + +## Key Dependencies + +```json +{ + "@ag-ui/mastra": "beta", + "@mastra/core": "beta", + "@mastra/memory": "beta", + "@mastra/libsql": "beta", + "mastra": "beta", + "@copilotkit/react": "latest", + "@copilotkit/runtime": "latest" +} +``` + +## Agent Definition (src/mastra/agents/index.ts) + +```typescript +import { openai } from "@ai-sdk/openai"; +import { Agent } from "@mastra/core/agent"; +import { weatherTool } from "@/mastra/tools"; +import { LibSQLStore } from "@mastra/libsql"; +import { z } from "zod"; +import { Memory } from "@mastra/memory"; + +// Define shared state schema with Zod +export const AgentState = z.object({ + proverbs: z.array(z.string()).default([]), +}); + +export const weatherAgent = new Agent({ + id: "weather-agent", + name: "Weather Agent", + tools: { weatherTool }, + model: openai("gpt-4o"), + instructions: "You are a helpful assistant.", + memory: new Memory({ + storage: new LibSQLStore({ + id: "weather-agent-memory", + url: "file::memory:", + }), + options: { + workingMemory: { + enabled: true, + schema: AgentState, + }, + }, + }), +}); +``` + +Key patterns: +- Shared state is defined as a Zod schema and passed to Mastra's `Memory` via `workingMemory.schema` +- Tools are created with Mastra's `createTool()` helper +- The agent uses `@ai-sdk/openai` for the model provider + +## Tools (src/mastra/tools/index.ts) + +```typescript +import { createTool } from "@mastra/core/tools"; +import { z } from "zod"; + +export const weatherTool = createTool({ + id: "get-weather", + description: "Get current weather for a location", + inputSchema: z.object({ + location: z.string().describe("City name"), + }), + outputSchema: z.object({ + temperature: z.number(), + feelsLike: z.number(), + humidity: z.number(), + windSpeed: z.number(), + windGust: z.number(), + conditions: z.string(), + location: z.string(), + }), + execute: async (inputData) => { + // Call weather API... + return await getWeather(inputData.location); + }, +}); +``` + +## Mastra Instance (src/mastra/index.ts) + +```typescript +import { Mastra } from "@mastra/core/mastra"; +import { LibSQLStore } from "@mastra/libsql"; +import { weatherAgent } from "./agents"; + +export const mastra = new Mastra({ + agents: { weatherAgent }, + storage: new LibSQLStore({ id: "mastra-storage", url: ":memory:" }), +}); +``` + +## Next.js Route (src/app/api/copilotkit/route.ts) + +```typescript +import { + CopilotRuntime, + ExperimentalEmptyAdapter, + copilotRuntimeNextJSAppRouterEndpoint, +} from "@copilotkit/runtime"; +import { MastraAgent } from "@ag-ui/mastra"; +import { NextRequest } from "next/server"; +import { mastra } from "@/mastra"; + +export const POST = async (req: NextRequest) => { + const runtime = new CopilotRuntime({ + // @ts-expect-error - typing issue in current beta + agents: MastraAgent.getLocalAgents({ mastra }), + }); + + const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({ + runtime, + serviceAdapter: new ExperimentalEmptyAdapter(), + endpoint: "/api/copilotkit", + }); + return handleRequest(req); +}; +``` + +Key difference from other integrations: `MastraAgent.getLocalAgents({ mastra })` automatically discovers all agents registered in the Mastra instance. No need to manually specify URLs or create agent instances -- the agents run in-process. + +## Running + +Mastra uses its own dev server alongside Next.js: + +```json +{ + "scripts": { + "dev": "next dev --turbopack", + "dev:agent": "mastra dev", + "dev:ui": "next dev --turbopack" + } +} +``` + +Run `pnpm dev` to start the Next.js app (Mastra agents load in-process). Use `pnpm dev:agent` for the standalone Mastra dev server with its own UI. diff --git a/skills/copilotkit-integrations/references/integrations/ms-agent-framework.md b/skills/copilotkit-integrations/references/integrations/ms-agent-framework.md new file mode 100644 index 00000000000..74f2741569f --- /dev/null +++ b/skills/copilotkit-integrations/references/integrations/ms-agent-framework.md @@ -0,0 +1,245 @@ +# Microsoft Agent Framework Integration + +Microsoft Agent Framework integrates with CopilotKit via `agent-framework-ag-ui` (Python) or `Microsoft.Agents.AI.Hosting.AGUI.AspNetCore` (.NET). Both run as HTTP servers exposing AG-UI endpoints. + +## Python + +### Prerequisites + +- Python 3.12+ +- Node.js 20+ +- OpenAI API key or Azure OpenAI credentials + +### Python Dependencies + +```toml +[project] +dependencies = [ + "agent-framework-ag-ui>=1.0.0b251117", + "python-dotenv", +] +``` + +The `agent-framework-ag-ui` package pulls in the core `agent-framework` package. + +### Agent Definition (agent/agent.py) + +```python +from __future__ import annotations +from textwrap import dedent +from typing import Annotated + +from agent_framework import ChatAgent, ChatClientProtocol, ai_function +from agent_framework_ag_ui import AgentFrameworkAgent +from pydantic import Field + +# State schema for AG-UI shared state +STATE_SCHEMA: dict[str, object] = { + "proverbs": { + "type": "array", + "items": {"type": "string"}, + "description": "Ordered list of the user's saved proverbs.", + } +} + +# Maps tool names to state fields for predictive state updates +PREDICT_STATE_CONFIG: dict[str, dict[str, str]] = { + "proverbs": { + "tool": "update_proverbs", + "tool_argument": "proverbs", + } +} + +@ai_function( + name="update_proverbs", + description="Replace the entire list of proverbs with the provided values.", +) +def update_proverbs( + proverbs: Annotated[ + list[str], + Field(description="The complete source of truth for the user's proverbs."), + ], +) -> str: + return f"Proverbs updated. Tracking {len(proverbs)} item(s)." + +@ai_function( + name="get_weather", + description="Share a quick weather update for a location.", +) +def get_weather( + location: Annotated[str, Field(description="The city or region to describe.")], +) -> str: + return f"The weather in {location.strip().title()} is mild with a light breeze." + +@ai_function( + name="go_to_moon", + description="Request human-in-the-loop confirmation before launching.", + approval_mode="always_require", +) +def go_to_moon() -> str: + return "Mission control requested. Awaiting human approval." + +def create_agent(chat_client: ChatClientProtocol) -> AgentFrameworkAgent: + base_agent = ChatAgent( + name="proverbs_agent", + instructions=dedent("..."), # Agent instructions + chat_client=chat_client, + tools=[update_proverbs, get_weather, go_to_moon], + ) + return AgentFrameworkAgent( + agent=base_agent, + name="CopilotKitMicrosoftAgentFrameworkAgent", + description="Manages proverbs, weather, and moon launches.", + state_schema=STATE_SCHEMA, + predict_state_config=PREDICT_STATE_CONFIG, + require_confirmation=False, + ) +``` + +Key patterns: +- `@ai_function` decorator defines tools with `name`, `description`, and optional `approval_mode` +- `approval_mode="always_require"` enables human-in-the-loop approval +- `STATE_SCHEMA` defines the AG-UI shared state structure +- `PREDICT_STATE_CONFIG` maps state fields to tool names/arguments for predictive updates -- when a tool is called, the framework can predict the state change without waiting for execution +- `AgentFrameworkAgent` wraps the base `ChatAgent` for AG-UI compatibility + +### Server (agent/main.py) + +```python +from agent_framework.openai import OpenAIChatClient +from agent_framework_ag_ui import add_agent_framework_fastapi_endpoint +from fastapi import FastAPI + +chat_client = OpenAIChatClient( + model_id=os.getenv("OPENAI_CHAT_MODEL_ID", "gpt-4o-mini"), + api_key=os.getenv("OPENAI_API_KEY"), +) +my_agent = create_agent(chat_client) + +app = FastAPI() +add_agent_framework_fastapi_endpoint(app=app, agent=my_agent, path="/") +``` + +For Azure OpenAI: + +```python +from agent_framework.azure import AzureOpenAIChatClient +from azure.identity import DefaultAzureCredential + +chat_client = AzureOpenAIChatClient( + credential=DefaultAzureCredential(), + deployment_name=os.getenv("AZURE_OPENAI_CHAT_DEPLOYMENT_NAME", "gpt-4o-mini"), + endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"), +) +``` + +### Environment + +OpenAI: +``` +OPENAI_API_KEY=sk-... +OPENAI_CHAT_MODEL_ID=gpt-4o-mini +``` + +Azure OpenAI: +``` +AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com/ +AZURE_OPENAI_CHAT_DEPLOYMENT_NAME=gpt-4o-mini +``` + +--- + +## .NET (C#) + +### Prerequisites + +- .NET 9.0 SDK +- Node.js 20+ +- GitHub Personal Access Token (for GitHub Models API) + +### Agent Definition (agent/Program.cs) + +```csharp +using Microsoft.Agents.AI; +using Microsoft.Agents.AI.Hosting.AGUI.AspNetCore; +using Microsoft.Extensions.AI; + +WebApplicationBuilder builder = WebApplication.CreateBuilder(args); +builder.Services.AddAGUI(); + +WebApplication app = builder.Build(); + +var agentFactory = new ProverbsAgentFactory(builder.Configuration, ...); +app.MapAGUI("/", agentFactory.CreateProverbsAgent()); + +await app.RunAsync(); + +public class ProverbsState +{ + public List Proverbs { get; set; } = []; +} + +public class ProverbsAgentFactory +{ + public AIAgent CreateProverbsAgent() + { + var chatClient = _openAiClient.GetChatClient("gpt-4o-mini").AsIChatClient(); + var chatClientAgent = new ChatClientAgent( + chatClient, + name: "ProverbsAgent", + description: "...", + tools: [ + AIFunctionFactory.Create(GetProverbs, ...), + AIFunctionFactory.Create(AddProverbs, ...), + AIFunctionFactory.Create(SetProverbs, ...), + AIFunctionFactory.Create(GetWeather, ...), + ]); + return new SharedStateAgent(chatClientAgent, _jsonSerializerOptions); + } +} +``` + +Key .NET patterns: +- `builder.Services.AddAGUI()` registers AG-UI services +- `app.MapAGUI("/", agent)` maps the AG-UI endpoint +- `SharedStateAgent` wraps `ChatClientAgent` for state management +- Tools are created via `AIFunctionFactory.Create()` +- Uses GitHub Models API (free tier) via OpenAI client with custom endpoint + +### Setup + +```bash +cd agent +dotnet user-secrets set GitHubToken "$(gh auth token)" +``` + +--- + +## Next.js Route (both Python and .NET) + +```typescript +import { + CopilotRuntime, + ExperimentalEmptyAdapter, + copilotRuntimeNextJSAppRouterEndpoint, +} from "@copilotkit/runtime"; +import { HttpAgent } from "@ag-ui/client"; +import { NextRequest } from "next/server"; + +const runtime = new CopilotRuntime({ + agents: { + my_agent: new HttpAgent({ url: "http://localhost:8000/" }), + }, +}); + +export const POST = async (req: NextRequest) => { + const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({ + runtime, + serviceAdapter: new ExperimentalEmptyAdapter(), + endpoint: "/api/copilotkit", + }); + return handleRequest(req); +}; +``` + +Both Python and .NET variants use `HttpAgent` from `@ag-ui/client`. diff --git a/skills/copilotkit-integrations/references/integrations/pydantic-ai.md b/skills/copilotkit-integrations/references/integrations/pydantic-ai.md new file mode 100644 index 00000000000..5eb7f26c576 --- /dev/null +++ b/skills/copilotkit-integrations/references/integrations/pydantic-ai.md @@ -0,0 +1,138 @@ +# PydanticAI Integration + +PydanticAI has first-class AG-UI support built into `pydantic-ai-slim[ag-ui]`. The integration is minimal -- the agent exposes itself as an ASGI app with `agent.to_ag_ui()`. + +## Prerequisites + +- Python 3.12+ +- Node.js 20+ +- `uv` for Python dependency management +- OpenAI API key + +## Python Dependencies + +```toml +[project] +dependencies = [ + "uvicorn", + "pydantic-ai-slim[ag-ui]", + "pydantic-ai-slim[openai]", + "python-dotenv", +] +``` + +## Agent Definition (agent/src/agent.py) + +```python +from textwrap import dedent +from pydantic import BaseModel, Field +from pydantic_ai import Agent, RunContext +from pydantic_ai.ag_ui import StateDeps +from ag_ui.core import EventType, StateSnapshotEvent +from pydantic_ai.models.openai import OpenAIResponsesModel +from dotenv import load_dotenv + +load_dotenv() + +# Define shared state as a Pydantic model +class ProverbsState(BaseModel): + proverbs: list[str] = Field( + default_factory=list, + description='The list of already written proverbs', + ) + +# Create the agent with StateDeps for AG-UI state management +agent = Agent( + model=OpenAIResponsesModel('gpt-4.1-mini'), + deps_type=StateDeps[ProverbsState], + system_prompt=dedent(""" + You are a helpful assistant that helps manage and discuss proverbs. + When discussing proverbs, ALWAYS use the get_proverbs tool first. + """).strip() +) + +# Tools that read state +@agent.tool +def get_proverbs(ctx: RunContext[StateDeps[ProverbsState]]) -> list[str]: + """Get the current list of proverbs.""" + return ctx.deps.state.proverbs + +# Tools that modify state -- return StateSnapshotEvent to sync with frontend +@agent.tool +async def add_proverbs( + ctx: RunContext[StateDeps[ProverbsState]], proverbs: list[str] +) -> StateSnapshotEvent: + ctx.deps.state.proverbs.extend(proverbs) + return StateSnapshotEvent( + type=EventType.STATE_SNAPSHOT, + snapshot=ctx.deps.state, + ) + +@agent.tool +async def set_proverbs( + ctx: RunContext[StateDeps[ProverbsState]], proverbs: list[str] +) -> StateSnapshotEvent: + ctx.deps.state.proverbs = proverbs + return StateSnapshotEvent( + type=EventType.STATE_SNAPSHOT, + snapshot=ctx.deps.state, + ) + +@agent.tool +def get_weather(_: RunContext[StateDeps[ProverbsState]], location: str) -> str: + """Get the weather for a given location.""" + return f"The weather in {location} is sunny." +``` + +Key patterns: +- Use `StateDeps[YourStateModel]` as the `deps_type` to enable AG-UI shared state +- State-reading tools access `ctx.deps.state` directly +- State-modifying tools return `StateSnapshotEvent` with the updated state -- this triggers a state sync to the frontend +- The `RunContext` provides access to both state and dependencies + +## FastAPI Server (agent/src/main.py) + +```python +from agent import ProverbsState, StateDeps, agent + +app = agent.to_ag_ui(deps=StateDeps(ProverbsState())) + +if __name__ == "__main__": + import uvicorn + uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True) +``` + +The `agent.to_ag_ui()` call creates a full ASGI application. Pass initial `deps` with default state. + +## Next.js Route (src/app/api/copilotkit/route.ts) + +```typescript +import { + CopilotRuntime, + ExperimentalEmptyAdapter, + copilotRuntimeNextJSAppRouterEndpoint, +} from "@copilotkit/runtime"; +import { HttpAgent } from "@ag-ui/client"; +import { NextRequest } from "next/server"; + +const runtime = new CopilotRuntime({ + agents: { + sample_agent: new HttpAgent({ url: "http://localhost:8000/" }), + }, +}); + +export const POST = async (req: NextRequest) => { + const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({ + runtime, + serviceAdapter: new ExperimentalEmptyAdapter(), + endpoint: "/api/copilotkit", + }); + return handleRequest(req); +}; +``` + +PydanticAI uses the generic `HttpAgent` from `@ag-ui/client`. + +## Frontend Usage + +The frontend is standard CopilotKit -- `useAgent` for shared state, `useRenderToolCall` for generative UI, `useHumanInTheLoop` for approval flows. See the main SKILL.md for common patterns. diff --git a/skills/copilotkit-integrations/references/integrations/strands.md b/skills/copilotkit-integrations/references/integrations/strands.md new file mode 100644 index 00000000000..63849b112c8 --- /dev/null +++ b/skills/copilotkit-integrations/references/integrations/strands.md @@ -0,0 +1,157 @@ +# Strands Integration + +AWS Strands Agents integrates with CopilotKit via `ag_ui_strands`. It features explicit control over tool behaviors including state extraction from tool arguments and prompt injection. + +## Prerequisites + +- Python 3.12+ (< 3.14) +- Node.js 20+ +- OpenAI API key + +## Python Dependencies + +```toml +[project] +dependencies = [ + "ag-ui-protocol>=0.1.5", + "fastapi>=0.115.12", + "uvicorn>=0.34.3", + "strands-agents[OpenAI]>=1.15.0", + "strands-agents-tools>=0.2.14", + "ag_ui_strands~=0.1.0", +] +``` + +## Agent Definition (agent/main.py) + +```python +import json +import os +from typing import List + +from ag_ui_strands import ( + StrandsAgent, + StrandsAgentConfig, + ToolBehavior, + create_strands_app, +) +from dotenv import load_dotenv +from pydantic import BaseModel, Field +from strands import Agent, tool +from strands.models.openai import OpenAIModel + +load_dotenv() + +class ProverbsList(BaseModel): + proverbs: List[str] = Field(description="The complete list of proverbs") + +@tool +def get_weather(location: str): + """Get the weather for a location.""" + return json.dumps({"location": "70 degrees"}) + +@tool +def set_theme_color(theme_color: str): + """Change the theme color of the UI. Frontend tool -- returns None.""" + return None + +@tool +def update_proverbs(proverbs_list: ProverbsList): + """Update the complete list of proverbs. Always provide the entire list.""" + return "Proverbs updated successfully" + +# Prompt builder injects current state into the user message +def build_proverbs_prompt(input_data, user_message: str) -> str: + state_dict = getattr(input_data, "state", None) + if isinstance(state_dict, dict) and "proverbs" in state_dict: + proverbs_json = json.dumps(state_dict["proverbs"], indent=2) + return f"Current proverbs list:\n{proverbs_json}\n\nUser request: {user_message}" + return user_message + +# Extract state from tool arguments for state snapshot emission +async def proverbs_state_from_args(context): + try: + tool_input = context.tool_input + if isinstance(tool_input, str): + tool_input = json.loads(tool_input) + proverbs_data = tool_input.get("proverbs_list", tool_input) + if isinstance(proverbs_data, dict): + return {"proverbs": proverbs_data.get("proverbs", [])} + return {"proverbs": []} + except Exception: + return None + +# Configure AG-UI behaviors per tool +shared_state_config = StrandsAgentConfig( + state_context_builder=build_proverbs_prompt, + tool_behaviors={ + "update_proverbs": ToolBehavior( + skip_messages_snapshot=True, + state_from_args=proverbs_state_from_args, + ) + }, +) + +model = OpenAIModel( + client_args={"api_key": os.getenv("OPENAI_API_KEY", "")}, + model_id="gpt-4o", +) + +strands_agent = Agent( + model=model, + system_prompt="You are a helpful assistant that manages proverbs.", + tools=[update_proverbs, get_weather, set_theme_color], +) + +# Wrap with AG-UI integration +agui_agent = StrandsAgent( + agent=strands_agent, + name="proverbs_agent", + description="A proverbs assistant", + config=shared_state_config, +) + +# Create the FastAPI app +app = create_strands_app(agui_agent, os.getenv("AGENT_PATH", "/")) +``` + +Key patterns: +- `StrandsAgentConfig` controls AG-UI behavior: + - `state_context_builder` -- function that injects state into the prompt + - `tool_behaviors` -- per-tool configuration for state extraction and message handling +- `ToolBehavior` options: + - `skip_messages_snapshot=True` -- skip message snapshot after this tool (for state-only tools) + - `state_from_args` -- async function to extract state from tool arguments for `STATE_SNAPSHOT` events +- Frontend tools return `None` -- actual execution happens on the client +- `create_strands_app()` builds a complete FastAPI application + +## Next.js Route (src/app/api/copilotkit/route.ts) + +```typescript +import { + CopilotRuntime, + ExperimentalEmptyAdapter, + copilotRuntimeNextJSAppRouterEndpoint, +} from "@copilotkit/runtime"; +import { HttpAgent } from "@ag-ui/client"; +import { NextRequest } from "next/server"; + +const runtime = new CopilotRuntime({ + agents: { + strands_agent: new HttpAgent({ + url: process.env.STRANDS_AGENT_URL || "http://localhost:8000", + }), + }, +}); + +export const POST = async (req: NextRequest) => { + const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({ + runtime, + serviceAdapter: new ExperimentalEmptyAdapter(), + endpoint: "/api/copilotkit", + }); + return handleRequest(req); +}; +``` + +Strands uses the generic `HttpAgent` from `@ag-ui/client`. diff --git a/skills/copilotkit-integrations/sources.md b/skills/copilotkit-integrations/sources.md new file mode 100644 index 00000000000..36d504d58e8 --- /dev/null +++ b/skills/copilotkit-integrations/sources.md @@ -0,0 +1,40 @@ +# Sources + +Files and directories read from CopilotKit/CopilotKit to generate this skill's references. +Generated: 2026-03-28 + +## integrations/a2a.md +- examples/integrations/a2a-middleware/ (A2AMiddlewareAgent pattern, Next.js route, orchestrator setup) +- examples/integrations/a2a-a2ui/ (A2UI declarative component rendering pattern) +- examples/integrations/mcp-apps/ (MCPAppsMiddleware, BuiltInAgent with MCP servers) + +## integrations/adk.md +- examples/integrations/adk/ (Google ADK agent with LlmAgent, ADKAgent wrapper, FastAPI server, Next.js route) + +## integrations/agno.md +- examples/integrations/agno/ (Agno agent with AgentOS, AGUI interface, FastAPI server, Next.js route) + +## integrations/crewai.md +- examples/integrations/crewai-flows/ (CrewAI Flows with CopilotKitState, copilotkit_stream, litellm, FastAPI server) +- examples/integrations/crewai-crews/ (CrewAI Crews with @CrewBase decorator, YAML config, multi-agent pipelines) + +## integrations/langgraph.md +- examples/integrations/langgraph-fastapi/ (LangGraph Python self-hosted with LangGraphAGUIAgent, FastAPI, LangGraphHttpAgent) +- examples/integrations/langgraph-python/ (LangGraph Platform with LangGraphAgent, deploymentUrl, graphId) +- examples/integrations/langgraph-js/ (LangGraph JS with CopilotKitStateAnnotation, convertActionsToDynamicStructuredTools, Turborepo monorepo) + +## integrations/llamaindex.md +- examples/integrations/llamaindex/ (LlamaIndex with get_ag_ui_workflow_router, frontend_tools/backend_tools, LlamaIndexAgent) + +## integrations/mastra.md +- examples/integrations/mastra/ (Mastra TypeScript agent with @mastra/core, MastraAgent.getLocalAgents, in-process agents, LibSQLStore memory) + +## integrations/ms-agent-framework.md +- examples/integrations/microsoft-agent-framework/ (Python: ChatAgent with @ai_function, AgentFrameworkAgent, predict_state_config) +- examples/integrations/microsoft-agent-framework-dotnet/ (.NET: AddAGUI, MapAGUI, SharedStateAgent, AIFunctionFactory) + +## integrations/pydantic-ai.md +- examples/integrations/pydantic-ai/ (PydanticAI with StateDeps, agent.to_ag_ui(), StateSnapshotEvent for state sync) + +## integrations/strands.md +- examples/integrations/strands/ (AWS Strands with StrandsAgent, StrandsAgentConfig, ToolBehavior, state_from_args, create_strands_app) diff --git a/skills/copilotkit-self-update/SKILL.md b/skills/copilotkit-self-update/SKILL.md new file mode 100644 index 00000000000..f457ff9da6d --- /dev/null +++ b/skills/copilotkit-self-update/SKILL.md @@ -0,0 +1,28 @@ +--- +name: copilotkit-self-update +description: Use when the user wants to update, refresh, or reinstall the CopilotKit agent SKILLS (the SKILL.md files that teach this agent about CopilotKit). NOT for updating the CopilotKit codebase or project — this is specifically about refreshing the skills/knowledge this agent has loaded. Triggers on "update copilotkit skills", "update skills", "refresh skills", "skills are stale", "skills are outdated", "get latest skills", "my copilotkit knowledge is wrong", "copilotkit APIs changed", "skills seem old", "wrong API names", "reinstall skills", "skills not working right", "update your copilotkit knowledge". +version: 1.0.0 +user_invocable: true +argument_hint: "" +--- + +# Update CopilotKit Skills + +Run this command to pull the latest CopilotKit skills from GitHub: + +```bash +npx skills add copilotkit/CopilotKit --full-depth -y +``` + +This does a fresh clone every time — it always gets the latest version regardless of what's cached. + +This works across all tools — Claude Code, Codex, Cursor, Gemini CLI, and others. It detects which tools are installed and updates skills for each. + +After the command completes, **start a new session** in your tool to pick up the changes. + +## When to Suggest This + +- User says the skills have wrong API names or outdated information +- User reports that a CopilotKit API doesn't match what the skill says +- User explicitly asks to update or refresh skills +- A new CopilotKit version was released and skills may be stale diff --git a/skills/copilotkit-setup/SKILL.md b/skills/copilotkit-setup/SKILL.md new file mode 100644 index 00000000000..e3e4ad7bb14 --- /dev/null +++ b/skills/copilotkit-setup/SKILL.md @@ -0,0 +1,395 @@ +--- +name: copilotkit-setup +description: > + Use when adding CopilotKit to an existing project or bootstrapping a new CopilotKit + project from scratch. Covers framework detection, package installation, runtime wiring, + provider setup, and first working chat integration. +version: 1.0.0 +--- + +# CopilotKit Setup + +## Prerequisites + +### Live Documentation (MCP) + +This plugin includes an MCP server (`copilotkit-docs`) that provides `search-docs` and `search-code` tools for querying live CopilotKit documentation and source code. + +- **Claude Code:** Auto-configured by the plugin's `.mcp.json` -- no setup needed. +- **Codex:** Requires manual configuration. See the [copilotkit-debug skill](../copilotkit-debug/SKILL.md#mcp-setup) for setup instructions. + +### Environment + +Before starting setup, verify: + +1. **Node.js >= 18** (required for `fetch` globals used by the runtime) +2. **An AI provider API key** (one of: `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GOOGLE_API_KEY`) +3. **A React-based frontend** (Next.js App Router, Next.js Pages Router, Vite + React, or Angular) +4. **A backend capable of running the runtime** (same Next.js app via API routes, or a standalone Express/Hono server) + +## Framework Detection + +Before generating any code, detect the project's framework by checking files in the project root. See `references/framework-detection.md` for the full decision tree. + +**Quick summary:** + +| Signal File | Framework | +|---|---| +| `next.config.{js,ts,mjs}` + `app/` directory | Next.js App Router | +| `next.config.{js,ts,mjs}` + `pages/` directory | Next.js Pages Router | +| `angular.json` | Angular | +| `vite.config.{js,ts}` + React deps in package.json | Vite + React | + +## Setup Workflow + +### Step 1: Install packages + +All packages use the `@copilotkit` namespace. + +**Frontend (React) packages:** +```bash +npm install @copilotkit/react @copilotkit/core +``` + +**Runtime packages (backend):** +```bash +npm install @copilotkit/runtime @copilotkit/agent +``` + +If the runtime runs in the same Next.js app as the frontend, install all four packages together. + +For standalone Express backends, also install Express adapter dependencies: +```bash +npm install express cors +npm install -D @types/express @types/cors +``` + +### Step 2: Configure the runtime + +The runtime is the server-side component that manages agent execution. See `references/runtime-architecture.md` for details. + +There are two endpoint styles: + +1. **Multi-route (Hono)** -- uses `createCopilotEndpoint`. Requires a catch-all route (`[[...slug]]` in Next.js). Each operation (run, connect, stop, info, transcribe, threads) gets its own HTTP path. +2. **Single-route (Hono or Express)** -- uses `createCopilotEndpointSingleRoute` or `createCopilotEndpointSingleRouteExpress`. All operations go through a single POST endpoint with method multiplexing. + +#### Next.js App Router (recommended: multi-route with Hono) + +Create `src/app/api/copilotkit/[[...slug]]/route.ts`: + +```typescript +import { + CopilotRuntime, + createCopilotEndpoint, + InMemoryAgentRunner, +} from "@copilotkit/runtime"; +import { BuiltInAgent } from "@copilotkit/agent"; +import { handle } from "hono/vercel"; + +const agent = new BuiltInAgent({ + model: "openai/gpt-4o", + prompt: "You are a helpful AI assistant.", +}); + +const runtime = new CopilotRuntime({ + agents: { + default: agent, + }, + runner: new InMemoryAgentRunner(), +}); + +const app = createCopilotEndpoint({ + runtime, + basePath: "/api/copilotkit", +}); + +export const GET = handle(app); +export const POST = handle(app); +``` + +This requires `hono` as a dependency: +```bash +npm install hono +``` + +#### Next.js App Router (alternative: single-route) + +Create `src/app/api/copilotkit/route.ts`: + +```typescript +import { + CopilotRuntime, + createCopilotEndpointSingleRoute, + InMemoryAgentRunner, +} from "@copilotkit/runtime"; +import { BuiltInAgent } from "@copilotkit/agent"; +import { handle } from "hono/vercel"; + +const agent = new BuiltInAgent({ + model: "openai/gpt-4o", + prompt: "You are a helpful AI assistant.", +}); + +const runtime = new CopilotRuntime({ + agents: { + default: agent, + }, + runner: new InMemoryAgentRunner(), +}); + +const app = createCopilotEndpointSingleRoute({ + runtime, + basePath: "/api/copilotkit", +}); + +export const POST = handle(app); +``` + +When using single-route, the frontend must set `useSingleEndpoint` on the provider (see Step 3). + +#### Standalone Express Server + +Create `src/index.ts`: + +```typescript +import express from "express"; +import { CopilotRuntime } from "@copilotkit/runtime"; +import { createCopilotEndpointSingleRouteExpress } from "@copilotkit/runtime/express"; +import { BuiltInAgent, defineTool } from "@copilotkit/agent"; +import { z } from "zod"; + +const agent = new BuiltInAgent({ + model: "openai/gpt-4o", +}); + +const runtime = new CopilotRuntime({ + agents: { + default: agent, + }, +}); + +const app = express(); + +app.use( + "/api/copilotkit", + createCopilotEndpointSingleRouteExpress({ + runtime, + basePath: "/", + }), +); + +const port = Number(process.env.PORT ?? 4000); +app.listen(port, () => { + console.log(`CopilotKit runtime listening at http://localhost:${port}/api/copilotkit`); +}); +``` + +For multi-route Express, use `createCopilotEndpointExpress` instead (imported from `@copilotkit/runtime/express`). + +#### Standalone Hono Server (non-Vercel) + +```typescript +import { CopilotRuntime, createCopilotEndpoint } from "@copilotkit/runtime"; +import { BuiltInAgent } from "@copilotkit/agent"; +import { serve } from "@hono/node-server"; + +const runtime = new CopilotRuntime({ + agents: { + default: new BuiltInAgent({ model: "openai/gpt-4o" }), + }, +}); + +const app = createCopilotEndpoint({ + runtime, + basePath: "/api/copilotkit", +}); + +serve({ fetch: app.fetch, port: 8787 }); +``` + +Requires `@hono/node-server`: +```bash +npm install hono @hono/node-server +``` + +### Step 3: Set up the frontend provider + +Wrap your application with `CopilotKitProvider` from `@copilotkit/react`. + +**Important:** Import the stylesheet in your root layout: +```typescript +import "@copilotkit/react/styles.css"; +``` + +#### Next.js App Router + +In `src/app/page.tsx` (or a client component): + +```tsx +"use client"; + +import { CopilotKitProvider, CopilotChat } from "@copilotkit/react"; + +export default function Home() { + return ( + +
+ +
+
+ ); +} +``` + +#### Connecting to an external runtime + +When the runtime runs on a separate server (e.g., Express on port 4000): + +```tsx + + {children} + +``` + +Set `useSingleEndpoint` when the backend uses single-route endpoints (`createCopilotEndpointSingleRoute` or `createCopilotEndpointSingleRouteExpress`). + +#### CopilotKitProvider key props + +| Prop | Type | Description | +|---|---|---| +| `runtimeUrl` | `string` | URL of the CopilotKit runtime endpoint | +| `useSingleEndpoint` | `boolean` | Set to `true` when using single-route endpoints | +| `headers` | `Record` | Custom headers sent with every request | +| `credentials` | `RequestCredentials` | Fetch credentials mode (e.g., `"include"` for cookies) | +| `publicApiKey` | `string` | Copilot Cloud public API key (if using hosted runtime) | +| `showDevConsole` | `boolean \| "auto"` | Show the dev inspector (`"auto"` = development only) | +| `renderToolCalls` | `ReactToolCallRenderer[]` | Custom renderers for tool call UI | +| `frontendTools` | `ReactFrontendTool[]` | Frontend-defined tools (declarative alternative to `useFrontendTool`) | +| `onError` | `(event) => void` | Global error handler | + +### Step 4: Add a chat UI component + +CopilotKit provides three pre-built chat layouts: + +| Component | Usage | +|---|---| +| `CopilotChat` | Inline chat, fills its container | +| `CopilotSidebar` | Collapsible sidebar panel | +| `CopilotPopup` | Floating popup widget | + +Example with sidebar: +```tsx + + + + +``` + +### Step 5: Set environment variables + +Create a `.env.local` (Next.js) or `.env` file: + +``` +OPENAI_API_KEY=sk-... +``` + +The `BuiltInAgent` automatically resolves API keys from environment variables based on the model prefix: +- `openai/*` models read `OPENAI_API_KEY` +- `anthropic/*` models read `ANTHROPIC_API_KEY` +- `google/*` models read `GOOGLE_API_KEY` + +You can also pass `apiKey` directly to `BuiltInAgent` if needed. + +### Step 6: Connect to the cloud platform (telemetry) + +CopilotKit uses telemetry to understand adoption, improve the product, and provide better support. Connecting to the CopilotKit cloud platform gives you access to analytics and optional premium features. + +1. Ask the user if they'd like to connect to the CopilotKit cloud platform (default: yes). +2. If yes, run the CopilotKit CLI authentication flow (verify the exact command with `npx copilotkit --help` as it may vary by version): + ```bash + npx copilotkit auth + ``` +3. Guide the user through the browser-based authentication that opens. +4. Once authentication completes, the CLI outputs a license key (format: `ck_...`). +5. Add the license key to the `CopilotKitProvider`: + ```tsx + + ``` + Alternatively, store it as an environment variable (`COPILOTKIT_LICENSE_KEY` in `.env.local` or `.env`) and reference it: + ```tsx + + ``` + +See `references/telemetry-setup.md` for full details on what the license key enables and how to opt out. + +### Step 7: Verify the setup + +1. Start the dev server +2. Open the app in a browser +3. The chat UI should render and connect to the runtime +4. Send a test message -- you should receive an AI response +5. Check the runtime's `/info` endpoint (GET) to confirm it reports available agents + +## Quick Reference + +### Package map + +| Package | Purpose | +|---|---| +| `@copilotkit/react` | React components, hooks, provider | +| `@copilotkit/core` | Core types, agent abstraction, state management | +| `@copilotkit/runtime` | Server-side runtime, endpoint factories, agent runners | +| `@copilotkit/agent` | `BuiltInAgent`, `defineTool`, model resolution | +| `@copilotkit/shared` | Shared utilities, logger, types | + +### Endpoint factory functions + +| Function | Import | Protocol | Framework | +|---|---|---|---| +| `createCopilotEndpoint` | `@copilotkit/runtime` | Multi-route (Hono) | Next.js App Router, Hono standalone | +| `createCopilotEndpointSingleRoute` | `@copilotkit/runtime` | Single-route (Hono) | Next.js App Router | +| `createCopilotEndpointExpress` | `@copilotkit/runtime/express` | Multi-route (Express) | Express standalone | +| `createCopilotEndpointSingleRouteExpress` | `@copilotkit/runtime/express` | Single-route (Express) | Express standalone | + +### Runtime classes + +| Class | Use case | +|---|---| +| `CopilotRuntime` | Compatibility shim; auto-selects SSE or Intelligence mode | +| `CopilotSseRuntime` | Explicit SSE mode (default, in-memory threads) | +| `CopilotIntelligenceRuntime` | Intelligence mode (durable threads, realtime events) | + +### Agent runners + +| Runner | Description | +|---|---| +| `InMemoryAgentRunner` | Default. Stores thread state in process memory. Suitable for development and single-instance deployments. | +| `IntelligenceAgentRunner` | Used automatically with `CopilotIntelligenceRuntime`. Connects to CopilotKit Intelligence Platform via WebSocket. | + +### Supported models (BuiltInAgent) + +Format: `"provider/model-name"` string or a Vercel AI SDK `LanguageModel` instance. + +**OpenAI:** `openai/gpt-5`, `openai/gpt-5-mini`, `openai/gpt-4.1`, `openai/gpt-4.1-mini`, `openai/gpt-4.1-nano`, `openai/gpt-4o`, `openai/gpt-4o-mini`, `openai/o3`, `openai/o3-mini`, `openai/o4-mini` + +**Anthropic:** `anthropic/claude-sonnet-4.5`, `anthropic/claude-sonnet-4`, `anthropic/claude-3.7-sonnet`, `anthropic/claude-opus-4.1`, `anthropic/claude-opus-4`, `anthropic/claude-3.5-haiku` + +**Google:** `google/gemini-2.5-pro`, `google/gemini-2.5-flash`, `google/gemini-2.5-flash-lite` + +Any `string` is accepted (for custom/unlisted models); the provider is parsed from the prefix before `/`. diff --git a/skills/copilotkit-setup/assets/express-runtime.ts b/skills/copilotkit-setup/assets/express-runtime.ts new file mode 100644 index 00000000000..090b348c32d --- /dev/null +++ b/skills/copilotkit-setup/assets/express-runtime.ts @@ -0,0 +1,66 @@ +// File: src/index.ts +// Standalone Express server with CopilotKit runtime (single-route) +// +// Prerequisites: +// npm install @copilotkit/runtime @copilotkit/agent express dotenv zod +// npm install -D @types/express tsx typescript +// +// Environment variables: +// OPENAI_API_KEY=sk-... (or ANTHROPIC_API_KEY / GOOGLE_API_KEY) +// PORT=4000 (optional, defaults to 4000) +// +// Run: +// npx tsx watch src/index.ts + +import express from "express"; +import dotenv from "dotenv"; +import { z } from "zod"; +import { CopilotRuntime } from "@copilotkit/runtime"; +import { createCopilotEndpointSingleRouteExpress } from "@copilotkit/runtime/express"; +import { BuiltInAgent, defineTool } from "@copilotkit/agent"; +import type { ToolDefinition } from "@copilotkit/agent"; + +dotenv.config(); + +// Example server-side tool +const weatherTool = defineTool({ + name: "getWeather", + description: "Get the current weather for a city", + parameters: z.object({ + city: z.string().describe("The city name"), + }), + execute: async ({ city }) => { + // Replace with real weather API call + return { city, temperature: 72, condition: "sunny" }; + }, +}) as unknown as ToolDefinition; + +const agent = new BuiltInAgent({ + model: "openai/gpt-4o", + prompt: "You are a helpful AI assistant.", + tools: [weatherTool], +}); + +const runtime = new CopilotRuntime({ + agents: { + default: agent, + }, +}); + +const app = express(); + +app.use( + "/api/copilotkit", + createCopilotEndpointSingleRouteExpress({ + runtime, + basePath: "/", + }), +); + +const port = Number(process.env.PORT ?? 4000); + +app.listen(port, () => { + console.log( + `CopilotKit runtime listening at http://localhost:${port}/api/copilotkit`, + ); +}); diff --git a/skills/copilotkit-setup/assets/nextjs-app-router-page.tsx b/skills/copilotkit-setup/assets/nextjs-app-router-page.tsx new file mode 100644 index 00000000000..183d0f3d5b7 --- /dev/null +++ b/skills/copilotkit-setup/assets/nextjs-app-router-page.tsx @@ -0,0 +1,22 @@ +// File: src/app/page.tsx +// Next.js App Router frontend with CopilotKit provider and chat UI +// +// Prerequisites: +// npm install @copilotkit/react @copilotkit/core +// +// Also add to layout.tsx: +// import "@copilotkit/react/styles.css"; + +"use client"; + +import { CopilotKitProvider, CopilotChat } from "@copilotkit/react"; + +export default function Home() { + return ( + +
+ +
+
+ ); +} diff --git a/skills/copilotkit-setup/assets/nextjs-app-router-route.ts b/skills/copilotkit-setup/assets/nextjs-app-router-route.ts new file mode 100644 index 00000000000..f3efa52d6f9 --- /dev/null +++ b/skills/copilotkit-setup/assets/nextjs-app-router-route.ts @@ -0,0 +1,36 @@ +// File: src/app/api/copilotkit/[[...slug]]/route.ts +// Next.js App Router + Hono multi-route endpoint +// +// Prerequisites: +// npm install @copilotkit/runtime @copilotkit/agent hono +// +// Environment variables: +// OPENAI_API_KEY=sk-... (or ANTHROPIC_API_KEY / GOOGLE_API_KEY) + +import { + CopilotRuntime, + createCopilotEndpoint, + InMemoryAgentRunner, +} from "@copilotkit/runtime"; +import { BuiltInAgent } from "@copilotkit/agent"; +import { handle } from "hono/vercel"; + +const agent = new BuiltInAgent({ + model: "openai/gpt-4o", + prompt: "You are a helpful AI assistant.", +}); + +const runtime = new CopilotRuntime({ + agents: { + default: agent, + }, + runner: new InMemoryAgentRunner(), +}); + +const app = createCopilotEndpoint({ + runtime, + basePath: "/api/copilotkit", +}); + +export const GET = handle(app); +export const POST = handle(app); diff --git a/skills/copilotkit-setup/eval.yaml b/skills/copilotkit-setup/eval.yaml new file mode 100644 index 00000000000..d36e3b01224 --- /dev/null +++ b/skills/copilotkit-setup/eval.yaml @@ -0,0 +1,191 @@ +version: "1" + +defaults: + agent: claude + provider: docker + trials: 3 + timeout: 300 + threshold: 0.8 + docker: + base: node:20-slim + setup: | + apt-get update && apt-get install -y git jq + +tasks: + - name: nextjs-app-router-setup + instruction: | + Create a new Next.js App Router project and add CopilotKit with a basic chat + interface using BuiltInAgent. The project should have: + - CopilotKit frontend packages (@copilotkit/react, @copilotkit/core) + - CopilotKit runtime packages (@copilotkit/runtime, @copilotkit/agent) + - A runtime API route at src/app/api/copilotkit/[[...slug]]/route.ts using + createCopilotEndpoint with a BuiltInAgent + - A page component with CopilotKitProvider wrapping CopilotChat + - The @copilotkit/react stylesheet imported + graders: + - type: deterministic + run: | + cd /workspace + PROJECT_DIR=$(find . -maxdepth 1 -type d ! -name '.' | head -1) + if [ -z "$PROJECT_DIR" ]; then + echo '{"score": 0.0, "details": "No project directory found"}' + exit 0 + fi + cd "$PROJECT_DIR" + + CHECKS='[]' + add_check() { + CHECKS=$(echo "$CHECKS" | jq --arg name "$1" --argjson passed "$2" --arg msg "$3" '. + [{"name": $name, "passed": $passed, "message": $msg}]') + } + + # Check frontend packages + if jq -e '.dependencies["@copilotkit/react"]' package.json > /dev/null 2>&1; then + add_check "@copilotkit/react installed" true "Found in dependencies" + else + add_check "@copilotkit/react installed" false "Not found in package.json dependencies" + fi + + if jq -e '.dependencies["@copilotkit/core"]' package.json > /dev/null 2>&1; then + add_check "@copilotkit/core installed" true "Found in dependencies" + else + add_check "@copilotkit/core installed" false "Not found in package.json dependencies" + fi + + # Check runtime packages + if jq -e '.dependencies["@copilotkit/runtime"]' package.json > /dev/null 2>&1; then + add_check "@copilotkit/runtime installed" true "Found in dependencies" + else + add_check "@copilotkit/runtime installed" false "Not found in package.json dependencies" + fi + + if jq -e '.dependencies["@copilotkit/agent"]' package.json > /dev/null 2>&1; then + add_check "@copilotkit/agent installed" true "Found in dependencies" + else + add_check "@copilotkit/agent installed" false "Not found in package.json dependencies" + fi + + # Check runtime route exists + ROUTE_FILE=$(find . -path '*/api/copilotkit/*/route.ts' -o -path '*/api/copilotkit/*/route.js' 2>/dev/null | head -1) + if [ -n "$ROUTE_FILE" ]; then + add_check "Runtime route file exists" true "Found at $ROUTE_FILE" + else + add_check "Runtime route file exists" false "No copilotkit API route found" + fi + + # Check for CopilotKitProvider usage + if grep -r "CopilotKitProvider" --include='*.tsx' --include='*.ts' --include='*.jsx' . > /dev/null 2>&1; then + add_check "CopilotKitProvider used" true "Found in source files" + else + add_check "CopilotKitProvider used" false "CopilotKitProvider not found in any source file" + fi + + # Check for CopilotChat usage + if grep -r "CopilotChat\|CopilotSidebar\|CopilotPopup" --include='*.tsx' --include='*.ts' --include='*.jsx' . > /dev/null 2>&1; then + add_check "Chat component used" true "Found chat component in source files" + else + add_check "Chat component used" false "No CopilotChat/CopilotSidebar/CopilotPopup found" + fi + + PASSED=$(echo "$CHECKS" | jq '[.[] | select(.passed == true)] | length') + TOTAL=$(echo "$CHECKS" | jq 'length') + SCORE=$(echo "scale=2; $PASSED / $TOTAL" | bc) + + echo "{\"score\": $SCORE, \"details\": \"$PASSED/$TOTAL checks passed\", \"checks\": $CHECKS}" + weight: 0.7 + - type: llm_rubric + rubric: | + Evaluate whether this project has a complete, working CopilotKit setup: + 1. Does it have a CopilotKitProvider wrapping a CopilotChat (or CopilotSidebar/CopilotPopup) component? + 2. Does the runtime route use createCopilotEndpoint or createCopilotEndpointSingleRoute with a BuiltInAgent? + 3. Is the @copilotkit/react stylesheet imported (styles.css)? + 4. Does the provider's runtimeUrl point to the correct API route? + 5. Is the project structured correctly for Next.js App Router (app/ directory, "use client" directives where needed)? + weight: 0.3 + + - name: vite-react-setup + instruction: | + Add CopilotKit to this existing Vite+React project with a chat sidebar and a + BuiltInAgent backend. The setup should include: + - CopilotKit frontend packages (@copilotkit/react, @copilotkit/core) + - CopilotKit runtime packages (@copilotkit/runtime, @copilotkit/agent) + - A backend server (Express or Hono) running the CopilotRuntime with a BuiltInAgent + - CopilotKitProvider in the React app pointing to the backend URL + - A CopilotSidebar component for the chat UI + - The @copilotkit/react stylesheet imported + workspace: + - src: workspace/vite-react + dest: /workspace + graders: + - type: deterministic + run: | + cd /workspace + + CHECKS='[]' + add_check() { + CHECKS=$(echo "$CHECKS" | jq --arg name "$1" --argjson passed "$2" --arg msg "$3" '. + [{"name": $name, "passed": $passed, "message": $msg}]') + } + + # Check frontend packages + if jq -e '.dependencies["@copilotkit/react"]' package.json > /dev/null 2>&1; then + add_check "@copilotkit/react installed" true "Found in dependencies" + else + add_check "@copilotkit/react installed" false "Not found in package.json dependencies" + fi + + # Check runtime packages (may be in a separate server directory) + RUNTIME_FOUND=false + for PKG_FILE in $(find . -name 'package.json' -not -path '*/node_modules/*' 2>/dev/null); do + if jq -e '.dependencies["@copilotkit/runtime"]' "$PKG_FILE" > /dev/null 2>&1; then + RUNTIME_FOUND=true + break + fi + done + if [ "$RUNTIME_FOUND" = true ]; then + add_check "@copilotkit/runtime installed" true "Found in dependencies" + else + add_check "@copilotkit/runtime installed" false "Not found in any package.json" + fi + + # Check for CopilotKitProvider + if grep -r "CopilotKitProvider" --include='*.tsx' --include='*.ts' --include='*.jsx' . > /dev/null 2>&1; then + add_check "CopilotKitProvider used" true "Found in source files" + else + add_check "CopilotKitProvider used" false "CopilotKitProvider not found" + fi + + # Check for CopilotSidebar or other chat component + if grep -r "CopilotSidebar\|CopilotChat\|CopilotPopup" --include='*.tsx' --include='*.ts' --include='*.jsx' . > /dev/null 2>&1; then + add_check "Chat UI component used" true "Found sidebar/chat/popup component" + else + add_check "Chat UI component used" false "No chat UI component found" + fi + + # Check for BuiltInAgent in backend + if grep -r "BuiltInAgent" --include='*.ts' --include='*.js' . > /dev/null 2>&1; then + add_check "BuiltInAgent configured" true "Found in backend source" + else + add_check "BuiltInAgent configured" false "BuiltInAgent not found in any source file" + fi + + # Check for stylesheet import + if grep -r "styles\.css\|@copilotkit/react/styles" --include='*.tsx' --include='*.ts' --include='*.jsx' --include='*.css' . > /dev/null 2>&1; then + add_check "Stylesheet imported" true "Found styles.css import" + else + add_check "Stylesheet imported" false "No @copilotkit/react/styles.css import found" + fi + + PASSED=$(echo "$CHECKS" | jq '[.[] | select(.passed == true)] | length') + TOTAL=$(echo "$CHECKS" | jq 'length') + SCORE=$(echo "scale=2; $PASSED / $TOTAL" | bc) + + echo "{\"score\": $SCORE, \"details\": \"$PASSED/$TOTAL checks passed\", \"checks\": $CHECKS}" + weight: 0.7 + - type: llm_rubric + rubric: | + Evaluate whether CopilotKit is properly integrated into this Vite+React project: + 1. Is CopilotKitProvider set up with a runtimeUrl pointing to the backend server? + 2. Is there a CopilotSidebar (or equivalent chat component) rendered in the app? + 3. Does the backend use CopilotRuntime with a BuiltInAgent and an appropriate endpoint factory? + 4. Is the @copilotkit/react stylesheet imported? + 5. If the backend is a separate server, does the frontend URL account for the correct port and CORS is handled? + weight: 0.3 diff --git a/skills/copilotkit-setup/references/framework-detection.md b/skills/copilotkit-setup/references/framework-detection.md new file mode 100644 index 00000000000..5e0004c8e4d --- /dev/null +++ b/skills/copilotkit-setup/references/framework-detection.md @@ -0,0 +1,101 @@ +# Framework Detection + +Detect the project's framework before generating any setup code. The detection order matters -- check more specific signals first. + +## Detection Decision Tree + +``` +1. Does `angular.json` exist? + YES -> Angular + NO -> continue + +2. Does `next.config.{js,ts,mjs}` exist? + YES -> Next.js (go to step 3) + NO -> continue to step 4 + +3. Does an `app/` directory exist at the project root or under `src/`? + YES -> Next.js App Router + NO -> Does a `pages/` directory exist at the project root or under `src/`? + YES -> Next.js Pages Router + NO -> Next.js App Router (assume App Router for new projects) + +4. Does `vite.config.{js,ts}` exist AND does `package.json` list `react` as a dependency? + YES -> Vite + React + NO -> Unknown / standalone backend only +``` + +## What Differs Per Framework + +### Next.js App Router + +- **Runtime location:** `src/app/api/copilotkit/[[...slug]]/route.ts` (multi-route) or `src/app/api/copilotkit/route.ts` (single-route) +- **Provider placement:** In a `"use client"` page or layout component +- **Route handler style:** Named exports `GET` and `POST` using `handle(app)` from `hono/vercel` +- **Stylesheet import:** In `layout.tsx`: `import "@copilotkit/react/styles.css"` +- **Env file:** `.env.local` +- **Extra deps:** `hono` (for Hono adapter) + +### Next.js Pages Router + +- **Runtime location:** Typically runs as a separate Express server (not in API routes). The Pages Router examples in the CopilotKit repo use an external Express runtime. +- **Provider placement:** In `pages/_app.tsx` or a page component. Must be a client component (default in Pages Router). +- **Frontend connects to external URL:** `runtimeUrl` points to the Express server (e.g., `http://localhost:4000/api/copilotkit`) +- **Stylesheet import:** In `pages/_app.tsx` or `styles/globals.css`: `import "@copilotkit/react/styles.css"` +- **Env file:** `.env.local` +- **Key prop:** `useSingleEndpoint` must be set on the provider when using single-route Express endpoints + +### Angular + +- **Runtime location:** Separate backend server (Express or Hono standalone) +- **Provider placement:** Uses Angular-specific components from `@copilotkit/angular` (separate package) +- **Not React-based:** Does NOT use `CopilotKitProvider` or React hooks +- **Stylesheet import:** Via Angular styles configuration in `angular.json` +- **Package:** `@copilotkit/angular` instead of `@copilotkit/react` + +### Vite + React + +- **Runtime location:** Separate backend server (Express or Hono standalone). Vite dev server only serves the frontend. +- **Provider placement:** In the root `App.tsx` component +- **Frontend connects to external URL:** `runtimeUrl` points to the backend server +- **Stylesheet import:** In `main.tsx` or `App.tsx`: `import "@copilotkit/react/styles.css"` +- **Env file:** `.env` (Vite exposes vars prefixed with `VITE_`) +- **Note:** API keys should NOT be prefixed with `VITE_` -- they belong on the backend server, not exposed to the browser + +### Standalone Backend (Express) + +- **No framework detection needed** -- this is a backend-only setup +- **Uses:** `@copilotkit/runtime` and `@copilotkit/agent` +- **Does NOT need:** `@copilotkit/react` or `@copilotkit/core` +- **Endpoint factories:** `createCopilotEndpointExpress` (multi-route) or `createCopilotEndpointSingleRouteExpress` (single-route), both from `@copilotkit/runtime/express` +- **Env file:** `.env` (loaded via `dotenv`) + +### Standalone Backend (Hono) + +- **Same as Express** but uses `createCopilotEndpoint` or `createCopilotEndpointSingleRoute` from `@copilotkit/runtime` +- **Served via:** `@hono/node-server` with `serve({ fetch: app.fetch, port })` + +## File-Level Detection Commands + +When implementing framework detection in a skill, check these files: + +```bash +# Next.js +ls next.config.{js,ts,mjs} 2>/dev/null + +# App Router vs Pages Router +ls -d app src/app 2>/dev/null # App Router +ls -d pages src/pages 2>/dev/null # Pages Router + +# Angular +ls angular.json 2>/dev/null + +# Vite + React +ls vite.config.{js,ts} 2>/dev/null +grep -q '"react"' package.json 2>/dev/null + +# Package manager +ls pnpm-lock.yaml 2>/dev/null && echo "pnpm" +ls yarn.lock 2>/dev/null && echo "yarn" +ls package-lock.json 2>/dev/null && echo "npm" +ls bun.lockb 2>/dev/null && echo "bun" +``` diff --git a/skills/copilotkit-setup/references/runtime-architecture.md b/skills/copilotkit-setup/references/runtime-architecture.md new file mode 100644 index 00000000000..cbc7dc35717 --- /dev/null +++ b/skills/copilotkit-setup/references/runtime-architecture.md @@ -0,0 +1,237 @@ +# Runtime Architecture + +The CopilotKit v2 runtime (`@copilotkit/runtime`) is the server-side component that manages agent execution, thread state, and communication with the frontend via the AG-UI protocol (SSE-based events). + +## Core Concepts + +### CopilotRuntime + +`CopilotRuntime` is the main entry point. It is a compatibility shim that delegates to either `CopilotSseRuntime` (default) or `CopilotIntelligenceRuntime` depending on configuration. + +```typescript +import { CopilotRuntime } from "@copilotkit/runtime"; + +// SSE mode (default) -- in-memory thread state +const runtime = new CopilotRuntime({ + agents: { default: myAgent }, + runner: new InMemoryAgentRunner(), // optional, this is the default +}); + +// Intelligence mode -- durable threads via CopilotKit Intelligence Platform +const runtime = new CopilotRuntime({ + agents: { default: myAgent }, + intelligence: new CopilotKitIntelligence({ ... }), + identifyUser: (request) => ({ id: "user-123" }), +}); +``` + +**Constructor options (`CopilotRuntimeOptions`):** + +| Option | Type | Description | +|---|---|---| +| `agents` | `Record` | Map of named agents. Must have at least one entry. | +| `runner` | `AgentRunner` | Agent execution strategy. Defaults to `InMemoryAgentRunner`. | +| `intelligence` | `CopilotKitIntelligence` | Enables Intelligence mode with durable threads. | +| `identifyUser` | `(request: Request) => CopilotRuntimeUser` | Required with Intelligence mode. Resolves authenticated user. | +| `generateThreadNames` | `boolean` | Auto-generate thread names (Intelligence mode only, default: `true`). | +| `transcriptionService` | `TranscriptionService` | Optional audio transcription (e.g., `TranscriptionServiceOpenAI`). | +| `beforeRequestMiddleware` | `BeforeRequestMiddleware` | Callback or webhook URL invoked before each request. | +| `afterRequestMiddleware` | `AfterRequestMiddleware` | Callback or webhook URL invoked after each request. | +| `a2ui` | `{ agents?: string[] } & A2UIMiddlewareConfig` | Auto-apply A2UI (Agent-to-UI) middleware to agents. | +| `mcpApps` | `{ servers: McpAppsServerConfig[] }` | Auto-apply MCP Apps middleware with MCP server configs. | + +### Agents + +Agents implement the `AbstractAgent` interface from `@ag-ui/client`. CopilotKit provides `BuiltInAgent` (from `@copilotkit/agent`) as a ready-to-use implementation backed by the Vercel AI SDK. + +```typescript +import { BuiltInAgent, defineTool } from "@copilotkit/agent"; +import { z } from "zod"; + +const agent = new BuiltInAgent({ + model: "openai/gpt-4o", // "provider/model" string or LanguageModel instance + prompt: "You are helpful.", // System prompt + temperature: 0.7, // Sampling temperature + maxSteps: 5, // Max tool-calling iterations (default: 1) + tools: [ // Server-side tools + defineTool({ + name: "getWeather", + description: "Get current weather for a city", + parameters: z.object({ + city: z.string(), + }), + execute: async ({ city }) => { + return { temp: 72, condition: "sunny" }; + }, + }), + ], +}); +``` + +`BasicAgent` is an alias for `BuiltInAgent` (same class, exported for convenience). + +**BuiltInAgent configuration:** + +| Option | Type | Description | +|---|---|---| +| `model` | `string \| LanguageModel` | Model identifier (e.g., `"openai/gpt-4o"`) or AI SDK LanguageModel | +| `apiKey` | `string` | Provider API key (falls back to env vars) | +| `prompt` | `string` | System prompt | +| `temperature` | `number` | Sampling temperature | +| `maxSteps` | `number` | Max tool-calling iterations (default: 1) | +| `maxOutputTokens` | `number` | Max tokens to generate | +| `toolChoice` | `ToolChoice` | How tools are selected (`"auto"`, `"required"`, `"none"`, or specific) | +| `tools` | `ToolDefinition[]` | Server-side tools available to the agent | +| `mcpServers` | `MCPClientConfig[]` | MCP server connections for dynamic tool discovery | +| `providerOptions` | `Record` | Provider-specific options (e.g., `{ openai: { reasoningEffort: "high" } }`) | +| `overridableProperties` | `OverridableProperty[]` | Properties the frontend can override via forwarded props | +| `forwardSystemMessages` | `boolean` | Forward system-role messages from input (default: `false`) | +| `forwardDeveloperMessages` | `boolean` | Forward developer-role messages as system messages (default: `false`) | + +### AgentRunner + +The `AgentRunner` abstract class controls how agent execution is managed. It has four methods: + +```typescript +abstract class AgentRunner { + abstract run(request: AgentRunnerRunRequest): Observable; + abstract connect(request: AgentRunnerConnectRequest): Observable; + abstract isRunning(request: AgentRunnerIsRunningRequest): Promise; + abstract stop(request: AgentRunnerStopRequest): Promise; +} +``` + +**Built-in runners:** + +- **`InMemoryAgentRunner`** -- Default. Stores thread state (events, runs) in process memory using a global `Map` keyed by thread ID. Survives hot reloads via `Symbol.for` on `globalThis`. Suitable for development and single-instance deployments. +- **`IntelligenceAgentRunner`** -- Used automatically when `CopilotIntelligenceRuntime` is configured. Connects to the Intelligence Platform via WebSocket for durable, distributed thread management. + +## Endpoint Factories + +Endpoint factories create HTTP handlers that expose the runtime's functionality. There are four variants across two HTTP frameworks (Hono, Express) and two routing styles (multi-route, single-route). + +### Multi-Route Endpoints + +Each operation gets its own HTTP path under the base path: + +| Method | Path | Handler | +|---|---|---| +| POST | `/agent/:agentId/run` | Start an agent run | +| POST | `/agent/:agentId/connect` | Connect to an existing thread | +| POST | `/agent/:agentId/stop/:threadId` | Stop a running agent | +| GET | `/info` | Runtime info (version, available agents) | +| POST | `/transcribe` | Audio transcription | +| GET | `/threads` | List threads (Intelligence mode) | +| POST | `/threads/subscribe` | Subscribe to thread updates | +| PATCH | `/threads/:threadId` | Update thread metadata | +| POST | `/threads/:threadId/archive` | Archive a thread | +| DELETE | `/threads/:threadId` | Delete a thread | + +**Hono (`createCopilotEndpoint`):** +```typescript +import { CopilotRuntime, createCopilotEndpoint } from "@copilotkit/runtime"; + +const app = createCopilotEndpoint({ + runtime, + basePath: "/api/copilotkit", + cors: { // optional CORS config + origin: "https://myapp.com", // string, string[], or function + credentials: true, // enable for HTTP-only cookies + }, +}); +``` + +**Express (`createCopilotEndpointExpress`):** +```typescript +import { createCopilotEndpointExpress } from "@copilotkit/runtime/express"; + +const router = createCopilotEndpointExpress({ + runtime, + basePath: "/api/copilotkit", +}); +app.use(router); +``` + +### Single-Route Endpoints + +All operations go through a single POST endpoint. The operation is identified by a `method` field in the JSON body. This is simpler to deploy (one route, no catch-all needed). + +**Hono (`createCopilotEndpointSingleRoute`):** +```typescript +import { CopilotRuntime, createCopilotEndpointSingleRoute } from "@copilotkit/runtime"; + +const app = createCopilotEndpointSingleRoute({ + runtime, + basePath: "/api/copilotkit", +}); +``` + +**Express (`createCopilotEndpointSingleRouteExpress`):** +```typescript +import { createCopilotEndpointSingleRouteExpress } from "@copilotkit/runtime/express"; + +const router = createCopilotEndpointSingleRouteExpress({ + runtime, + basePath: "/", // relative to where it's mounted +}); +app.use("/api/copilotkit", router); +``` + +### When to Use Which + +| Scenario | Recommended | +|---|---| +| Next.js App Router | Multi-route Hono (`createCopilotEndpoint`) via `[[...slug]]` catch-all | +| Next.js App Router (no catch-all desired) | Single-route Hono (`createCopilotEndpointSingleRoute`) | +| Standalone Express server | Single-route Express (`createCopilotEndpointSingleRouteExpress`) | +| Standalone Hono/Node server | Multi-route Hono (`createCopilotEndpoint`) | +| Need thread management (Intelligence mode) | Multi-route only (thread endpoints not available in single-route) | + +## Middleware + +The runtime supports before/after request middleware for cross-cutting concerns (auth, logging, rate limiting). + +```typescript +const runtime = new CopilotRuntime({ + agents: { default: agent }, + beforeRequestMiddleware: async ({ request, path }) => { + // Validate auth, return modified request or void + const token = request.headers.get("Authorization"); + if (!token) { + throw new Response(JSON.stringify({ error: "Unauthorized" }), { + status: 401, + }); + } + return request; // or return void to pass through unchanged + }, + afterRequestMiddleware: async ({ response, path, messages, threadId }) => { + // Log, audit, etc. Non-blocking (errors are caught and logged). + console.log(`Completed request to ${path}, thread: ${threadId}`); + }, +}); +``` + +`afterRequestMiddleware` receives reconstructed `messages` from the SSE stream and the `threadId`/`runId` extracted from the `RUN_STARTED` event. + +## CORS + +All endpoint factories enable CORS by default with `origin: "*"`. For production with credentials (cookies), configure explicit origins: + +**Hono endpoints:** +```typescript +createCopilotEndpoint({ + runtime, + basePath: "/api/copilotkit", + cors: { + origin: "https://myapp.com", + credentials: true, + }, +}); +``` + +**Express endpoints:** CORS is handled internally via the `cors` middleware with permissive defaults. Customize by wrapping the router or adding your own CORS middleware upstream. + +**Frontend side:** Set `credentials: "include"` on `CopilotKitProvider` to send cookies: +```tsx + +``` diff --git a/skills/copilotkit-setup/references/telemetry-setup.md b/skills/copilotkit-setup/references/telemetry-setup.md new file mode 100644 index 00000000000..411a98e8591 --- /dev/null +++ b/skills/copilotkit-setup/references/telemetry-setup.md @@ -0,0 +1,83 @@ +# CopilotCloud Telemetry Setup + +## What is CopilotCloud? + +CopilotCloud is CopilotKit's hosted platform that provides: + +- **Usage analytics** -- see how users interact with your AI features (message volume, tool usage, session duration) +- **Error monitoring** -- surface runtime errors and failed agent interactions +- **Premium features** -- access to hosted runtimes, advanced agent orchestration, and priority support (requires a paid plan) + +The license key is a lightweight identifier that connects your local CopilotKit instance to CopilotCloud. It does not gate any open-source functionality -- CopilotKit works fully without it. + +## The `npx copilotkit auth` flow + +Running the CLI command starts an interactive authentication (verify the exact command with `npx copilotkit --help` as it may vary by version): + +```bash +npx copilotkit auth +``` + +1. The CLI opens your default browser to the CopilotCloud login/signup page. +2. Sign in with GitHub, Google, or email. +3. Select or create a project in the CopilotCloud dashboard. +4. The CLI receives the license key and prints it to stdout: + ``` + Successfully authenticated! + Your license key: ck_abc123... + ``` + +If the browser does not open automatically, the CLI prints a URL you can copy-paste manually. + +## Where to put the license key + +### Option A: Inline in CopilotKitProvider + +Pass the key directly as a prop: + +```tsx + + {children} + +``` + +This is the simplest approach for quick prototyping but exposes the key in source code. + +### Option B: Environment variable (recommended) + +Add the key to your environment file: + +**Next.js** (`.env.local`): +``` +NEXT_PUBLIC_COPILOTKIT_LICENSE_KEY=ck_abc123... +``` + +**Vite** (`.env`): +``` +VITE_COPILOTKIT_LICENSE_KEY=ck_abc123... +``` + +Then reference it in the provider: + +```tsx +// Next.js + + +// Vite + +``` + +The `NEXT_PUBLIC_` or `VITE_` prefix is required because the license key is used on the client side. It is safe to expose -- the key is a project identifier, not a secret. + +## Opting out + +To disconnect from CopilotCloud, simply remove the `licenseKey` prop from `CopilotKitProvider` (and delete the environment variable if you set one). No other changes are needed -- CopilotKit will continue to function normally without it. diff --git a/skills/copilotkit-setup/sources.md b/skills/copilotkit-setup/sources.md new file mode 100644 index 00000000000..63e6ca3dc38 --- /dev/null +++ b/skills/copilotkit-setup/sources.md @@ -0,0 +1,32 @@ +# Sources + +Files and directories read from CopilotKit/CopilotKit to generate this skill's references. +Generated: 2026-03-28 + +## framework-detection.md +- examples/v2/ (Angular, React, Node, Node-Express, Next Pages Router directory structures) +- examples/integrations/ (integration example directory structures for framework patterns) +- packages/v2/runtime/src/ (endpoint factories: createCopilotEndpoint, createCopilotEndpointExpress, createCopilotEndpointSingleRoute) +- packages/v2/react/src/ (CopilotKitProvider props, stylesheet imports) +- packages/v2/angular/src/ (Angular component package structure) + +## runtime-architecture.md +- packages/v2/runtime/src/ (CopilotRuntime, CopilotRuntimeOptions, AgentRunner, InMemoryAgentRunner, IntelligenceAgentRunner) +- packages/v2/runtime/src/endpoints/ (createCopilotEndpoint, createCopilotEndpointExpress, createCopilotEndpointSingleRoute, createCopilotEndpointSingleRouteExpress, CORS config, route definitions) +- packages/v2/runtime/src/intelligence-platform/ (CopilotKitIntelligence, CopilotSseRuntime, CopilotIntelligenceRuntime) +- packages/v2/agent/src/ (BuiltInAgent, BasicAgent, defineTool, ToolDefinition, resolveModel, MCPClientConfig) +- packages/v2/shared/src/ (TranscriptionService, BeforeRequestMiddleware, AfterRequestMiddleware) + +## assets/express-runtime.ts +- packages/v2/runtime/src/ (CopilotRuntime constructor, createCopilotEndpointSingleRouteExpress) +- packages/v2/agent/src/ (BuiltInAgent, defineTool, ToolDefinition) +- examples/v2/node-express/ (Express server setup patterns) + +## assets/nextjs-app-router-route.ts +- packages/v2/runtime/src/ (CopilotRuntime, createCopilotEndpoint, InMemoryAgentRunner) +- packages/v2/agent/src/ (BuiltInAgent) +- examples/v2/react/ (Next.js App Router route handler patterns) + +## assets/nextjs-app-router-page.tsx +- packages/v2/react/src/ (CopilotKitProvider, CopilotChat component exports) +- examples/v2/react/ (Next.js App Router page component patterns) diff --git a/skills/copilotkit-upgrade/SKILL.md b/skills/copilotkit-upgrade/SKILL.md new file mode 100644 index 00000000000..61bd82ac94e --- /dev/null +++ b/skills/copilotkit-upgrade/SKILL.md @@ -0,0 +1,134 @@ +--- +name: copilotkit-upgrade +description: "Use when migrating a CopilotKit v1 application to v2 -- updating package imports, replacing deprecated hooks and components, switching from GraphQL runtime to AG-UI protocol runtime, and resolving breaking API changes." +version: 1.0.0 +--- + +# CopilotKit v1 to v2 Migration Skill + +## Live Documentation (MCP) + +This plugin includes an MCP server (`copilotkit-docs`) that provides `search-docs` and `search-code` tools for querying live CopilotKit documentation and source code. Useful for looking up current v2 API signatures during migration. + +- **Claude Code:** Auto-configured by the plugin's `.mcp.json` -- no setup needed. +- **Codex:** Requires manual configuration. See the [copilotkit-debug skill](../copilotkit-debug/SKILL.md#mcp-setup) for setup instructions. + +## Overview + +CopilotKit v2 is a ground-up rewrite built on the AG-UI protocol (`@ag-ui/client` / `@ag-ui/core`). Users continue to install and import `@copilotkit/*` packages -- the v2 changes are exposed through the same package names with updated APIs (new hook names, component names, runtime configuration). The `@copilotkit/*` namespace is an internal implementation detail that users never interact with. + +## Migration Workflow + +### 1. Audit Current Usage + +Scan the codebase for all v1 imports and API usage: + +``` +@copilotkit/react-core -> hooks, CopilotKit provider, types +@copilotkit/react-ui -> CopilotChat, CopilotPopup, CopilotSidebar +@copilotkit/react-textarea -> CopilotTextarea (removed in v2) +@copilotkit/runtime -> CopilotRuntime, service adapters, framework integrations +@copilotkit/runtime-client-gql -> GraphQL client, message types +@copilotkit/shared -> utility types, constants +@copilotkit/sdk-js -> LangGraph/LangChain SDK +``` + +### 2. Identify Deprecated APIs + +Key hooks and components to find and replace: + +| v1 API | v2 Replacement | +|--------|---------------| +| `useCopilotAction` | `useFrontendTool` | +| `useCopilotReadable` | `useAgentContext` | +| `useCopilotChat` | `useAgent` + `useSuggestions` | +| `useCoAgent` | `useAgent` | +| `useCoAgentStateRender` | `useRenderToolCall` / `useRenderActivityMessage` | +| `useLangGraphInterrupt` | `useInterrupt` | +| `useCopilotChatSuggestions` | `useConfigureSuggestions` + `useSuggestions` | +| `useCopilotAdditionalInstructions` | `useAgentContext` | +| `useMakeCopilotDocumentReadable` | `useAgentContext` | +| `CopilotKit` (provider) | `CopilotKitProvider` | +| `CopilotTextarea` | Removed -- use standard textarea + `useFrontendTool` | + +### 3. Map to v2 Equivalents + +Refer to `references/v1-to-v2-migration.md` for detailed before/after code examples. + +### 4. Update Package Dependencies + +The `@copilotkit/*` package names stay the same. Update to the latest v2 versions: + +``` +@copilotkit/react-core -> @copilotkit/react (consolidated into one package) +@copilotkit/react-ui -> @copilotkit/react (consolidated into one package) +@copilotkit/react-textarea -> removed (no v2 equivalent) +@copilotkit/runtime -> @copilotkit/runtime (same package, new agent-based API) +@copilotkit/runtime-client-gql -> removed (replaced by AG-UI protocol, re-exported from @copilotkit/react) +@copilotkit/shared -> @copilotkit/shared (same package) +@copilotkit/sdk-js -> @copilotkit/agent (new package for agent definitions) +``` + +### 5. Update Runtime Configuration + +The v1 `CopilotRuntime` accepted service adapters (OpenAI, Anthropic, LangChain, etc.) and endpoint definitions. The v2 `CopilotRuntime` accepts AG-UI `AbstractAgent` instances directly. + +**v1 pattern** (service adapter + endpoints): +```ts +import { CopilotRuntime, OpenAIAdapter } from "@copilotkit/runtime"; +const runtime = new CopilotRuntime({ actions: [...] }); +// used with copilotKitEndpoint() for Next.js, Express, etc. +``` + +**v2 pattern** (agents + Hono endpoint): +```ts +import { CopilotRuntime, createCopilotEndpoint } from "@copilotkit/runtime"; +import { BuiltInAgent } from "@copilotkit/agent"; +const runtime = new CopilotRuntime({ + agents: { myAgent: new BuiltInAgent({ model: "openai:gpt-4o" }) }, +}); +const app = createCopilotEndpoint({ runtime, basePath: "/api/copilotkit" }); +``` + +### 6. Update Provider + +**v1:** +```tsx +import { CopilotKit } from "@copilotkit/react-core"; + + {children} + +``` + +**v2:** +```tsx +import { CopilotKitProvider } from "@copilotkit/react"; + + {children} + +``` + +### 7. Verify + +- Run the application and check for runtime errors +- Verify all agent interactions work (chat, tool calls, interrupts) +- Check that tool renderers display correctly +- Confirm suggestions load and display + +## Quick Reference + +| Concept | v1 | v2 | +|---------|----|----| +| Package scope | `@copilotkit/*` | `@copilotkit/*` (same scope, updated APIs) | +| Protocol | GraphQL | AG-UI (SSE) | +| Provider component | `CopilotKit` | `CopilotKitProvider` | +| Define frontend tool | `useCopilotAction` | `useFrontendTool` | +| Share app state | `useCopilotReadable` | `useAgentContext` | +| Agent interaction | `useCoAgent` | `useAgent` | +| Handle interrupts | `useLangGraphInterrupt` | `useInterrupt` | +| Render tool calls | `useCopilotAction({ render })` | `useRenderToolCall` | +| Chat suggestions | `useCopilotChatSuggestions` | `useConfigureSuggestions` | +| Runtime class | `CopilotRuntime` (adapters) | `CopilotRuntime` (agents) | +| Endpoint setup | `copilotKitEndpoint()` | `createCopilotEndpoint()` | +| Agent definition | `LangGraphAgent` endpoint | `AbstractAgent` / `BuiltInAgent` | +| Chat components | `CopilotChat`, `CopilotPopup`, `CopilotSidebar` | `CopilotChat`, `CopilotPopup`, `CopilotSidebar` (from `@copilotkit/react`) | diff --git a/skills/copilotkit-upgrade/references/breaking-changes.md b/skills/copilotkit-upgrade/references/breaking-changes.md new file mode 100644 index 00000000000..c1d099d36a6 --- /dev/null +++ b/skills/copilotkit-upgrade/references/breaking-changes.md @@ -0,0 +1,336 @@ +# CopilotKit v2 Breaking Changes + +## Package Structure + +### Consolidated React packages + +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 component + +v2 consolidates everything into a single package: +- `@copilotkit/react` -- provider, hooks, types, chat components, AG-UI re-exports + +### New package scope + +The v2 API is exposed through the same `@copilotkit/*` packages. No package name changes are required when upgrading. + +### Removed packages + +| Package | Status | +|---------|--------| +| `@copilotkit/react-textarea` | Removed. No v2 equivalent. | +| `@copilotkit/runtime-client-gql` | Replaced by `@ag-ui/client` (re-exported from `@copilotkit/react`) | +| `@copilotkit/sdk-js` | Replaced by `@copilotkit/agent` | + +--- + +## Protocol Change: GraphQL to AG-UI + +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:** +- All GraphQL message types (`TextMessage`, `ActionExecutionMessage`, `ResultMessage`, etc.) are replaced by AG-UI event types (`TextMessageChunkEvent`, `ToolCallStartEvent`, `ToolCallArgsEvent`, `ToolCallEndEvent`, `ToolCallResultEvent`, etc.) +- The `MessageRole` enum is replaced by AG-UI message roles +- Custom GraphQL queries/mutations against the runtime are no longer possible +- The runtime no longer exposes a GraphQL endpoint + +--- + +## Provider Changes + +### Component rename + +`CopilotKit` is renamed to `CopilotKitProvider`. + +### Props changes + +| v1 Prop | v2 Status | Notes | +|---------|----------|-------| +| `runtimeUrl` | Kept | Same behavior | +| `headers` | Kept | Same behavior | +| `publicApiKey` | Kept | Same behavior (also `publicLicenseKey` alias) | +| `properties` | Kept | Same behavior | +| `agents` | Removed | Use `selfManagedAgents` or `agents__unsafe_dev_only` | +| `guardrails_c` | Removed | -- | +| `children` | Kept | Same behavior | +| -- | Added: `credentials` | `RequestCredentials` for fetch (e.g., `"include"` for cookies) | +| -- | Added: `selfManagedAgents` | `Record` 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 | + +### Context hook rename + +`useCopilotContext` is replaced by `useCopilotKit` which returns `{ copilotkit: CopilotKitCoreReact }`. + +--- + +## Hook Renames and API Changes + +### useCopilotAction -> useFrontendTool + +**Parameter definition change:** v1 used a custom parameter descriptor format. v2 uses Zod schemas. + +```ts +// 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:** + +```ts +// v1 +handler: ({ city, units }) => { ... } + +// v2 +handler: async (args) => { ... } // args is typed from the Zod schema +``` + +**Render props change:** + +```ts +// v1 render status: "inProgress" | "executing" | "complete" +// v1 uses `respond()` callback for interactive actions + +// v2 render status: ToolCallStatus.InProgress | ToolCallStatus.Executing | ToolCallStatus.Complete +// v2 render props: { name, args, status, result } +``` + +**Availability change:** +```ts +// v1 +disabled: true // or available: "disabled" + +// v2 +available: "disabled" | "enabled" +``` + +### useCopilotReadable -> useAgentContext + +**Breaking:** The `parentId` parameter for hierarchical context is removed. Flatten nested contexts. + +```ts +// v1 (hierarchical) +const parentId = useCopilotReadable({ description: "Parent", value: "..." }); +useCopilotReadable({ description: "Child", value: "...", parentId }); + +// v2 (flat) +useAgentContext({ description: "Parent - Child context", value: { parent: "...", child: "..." } }); +``` + +### useCoAgent -> useAgent + +**Breaking:** Completely different return type. + +```ts +// 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` -> removed +- `state` -> accessed through AG-UI `StateSnapshotEvent` / `StateDeltaEvent` + +### useLangGraphInterrupt -> useInterrupt + +**Breaking:** Different API shape. + +- `agentName` -> `agentId` +- `nodeName` -> removed (use `enabled` predicate to filter) +- `render` props change: receives `InterruptRenderProps` instead of v1's `{ event, resolve }` +- New `renderInChat` prop (default `true`) controls whether interrupt renders inside CopilotChat +- New `handler` prop for programmatic handling before rendering +- New `enabled` predicate prop for filtering interrupts + +### useCopilotChat -> removed + +Replaced by `useAgent` for agent interaction. The headless chat API (appendMessage, visibleMessages, etc.) is replaced by the AG-UI agent event stream. + +### useCopilotChatSuggestions -> useConfigureSuggestions + useSuggestions + +Split into two hooks: one for configuration, one for reading state. + +### useCoAgentStateRender -> useRenderToolCall / useRenderActivityMessage + +Split into two hooks based on the type of rendering needed. + +### useCopilotAdditionalInstructions -> useAgentContext + +Use `useAgentContext` with an appropriate description to provide instructions. + +### useMakeCopilotDocumentReadable -> useAgentContext + +Use `useAgentContext` to pass document content. The `DocumentPointer` type and category-based filtering are removed. + +--- + +## Runtime Breaking Changes + +### Service adapters 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-20250514" })` | +| `GoogleGenerativeAIAdapter` | Use `BuiltInAgent({ model: "google:gemini-pro" })` | +| `LangChainAdapter` | Use a custom `AbstractAgent` implementation | +| `GroqAdapter` | Use `BuiltInAgent` with Groq-compatible model string | +| `UnifyAdapter` | Use a custom `AbstractAgent` implementation | +| `OpenAIAssistantAdapter` | Use a custom `AbstractAgent` implementation | +| `BedrockAdapter` | Use `BuiltInAgent({ model: "vertex:..." })` or custom agent | +| `OllamaAdapter` | Use a custom `AbstractAgent` implementation | +| `EmptyAdapter` | Not needed | + +### Runtime constructor changes + +```ts +// v1 +new CopilotRuntime({ + actions: [...], // Removed + remoteEndpoints: [...], // Removed + remoteActions: [...], // Removed + onBeforeRequest: (options) => {}, // Deprecated + onAfterRequest: (options) => {}, // Deprecated +}) + +// v2 +new CopilotRuntime({ + agents: { ... }, // Required: Record + 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, +}) +``` + +### Framework integrations removed + +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` | `createCopilotEndpoint` (Hono, works with Next.js) | +| `copilotRuntimeNextJSPagesRouterEndpoint` | `createCopilotEndpoint` (Hono) | +| `CopilotRuntimeNodeExpressEndpoint` | `createCopilotEndpointExpress` | +| `CopilotRuntimeNestEndpoint` | Use Hono adapter or Express endpoint | +| `CopilotRuntimeNodeHttpEndpoint` | Use Hono or Express endpoint | + +### Endpoint configuration + +```ts +// v1 (Next.js App Router example) +import { copilotRuntimeNextJSAppRouterEndpoint } from "@copilotkit/runtime"; + +export const POST = copilotRuntimeNextJSAppRouterEndpoint({ + runtime, + serviceAdapter, + endpoint: "/api/copilotkit", +}); + +// v2 +import { createCopilotEndpoint } from "@copilotkit/runtime"; + +const app = createCopilotEndpoint({ + 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; +``` + +### LangGraph agent configuration + +```ts +// v1 (remote endpoint) +new CopilotRuntime({ + remoteEndpoints: [ + { + url: "http://localhost:8000/copilotkit", + type: "langgraph", + }, + ], +}) + +// v2 (direct agent instance) +import { LangGraphAgent } from "@ag-ui/langgraph"; + +new CopilotRuntime({ + agents: { + myAgent: new LangGraphAgent({ + url: "http://localhost:8000", + graphId: "my-graph", + }), + }, +}) +``` + +--- + +## Type System Changes + +### Parameter types + +v1 used a custom `Parameter` type for defining tool parameters: + +```ts +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`). + +### Message types + +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 | + +### Event types + +v2 introduces AG-UI event types for streaming: + +- `RunStartedEvent`, `RunFinishedEvent`, `RunErrorEvent` +- `TextMessageChunkEvent` +- `ToolCallStartEvent`, `ToolCallArgsEvent`, `ToolCallEndEvent`, `ToolCallResultEvent` +- `StateSnapshotEvent`, `StateDeltaEvent` +- `ReasoningStartEvent`, `ReasoningMessageStartEvent`, `ReasoningMessageContentEvent`, `ReasoningMessageEndEvent`, `ReasoningEndEvent` diff --git a/skills/copilotkit-upgrade/references/deprecation-map.md b/skills/copilotkit-upgrade/references/deprecation-map.md new file mode 100644 index 00000000000..44d840d371e --- /dev/null +++ b/skills/copilotkit-upgrade/references/deprecation-map.md @@ -0,0 +1,112 @@ +# CopilotKit v1 Deprecation Map + +Complete mapping of every deprecated v1 API to its v2 replacement. + +## Hooks + +| v1 Hook | v1 Package | v2 Replacement | v2 Package | Status | +|---------|-----------|---------------|-----------|--------| +| `useCopilotAction` | `@copilotkit/react-core` | `useFrontendTool` | `@copilotkit/react` | Renamed + new parameter format (Zod) | +| `useCopilotReadable` | `@copilotkit/react-core` | `useAgentContext` | `@copilotkit/react` | Renamed, `parentId` removed | +| `useCopilotChat` | `@copilotkit/react-core` | `useAgent` | `@copilotkit/react` | Replaced (different API) | +| `useCoAgent` | `@copilotkit/react-core` | `useAgent` | `@copilotkit/react` | Renamed, different return type | +| `useCoAgentStateRender` | `@copilotkit/react-core` | `useRenderToolCall` / `useRenderActivityMessage` | `@copilotkit/react` | Split into two hooks | +| `useLangGraphInterrupt` | `@copilotkit/react-core` | `useInterrupt` | `@copilotkit/react` | Renamed + new API | +| `useCopilotChatSuggestions` | `@copilotkit/react-core` | `useConfigureSuggestions` + `useSuggestions` | `@copilotkit/react` | Split into two hooks | +| `useCopilotAdditionalInstructions` | `@copilotkit/react-core` | `useAgentContext` | `@copilotkit/react` | Use description/value context | +| `useMakeCopilotDocumentReadable` | `@copilotkit/react-core` | `useAgentContext` | `@copilotkit/react` | Pass content directly | +| `useCopilotRuntimeClient` | `@copilotkit/react-core` | `useCopilotKit` | `@copilotkit/react` | Access core via provider context | +| `useCopilotContext` | `@copilotkit/react-core` | `useCopilotKit` | `@copilotkit/react` | Returns `{ copilotkit }` | +| `useCopilotMessagesContext` | `@copilotkit/react-core` | -- | -- | Removed (use agent event stream) | +| `useCoAgentStateRenders` | `@copilotkit/react-core` | -- | -- | Removed (context no longer needed) | +| `useCopilotChatInternal` | `@copilotkit/react-core` | -- | -- | Internal, removed | +| `useCopilotChatHeadless_c` | `@copilotkit/react-core` | -- | -- | Internal, removed | +| `useCopilotAuthenticatedAction_c` | `@copilotkit/react-core` | -- | -- | Internal, removed | +| `useFrontendTool` | `@copilotkit/react-core` | `useFrontendTool` | `@copilotkit/react` | Same name, import path changes | +| `useHumanInTheLoop` | `@copilotkit/react-core` | `useHumanInTheLoop` | `@copilotkit/react` | Same name, import path changes | +| `useRenderToolCall` | `@copilotkit/react-core` | `useRenderToolCall` | `@copilotkit/react` | Same name, import path changes | +| `useDefaultTool` | `@copilotkit/react-core` | `useDefaultRenderTool` | `@copilotkit/react` | Renamed | +| `useLazyToolRenderer` | `@copilotkit/react-core` | -- | -- | Removed | +| `useChatContext` (react-ui) | `@copilotkit/react-ui` | `useCopilotChatConfiguration` | `@copilotkit/react` | Renamed | + +## Components + +| v1 Component | v1 Package | v2 Replacement | v2 Package | Status | +|-------------|-----------|---------------|-----------|--------| +| `CopilotKit` | `@copilotkit/react-core` | `CopilotKitProvider` | `@copilotkit/react` | Renamed | +| `CopilotChat` | `@copilotkit/react-ui` | `CopilotChat` | `@copilotkit/react` | Same name, new package | +| `CopilotPopup` | `@copilotkit/react-ui` | `CopilotPopup` | `@copilotkit/react` | Same name, new package | +| `CopilotSidebar` | `@copilotkit/react-ui` | `CopilotSidebar` | `@copilotkit/react` | Same name, new package | +| `CopilotTextarea` | `@copilotkit/react-textarea` | -- | -- | **Removed** | +| `CopilotDevConsole` | `@copilotkit/react-ui` | `CopilotKitInspector` | `@copilotkit/react` | Renamed | +| `Markdown` | `@copilotkit/react-ui` | -- | -- | Removed (use A2UI renderer) | +| `AssistantMessage` | `@copilotkit/react-ui` | `CopilotChatAssistantMessage` | `@copilotkit/react` | Renamed | +| `UserMessage` | `@copilotkit/react-ui` | `CopilotChatUserMessage` | `@copilotkit/react` | Renamed | +| `ImageRenderer` | `@copilotkit/react-ui` | -- | -- | Removed | +| `RenderSuggestionsList` | `@copilotkit/react-ui` | `CopilotChatSuggestionView` | `@copilotkit/react` | Renamed | +| `RenderSuggestion` | `@copilotkit/react-ui` | `CopilotChatSuggestionPill` | `@copilotkit/react` | Renamed | +| `CoAgentStateRendersProvider` | `@copilotkit/react-core` | -- | -- | Removed (no v2 equivalent) | +| `ThreadsProvider` | `@copilotkit/react-core` | -- | -- | Removed (threads managed by runtime) | + +## Runtime Classes + +| v1 Class/Function | v1 Package | v2 Replacement | v2 Package | Status | +|------------------|-----------|---------------|-----------|--------| +| `CopilotRuntime` | `@copilotkit/runtime` | `CopilotRuntime` | `@copilotkit/runtime` | Same name, different constructor API | +| `OpenAIAdapter` | `@copilotkit/runtime` | `BuiltInAgent({ model: "openai:..." })` | `@copilotkit/agent` | **Removed** | +| `AnthropicAdapter` | `@copilotkit/runtime` | `BuiltInAgent({ model: "anthropic:..." })` | `@copilotkit/agent` | **Removed** | +| `GoogleGenerativeAIAdapter` | `@copilotkit/runtime` | `BuiltInAgent({ model: "google:..." })` | `@copilotkit/agent` | **Removed** | +| `LangChainAdapter` | `@copilotkit/runtime` | Custom `AbstractAgent` | -- | **Removed** | +| `GroqAdapter` | `@copilotkit/runtime` | `BuiltInAgent` with Groq model | `@copilotkit/agent` | **Removed** | +| `UnifyAdapter` | `@copilotkit/runtime` | Custom `AbstractAgent` | -- | **Removed** | +| `OpenAIAssistantAdapter` | `@copilotkit/runtime` | Custom `AbstractAgent` | -- | **Removed** | +| `BedrockAdapter` | `@copilotkit/runtime` | `BuiltInAgent({ model: "vertex:..." })` | `@copilotkit/agent` | **Removed** | +| `OllamaAdapter` (experimental) | `@copilotkit/runtime` | Custom `AbstractAgent` | -- | **Removed** | +| `EmptyAdapter` | `@copilotkit/runtime` | -- | -- | **Removed** | +| `RemoteChain` | `@copilotkit/runtime` | -- | -- | **Removed** | +| `LangGraphAgent` | `@copilotkit/runtime` | `LangGraphAgent` | `@ag-ui/langgraph` | Moved to AG-UI package | +| `LangGraphHttpAgent` | `@copilotkit/runtime` | `LangGraphAgent` | `@ag-ui/langgraph` | Moved + renamed | + +## Runtime Framework Integrations + +| v1 Function | v1 Package | v2 Replacement | v2 Package | Status | +|------------|-----------|---------------|-----------|--------| +| `copilotRuntimeNextJSAppRouterEndpoint` | `@copilotkit/runtime` | `createCopilotEndpoint` | `@copilotkit/runtime` | **Removed** (use Hono) | +| `copilotRuntimeNextJSPagesRouterEndpoint` | `@copilotkit/runtime` | `createCopilotEndpoint` | `@copilotkit/runtime` | **Removed** (use Hono) | +| `CopilotRuntimeNodeExpressEndpoint` | `@copilotkit/runtime` | `createCopilotEndpointExpress` | `@copilotkit/runtime` | Renamed | +| `CopilotRuntimeNestEndpoint` | `@copilotkit/runtime` | `createCopilotEndpoint` | `@copilotkit/runtime` | **Removed** (use Hono) | +| `CopilotRuntimeNodeHttpEndpoint` | `@copilotkit/runtime` | `createCopilotEndpoint` | `@copilotkit/runtime` | **Removed** (use Hono) | + +## Types + +| v1 Type | v1 Package | v2 Replacement | v2 Package | Status | +|---------|-----------|---------------|-----------|--------| +| `CopilotKitProps` | `@copilotkit/react-core` | `CopilotKitProviderProps` | `@copilotkit/react` | Renamed | +| `CopilotContextParams` | `@copilotkit/react-core` | `CopilotKitContextValue` | `@copilotkit/react` | Renamed | +| `FrontendAction` | `@copilotkit/react-core` | `ReactFrontendTool` | `@copilotkit/react` | Renamed + restructured | +| `ActionRenderProps` | `@copilotkit/react-core` | `ReactToolCallRenderer` | `@copilotkit/react` | Renamed + restructured | +| `DocumentPointer` | `@copilotkit/react-core` | -- | -- | **Removed** | +| `SystemMessageFunction` | `@copilotkit/react-core` | -- | -- | **Removed** | +| `CopilotChatSuggestionConfiguration` | `@copilotkit/react-core` | `Suggestion` | `@copilotkit/core` | Renamed | +| `Parameter` | `@copilotkit/shared` | Zod schemas / `StandardSchemaV1` | `zod` / `@copilotkit/shared` | Replaced with schema-based | +| `CopilotServiceAdapter` | `@copilotkit/runtime` | `AbstractAgent` | `@ag-ui/client` | Replaced | +| `TextMessageEvents` | `@copilotkit/runtime` | -- | -- | **Removed** (@deprecated) | +| `ToolCallEvents` | `@copilotkit/runtime` | -- | -- | **Removed** (@deprecated) | +| `CustomEventNames` | `@copilotkit/runtime` | -- | -- | **Removed** (@deprecated) | +| `PredictStateTool` | `@copilotkit/runtime` | -- | -- | **Removed** (@deprecated) | + +## v1 Props Marked @deprecated Within v1 + +These were already deprecated within v1 itself: + +| Location | Deprecated API | Replacement | +|----------|---------------|-------------| +| `FrontendAction` | `disabled` | `available: "disabled"` | +| `ActionRenderProps` | `respond()` | Use `respond` (same, just documented differently) | +| `CopilotKitProps` | `guardrails_c` | Removed in v2 | +| `CopilotRuntime` | `onBeforeRequest` / `onAfterRequest` | `beforeRequestMiddleware` / `afterRequestMiddleware` | +| `useCopilotChat` | `visibleMessages` | Use AG-UI message stream | +| `useCopilotChat` | `appendMessage` | Use `sendMessage` or agent API | +| Chat component props | `AssistantMessage` / `UserMessage` / `Messages` render props | `RenderMessage` | +| `useA2UIStore` | `useA2UIStore` | `useA2UIContext` | +| `useA2UIStoreSelector` | `useA2UIStoreSelector` | `useA2UIContext` | diff --git a/skills/copilotkit-upgrade/references/v1-to-v2-migration.md b/skills/copilotkit-upgrade/references/v1-to-v2-migration.md new file mode 100644 index 00000000000..56a4d505a5d --- /dev/null +++ b/skills/copilotkit-upgrade/references/v1-to-v2-migration.md @@ -0,0 +1,553 @@ +# CopilotKit v1 to v2 Migration Guide + +## Package Migration + +### Step 1: Replace Dependencies + +Remove v1 packages and install v2 equivalents: + +```bash +# 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.) | + +### Step 2: Update All Imports + +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 +``` + +--- + +## Provider Migration + +### v1: `CopilotKit` + +```tsx +import { CopilotKit } from "@copilotkit/react-core"; + +function App() { + return ( + + + + ); +} +``` + +### v2: `CopilotKitProvider` + +```tsx +import { CopilotKitProvider } from "@copilotkit/react"; + +function App() { + return ( + + + + ); +} +``` + +**Key differences:** +- Component renamed from `CopilotKit` to `CopilotKitProvider` +- Props type renamed from `CopilotKitProps` to `CopilotKitProviderProps` +- v2 adds `credentials`, `selfManagedAgents`, `renderToolCalls`, `renderActivityMessages` props +- v2 removes `agents` prop (use `selfManagedAgents` or `agents__unsafe_dev_only` for local dev) + +--- + +## Hook Migrations + +### useCopilotAction -> useFrontendTool + +**v1:** +```tsx +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 }) => ( +
Adding: {args.title} (status: {status})
+ ), +}); +``` + +**v2:** +```tsx +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 }) => ( +
Adding: {args.title} (status: {status})
+ ), +}); +``` + +**Key differences:** +- Hook renamed from `useCopilotAction` to `useFrontendTool` +- Parameters use Zod schemas instead of the v1 parameter descriptor array (`{ name, type, required }`) +- The `handler` receives the full args object directly (not destructured from `{ arg1, arg2 }`) +- The `render` prop works similarly but uses `ToolCallStatus` enum (`"inProgress"`, `"executing"`, `"complete"`) +- v2 adds `available` prop (`"enabled"` | `"disabled"`) replacing the v1 `disabled` boolean +- v2 adds `agentId` prop to scope a tool to a specific agent + +### useCopilotReadable -> useAgentContext + +**v1:** +```tsx +import { useCopilotReadable } from "@copilotkit/react-core"; + +function EmployeeList({ employees }) { + useCopilotReadable({ + description: "The list of employees", + value: employees, + }); + + return
...
; +} +``` + +**v2:** +```tsx +import { useAgentContext } from "@copilotkit/react"; + +function EmployeeList({ employees }) { + useAgentContext({ + description: "The list of employees", + value: employees, + }); + + return
...
; +} +``` + +**Key differences:** +- Hook renamed from `useCopilotReadable` to `useAgentContext` +- The `parentId` hierarchical context feature from v1 is not available in v2; flatten your context instead +- The `value` prop accepts `JsonSerializable` (string, number, boolean, null, arrays, objects) -- objects are auto-serialized to JSON strings + +### useMakeCopilotDocumentReadable -> useAgentContext + +**v1:** +```tsx +import { useMakeCopilotDocumentReadable } from "@copilotkit/react-core"; + +useMakeCopilotDocumentReadable(documentPointer, ["category1"]); +``` + +**v2:** +```tsx +import { useAgentContext } from "@copilotkit/react"; + +useAgentContext({ + description: "Document content for category1", + value: documentContent, +}); +``` + +**Key differences:** +- No direct `DocumentPointer` equivalent in v2; pass document content directly via `useAgentContext` +- Categories are not supported; use the `description` field to provide context + +### useCoAgent -> useAgent + +**v1:** +```tsx +import { useCoAgent } from "@copilotkit/react-core"; + +type AgentState = { count: number }; + +const { name, state, setState, running, start, stop, run } = useCoAgent({ + name: "my-agent", + initialState: { count: 0 }, +}); +``` + +**v2:** +```tsx +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:** +- Hook renamed from `useCoAgent` to `useAgent` +- `name` prop renamed to `agentId` +- v2 does not have `initialState` / `setState` -- agent state is managed through AG-UI protocol events (`StateSnapshotEvent`, `StateDeltaEvent`) +- v2 returns an `AbstractAgent` instance instead of a destructured state object +- The `run` / `start` / `stop` API surface differs -- v2 uses AG-UI protocol methods + +### useCoAgentStateRender -> useRenderToolCall / useRenderActivityMessage + +**v1:** +```tsx +import { useCoAgentStateRender } from "@copilotkit/react-core"; + +useCoAgentStateRender({ + name: "basic_agent", + nodeName: "search_node", + render: ({ status, state, nodeName }) => ( + + ), +}); +``` + +**v2:** +```tsx +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) +- v2 does not have the `nodeName` filtering -- tool rendering is keyed by tool call ID +- v2 uses AG-UI `ToolCall` and `ToolMessage` types instead of the v1 agent state shape + +### useLangGraphInterrupt -> useInterrupt + +**v1:** +```tsx +import { useLangGraphInterrupt } from "@copilotkit/react-core"; + +useLangGraphInterrupt({ + name: "confirm-action", + nodeName: "confirmation_node", + agentName: "my-agent", + render: ({ event, resolve }) => ( + resolve("confirmed")} + onCancel={() => resolve("cancelled")} + /> + ), +}); +``` + +**v2:** +```tsx +import { useInterrupt } from "@copilotkit/react"; + +const interruptElement = useInterrupt({ + renderInChat: false, // false = you render it yourself; true = renders in CopilotChat + render: ({ event, resolve }) => ( + 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
{interruptElement}
; +``` + +**Key differences:** +- Hook renamed from `useLangGraphInterrupt` to `useInterrupt` +- `agentName` renamed to `agentId` +- `nodeName` filtering removed; use the `enabled` predicate instead +- v2 adds `renderInChat` prop (default `true`) -- when true, interrupt UI renders inside `` automatically +- v2 adds optional `handler` for programmatic interrupt handling before rendering +- v2 returns a `React.ReactElement | null` when `renderInChat: false` + +### useCopilotChat -> useAgent + useSuggestions + +**v1:** +```tsx +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:** +```tsx +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 interaction +- Message types change from `TextMessage`/`MessageRole` (GraphQL) to AG-UI event types +- For headless chat, use the `AbstractAgent` API directly + +### useCopilotChatSuggestions -> useConfigureSuggestions + useSuggestions + +**v1:** +```tsx +import { useCopilotChatSuggestions } from "@copilotkit/react-core"; + +useCopilotChatSuggestions({ + instructions: "Suggest helpful actions based on the current page", + maxSuggestions: 3, +}); +``` + +**v2:** +```tsx +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:** +- Split into two hooks: `useConfigureSuggestions` (write config) and `useSuggestions` (read state) +- `useSuggestions` returns `{ suggestions, reloadSuggestions, clearSuggestions, isLoading }` + +### useCopilotAdditionalInstructions -> useAgentContext + +**v1:** +```tsx +import { useCopilotAdditionalInstructions } from "@copilotkit/react-core"; + +useCopilotAdditionalInstructions({ + instructions: "Do not answer questions about the weather.", +}); +``` + +**v2:** +```tsx +import { useAgentContext } from "@copilotkit/react"; + +useAgentContext({ + description: "Additional instructions for the agent", + value: "Do not answer questions about the weather.", +}); +``` + +### useHumanInTheLoop (same name, different import) + +**v1:** +```tsx +import { useHumanInTheLoop } from "@copilotkit/react-core"; +``` + +**v2:** +```tsx +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. + +--- + +## Runtime Migration + +### v1: Service Adapter Pattern + +```ts +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); +``` + +### v2: AG-UI Agent Pattern + +```ts +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:** +- No more service adapters (`OpenAIAdapter`, `LangChainAdapter`, etc.) -- model selection is done inside agents +- No more `actions` array on the runtime -- frontend tools are registered via `useFrontendTool`, backend tools via agent configuration +- No more `remoteEndpoints` -- agents are passed directly as `AbstractAgent` instances +- Endpoint setup uses `createCopilotEndpoint` (Hono) or `createCopilotEndpointExpress` (Express) instead of framework-specific integrations +- v2 runtime supports SSE mode and Intelligence mode (durable threads with realtime events) + +### v2 Runtime Modes + +```ts +// 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 Component Migration + +Chat components have the same names but move to `@copilotkit/react`: + +**v1:** +```tsx +import { CopilotChat, CopilotPopup, CopilotSidebar } from "@copilotkit/react-ui"; +``` + +**v2:** +```tsx +import { CopilotChat, CopilotPopup, CopilotSidebar } from "@copilotkit/react"; +``` + +v2 adds new chat sub-components for granular customization: +- `CopilotChatView` -- the main chat view +- `CopilotChatInput` -- input area +- `CopilotChatAssistantMessage` / `CopilotChatUserMessage` -- message components +- `CopilotChatReasoningMessage` -- reasoning/thinking display +- `CopilotChatToolCallsView` -- tool call rendering +- `CopilotChatSuggestionView` / `CopilotChatSuggestionPill` -- suggestion UI +- `CopilotChatToggleButton` -- toggle button for popup/sidebar +- `CopilotSidebarView` / `CopilotPopupView` -- layout containers +- `CopilotModalHeader` -- header for modal layouts + +--- + +## CopilotTextarea Migration + +`CopilotTextarea` from `@copilotkit/react-textarea` has no v2 equivalent. If you were using it for AI-assisted text input, replace it with: + +1. A standard `