Skip to main content
CopilotKit Core (@copilotkitnext/core) is a framework-agnostic JavaScript library that powers the React and Angular integrations. You can use it directly in vanilla JavaScript applications, Node.js scripts, or any JavaScript environment.

Installation

npm install @copilotkitnext/core @ag-ui/client zod
# or
pnpm add @copilotkitnext/core @ag-ui/client zod
# or
yarn add @copilotkitnext/core @ag-ui/client zod

Core Concepts

CopilotKitCore

The CopilotKitCore class is the main entry point. It manages agents, tools, context, and communication with your CopilotKit runtime.
import { CopilotKitCore } from "@copilotkitnext/core";

const copilotkit = new CopilotKitCore({
  runtimeUrl: "http://localhost:3001/api/copilotkit",
  headers: {
    "Authorization": "Bearer your-token",
  },
  credentials: "include",
  properties: {
    userId: "user-123",
  },
});

Configuration Options

interface CopilotKitCoreConfig {
  // Runtime configuration
  runtimeUrl?: string;
  runtimeTransport?: "rest" | "single";
  
  // Authentication & headers
  headers?: Record<string, string>;
  credentials?: RequestCredentials;
  
  // Context & properties
  properties?: Record<string, unknown>;
  
  // Local agents (development only)
  agents__unsafe_dev_only?: Record<string, AbstractAgent>;
  
  // Tools
  tools?: FrontendTool[];
  
  // Suggestions
  suggestionsConfig?: SuggestionsConfig[];
}

Working with Agents

Connecting to an Agent

import { CopilotKitCore } from "@copilotkitnext/core";

const core = new CopilotKitCore({
  runtimeUrl: "http://localhost:3001/api/copilotkit",
});

// Get or create agent
const agent = core.getAgent("my-agent");

if (!agent) {
  console.error("Agent not found");
}

// Subscribe to agent events
const subscription = agent.subscribe({
  onMessagesChanged: (messages) => {
    console.log("Messages updated:", messages);
    renderMessages(messages);
  },
  onStateChanged: (state) => {
    console.log("State changed:", state);
  },
  onRunInitialized: () => {
    console.log("Agent started running");
  },
  onRunFinalized: () => {
    console.log("Agent finished running");
  },
  onRunFailed: (error) => {
    console.error("Agent run failed:", error);
  },
});

// Don't forget to unsubscribe when done
// subscription.unsubscribe();

Running an Agent

async function sendMessage(content) {
  const agent = core.getAgent("my-agent");
  
  try {
    await agent.run({
      messages: [
        { role: "user", content }
      ],
    });
  } catch (error) {
    console.error("Failed to run agent:", error);
  }
}

// Usage
sendMessage("Hello, how can you help me?");

Connecting to a Thread

async function connectToThread(threadId) {
  const agent = core.getAgent("my-agent");
  
  try {
    await agent.connect({
      threadId,
      // Optionally start with initial messages
      messages: [],
    });
    
    console.log("Connected to thread:", threadId);
  } catch (error) {
    console.error("Failed to connect:", error);
  }
}

// Usage
connectToThread("thread-123");

Frontend Tools

Frontend tools execute in the browser and can interact with your UI, local storage, or any browser API.

Registering Tools

import { z } from "zod";

// Simple tool
core.addTool({
  name: "getCurrentTime",
  description: "Get the current time",
  parameters: z.object({}),
  handler: async () => {
    return new Date().toLocaleTimeString();
  },
});

// Tool with parameters
core.addTool({
  name: "showNotification",
  description: "Show a browser notification to the user",
  parameters: z.object({
    title: z.string().describe("Notification title"),
    message: z.string().describe("Notification message"),
  }),
  handler: async ({ title, message }) => {
    if (!("Notification" in window)) {
      throw new Error("Notifications not supported");
    }
    
    const permission = await Notification.requestPermission();
    if (permission === "granted") {
      new Notification(title, { body: message });
      return `Notification shown: ${title}`;
    } else {
      throw new Error("Notification permission denied");
    }
  },
});

// Tool with state access
core.addTool({
  name: "updateCounter",
  description: "Update the counter value",
  parameters: z.object({
    increment: z.number().describe("Amount to increment by"),
  }),
  handler: async ({ increment }) => {
    const currentValue = parseInt(
      document.getElementById("counter").textContent
    );
    const newValue = currentValue + increment;
    document.getElementById("counter").textContent = newValue;
    return `Counter updated to ${newValue}`;
  },
});

Removing Tools

// Remove a specific tool
core.removeTool("showNotification");

// Remove tool for specific agent
core.removeTool("showNotification", "my-agent");

Context Management

Provide dynamic context to your agents:
// Add context
core.addContext("user-profile", {
  description: "Current user profile information",
  value: {
    id: "user-123",
    name: "John Doe",
    preferences: {
      theme: "dark",
      language: "en",
    },
  },
});

// Update context
core.updateContext("user-profile", {
  description: "Current user profile information",
  value: {
    id: "user-123",
    name: "John Doe",
    preferences: {
      theme: "light", // Changed
      language: "en",
    },
  },
});

// Remove context
core.removeContext("user-profile");

Event Subscription

Subscribe to core events for reactive updates:
const subscription = core.subscribe({
  // Runtime connection status
  onRuntimeConnectionStatusChanged: ({ status }) => {
    console.log("Runtime status:", status);
    updateConnectionIndicator(status);
  },
  
  // Tool execution events
  onToolExecutionStart: ({ toolName, args }) => {
    console.log(`Tool ${toolName} started with:`, args);
    showToolExecutionUI(toolName, "running");
  },
  
  onToolExecutionEnd: ({ toolName, result, error }) => {
    if (error) {
      console.error(`Tool ${toolName} failed:`, error);
      showToolExecutionUI(toolName, "error");
    } else {
      console.log(`Tool ${toolName} completed:`, result);
      showToolExecutionUI(toolName, "completed");
    }
  },
  
  // Agent changes
  onAgentsChanged: ({ agents }) => {
    console.log("Available agents:", Object.keys(agents));
    updateAgentsList(agents);
  },
  
  // Context changes
  onContextChanged: ({ context }) => {
    console.log("Context updated:", context);
  },
  
  // Errors
  onError: ({ error, code, context }) => {
    console.error(`Error [${code}]:`, error);
    console.error("Context:", context);
    showErrorNotification(error.message);
  },
});

// Clean up subscription when done
subscription.unsubscribe();

Suggestions

Configure AI-generated suggestions:
// Add suggestions config
core.addSuggestionsConfig("my-suggestions", {
  instructions: "Suggest helpful next actions based on the current context",
  available: "always",
  agentId: "my-agent",
});

// Get suggestions
const { suggestions, isLoading } = core.getSuggestions("my-agent");

console.log("Current suggestions:", suggestions);
console.log("Loading:", isLoading);

// Subscribe to suggestion changes
core.subscribe({
  onSuggestionsChanged: ({ agentId, suggestions }) => {
    console.log(`Suggestions for ${agentId}:`, suggestions);
    renderSuggestions(suggestions);
  },
  
  onSuggestionsStartedLoading: ({ agentId }) => {
    console.log(`Loading suggestions for ${agentId}`);
    showSuggestionsLoader();
  },
  
  onSuggestionsFinishedLoading: ({ agentId }) => {
    console.log(`Finished loading suggestions for ${agentId}`);
    hideSuggestionsLoader();
  },
});

Complete Example: Chat Application

Here’s a complete example of a vanilla JavaScript chat application:
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>CopilotKit Chat</title>
  <style>
    body {
      font-family: system-ui, -apple-system, sans-serif;
      max-width: 800px;
      margin: 0 auto;
      padding: 20px;
    }
    
    #messages {
      border: 1px solid #ddd;
      border-radius: 8px;
      padding: 20px;
      min-height: 400px;
      max-height: 400px;
      overflow-y: auto;
      margin-bottom: 20px;
    }
    
    .message {
      margin-bottom: 15px;
      padding: 10px;
      border-radius: 6px;
    }
    
    .user {
      background-color: #e3f2fd;
      text-align: right;
    }
    
    .assistant {
      background-color: #f5f5f5;
    }
    
    #input-container {
      display: flex;
      gap: 10px;
    }
    
    #message-input {
      flex: 1;
      padding: 10px;
      border: 1px solid #ddd;
      border-radius: 6px;
      font-size: 16px;
    }
    
    #send-button {
      padding: 10px 20px;
      background-color: #2196f3;
      color: white;
      border: none;
      border-radius: 6px;
      cursor: pointer;
      font-size: 16px;
    }
    
    #send-button:hover {
      background-color: #1976d2;
    }
    
    #send-button:disabled {
      background-color: #ccc;
      cursor: not-allowed;
    }
    
    #status {
      margin-bottom: 10px;
      padding: 10px;
      border-radius: 6px;
      font-size: 14px;
    }
    
    .status-connected {
      background-color: #c8e6c9;
      color: #2e7d32;
    }
    
    .status-connecting {
      background-color: #fff9c4;
      color: #f57f17;
    }
    
    .status-error {
      background-color: #ffcdd2;
      color: #c62828;
    }
  </style>
</head>
<body>
  <h1>CopilotKit Chat Demo</h1>
  
  <div id="status" class="status-connecting">Connecting...</div>
  
  <div id="messages"></div>
  
  <div id="input-container">
    <input
      type="text"
      id="message-input"
      placeholder="Type a message..."
      disabled
    />
    <button id="send-button" disabled>Send</button>
  </div>

  <script type="module">
    import { CopilotKitCore } from "@copilotkitnext/core";
    import { z } from "zod";

    const messagesEl = document.getElementById("messages");
    const inputEl = document.getElementById("message-input");
    const sendButtonEl = document.getElementById("send-button");
    const statusEl = document.getElementById("status");

    // Initialize CopilotKit
    const core = new CopilotKitCore({
      runtimeUrl: "http://localhost:3001/api/copilotkit",
    });

    let agent = null;
    let isRunning = false;

    // Subscribe to runtime status
    core.subscribe({
      onRuntimeConnectionStatusChanged: ({ status }) => {
        updateStatus(status);
      },
      
      onError: ({ error, code }) => {
        console.error(`Error [${code}]:`, error);
        statusEl.textContent = `Error: ${error.message}`;
        statusEl.className = "status-error";
      },
    });

    // Register frontend tools
    core.addTool({
      name: "getCurrentTime",
      description: "Get the current time",
      parameters: z.object({}),
      handler: async () => {
        return new Date().toLocaleString();
      },
    });

    core.addTool({
      name: "alertUser",
      description: "Show an alert to the user",
      parameters: z.object({
        message: z.string().describe("The message to show"),
      }),
      handler: async ({ message }) => {
        alert(message);
        return "Alert shown";
      },
    });

    // Initialize agent
    async function initializeAgent() {
      agent = core.getAgent("default");
      
      if (!agent) {
        console.error("Agent not found");
        return;
      }

      // Subscribe to agent updates
      agent.subscribe({
        onMessagesChanged: (messages) => {
          renderMessages(messages);
        },
        
        onRunInitialized: () => {
          isRunning = true;
          sendButtonEl.disabled = true;
          sendButtonEl.textContent = "Sending...";
        },
        
        onRunFinalized: () => {
          isRunning = false;
          sendButtonEl.disabled = false;
          sendButtonEl.textContent = "Send";
        },
        
        onRunFailed: (error) => {
          isRunning = false;
          sendButtonEl.disabled = false;
          sendButtonEl.textContent = "Send";
          console.error("Run failed:", error);
        },
      });

      // Connect to thread
      await agent.connect({
        threadId: "demo-thread",
      });
    }

    // Render messages
    function renderMessages(messages) {
      messagesEl.innerHTML = "";
      
      messages.forEach((msg) => {
        const msgDiv = document.createElement("div");
        msgDiv.className = `message ${msg.role}`;
        msgDiv.textContent = msg.content;
        messagesEl.appendChild(msgDiv);
      });
      
      // Scroll to bottom
      messagesEl.scrollTop = messagesEl.scrollHeight;
    }

    // Update status indicator
    function updateStatus(status) {
      const statusMap = {
        connected: { text: "Connected", class: "status-connected" },
        connecting: { text: "Connecting...", class: "status-connecting" },
        disconnected: { text: "Disconnected", class: "status-connecting" },
        error: { text: "Connection Error", class: "status-error" },
      };
      
      const { text, class: className } = statusMap[status] || statusMap.error;
      statusEl.textContent = text;
      statusEl.className = className;
      
      if (status === "connected") {
        inputEl.disabled = false;
        sendButtonEl.disabled = false;
      }
    }

    // Send message
    async function sendMessage() {
      const content = inputEl.value.trim();
      if (!content || !agent || isRunning) return;
      
      inputEl.value = "";
      
      try {
        await agent.run({
          messages: [
            { role: "user", content }
          ],
        });
      } catch (error) {
        console.error("Failed to send message:", error);
        alert("Failed to send message: " + error.message);
      }
    }

    // Event listeners
    sendButtonEl.addEventListener("click", sendMessage);
    
    inputEl.addEventListener("keypress", (e) => {
      if (e.key === "Enter") {
        sendMessage();
      }
    });

    // Initialize
    initializeAgent();
  </script>
</body>
</html>

TypeScript Support

CopilotKit Core is fully typed. Import types for better developer experience:
import type {
  CopilotKitCore,
  CopilotKitCoreConfig,
  CopilotKitCoreSubscriber,
  CopilotKitCoreRuntimeConnectionStatus,
  CopilotKitCoreErrorCode,
  FrontendTool,
  SuggestionsConfig,
} from "@copilotkitnext/core";

import type {
  AbstractAgent,
  Context,
  State,
  Message,
} from "@ag-ui/client";

Next Steps

React Integration

Use CopilotKit with React hooks and components

Angular Integration

Use CopilotKit with Angular services and directives

Tool Development

Build custom tools for your agents