Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@bdky/chat-pilot-kit

Package Overview
Dependencies
Maintainers
3
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@bdky/chat-pilot-kit

企业级 AI Agent 对话 SDK,框架无关,支持 SSE 流式对话与 WebSocket,内置 Agent 管理、消息处理管道与 NodeView 扩展机制

latest
npmnpm
Version
1.0.5
Version published
Weekly downloads
19
18.75%
Maintainers
3
Weekly downloads
 
Created
Source

@bdky/chat-pilot-kit

npm version License: MIT TypeScript

English | 简体中文

A headless, framework-agnostic AI chat SDK with streaming SSE support, extensible message nodes, and pluggable architecture.

@bdky/chat-pilot-kit is a lightweight, flexible SDK for building AI-powered chat applications. It provides a complete conversation management system with streaming support, multi-modal content handling, and a powerful extension system—all without imposing any UI framework constraints.

✨ Highlights

  • 🎯 Headless & framework-agnostic — No UI dependencies, works with React/Vue/vanilla JavaScript
  • 🌊 Streaming-first — Built-in SSE streaming with time-to-first-token (TTFT) tracking
  • 🧩 Extensible node system — 10 built-in node types + custom extension API
  • 🔌 Plugin architecture — MessageExtension system for custom message processing
  • 🎨 Multi-modal support — Text, Markdown, Image, File, Audio, Video, Thinking Blocks, Tool Calls
  • 📡 Event-driven — Emittery-based event system for real-time state updates
  • 💾 Conversation management — Import/export, session management, history persistence
  • 📘 Full TypeScript support — Complete type definitions out of the box

📦 Install

# npm
npm install @bdky/chat-pilot-kit reflect-metadata

# yarn
yarn add @bdky/chat-pilot-kit reflect-metadata

# pnpm
pnpm add @bdky/chat-pilot-kit reflect-metadata

Peer Dependencies:

  • reflect-metadata (required for dependency injection)

Formats:

  • ESM: @bdky/chat-pilot-kit (.esm.js)
  • CJS: @bdky/chat-pilot-kit (.cjs.js)
  • TypeScript declarations included

🚀 Quick Start

import 'reflect-metadata';
import {createChatPilotKit, BaseAgentService} from '@bdky/chat-pilot-kit';
import type {AgentMessageData} from '@bdky/chat-pilot-kit';

// 1. Define your custom AgentService
class MyAgentService extends BaseAgentService {
    async query(text: string): Promise<void> {
        const sessionId = this.sessionId();
        const queryId = this.queryId();

        // Simulate SSE streaming
        const chunks: AgentMessageData[] = [
            {answer: 'Hello', nodeType: 'text', queryId, sessionId},
            {answer: ' world!', nodeType: 'text', queryId, sessionId}
        ];

        for (const chunk of chunks) {
            this.onData(chunk);
        }

        this.onCompleted();
    }

    dispose(): void {
        // Cleanup resources
    }
}

// 2. Create ChatPilotKit instance
const {controller, emitter} = createChatPilotKit({
    agentService: MyAgentService
});

// 3. Subscribe to events
emitter.on('conversation_change', payload => {
    console.log('Conversation updated:', payload);
});

// 4. Send a query
await controller.query('Hello');

🏗️ Core Concepts

User Input → Controller.query()
  → AgentService.query() → AI Backend (SSE)
    → AgentMessageData chunks
      → Extension.canProcess() → Extension.process()
        → ConversationNode created/updated
          → Events emitted → UI subscribes & renders

Controller

The ChatPilotKitController is the main entry point for interacting with the SDK.

MethodParametersReturnsDescription
querytext: string, options?: IQueryOptionsPromise<void>Send a text query to the AI agent
queryWithAttachmentstext: string, attachments: IAttachmentInput[], options?: IQueryOptionsPromise<void>Send a query with file attachments
interrupt-voidInterrupt the current query
clear-voidClear all conversations
dispose-voidCleanup and destroy the controller
getOptions-IResolvedOptionsGet current configuration
exportConversations-IConversationBeanSnapshot[]Export all conversations as JSON
importConversationsconversations: IConversationBeanInput[], options?: IImportOptionsvoidImport conversations from JSON

Agent Service

Extend BaseAgentService to integrate with your AI backend.

Abstract Methods (must implement):

  • query(text: string): Promise<void> — Send query to AI backend
  • dispose(): void — Cleanup resources

Protected Methods (call from your implementation):

  • onData(data: AgentMessageData): void — Emit a data chunk
  • onCompleted(): void — Signal query completion
  • onError(error: Error): void — Report an error
  • onTtft(timestamp: number): void — Report time-to-first-token

Utility Methods:

  • sessionId(): string — Get current session ID
  • queryId(): string — Get current query ID
  • setSessionId(id: string): void — Set session ID
  • setQueryId(id: string): void — Set query ID
  • abort(): void — Abort current request

AgentMessageData Interface:

interface AgentMessageData {
    answer: string;           // Message text content
    nodeType?: string;        // Node type identifier (matches Extension.name)
    nodeData?: Record<string, unknown>; // Node-specific data
    queryId: string;          // Current query ID
    sessionId: string;        // Current session ID
}

Complete Example with SSE:

import {BaseAgentService} from '@bdky/chat-pilot-kit';
import {ky, sseHook} from '@bdky/chat-pilot-kit/http';
import type {AgentMessageData} from '@bdky/chat-pilot-kit';

class MySSEAgentService extends BaseAgentService {
    private abortController: AbortController | null = null;

    async query(text: string): Promise<void> {
        this.abortController = new AbortController();
        const sessionId = this.sessionId();
        const queryId = this.queryId();

        try {
            await ky.post('https://api.example.com/chat', {
                json: {message: text, sessionId, queryId},
                signal: this.abortController.signal,
                hooks: {
                    afterResponse: [
                        sseHook.afterResponse<AgentMessageData>({
                            onMessage: chunk => {
                                this.onData(chunk);
                            },
                            onComplete: () => {
                                this.onCompleted();
                            },
                            onError: error => {
                                this.onError(error);
                            }
                        })
                    ]
                }
            });
        }
        catch (error) {
            if (error.name !== 'AbortError') {
                this.onError(error as Error);
            }
        }
    }

    dispose(): void {
        this.abortController?.abort();
    }
}

Conversations & Nodes

ConversationBean Structure:

interface ConversationBean {
    id: string;                    // Unique conversation ID
    role: 'client' | 'aiWorker';   // Conversation role
    nodes: ConversationNode[];     // Array of message nodes
    completed: boolean;            // Whether conversation is complete
    createdAt: number;             // Creation timestamp
    updatedAt: number;             // Last update timestamp
}

ConversationNode Base Class:

All message nodes extend ConversationNode<TContent>:

abstract class ConversationNode<TContent = unknown> {
    id: string;
    type: string;
    content: TContent;
    completed: boolean;
    createdAt: number;
    updatedAt: number;
    metadata?: Record<string, unknown>;

    abstract toJSON(): IConversationNodeSnapshot<TContent>;
    updateContent(content: TContent): void;
    updateMetadata(metadata: Record<string, unknown>): void;
}

Built-in Node Types:

Node ClassType StringContent TypeStreamableFactory Methods
TextNode'text'stringNoTextNode.fromString(text)
MarkdownNode'markdown'stringYesMarkdownNode.fromString(text)
ImageNode'image'IImageContentNoImageNode.fromContent(content)
FileNode'file'IFileContentNoFileNode.fromContent(content)
AudioNode'audio'IAudioContentNoAudioNode.fromContent(content)
VideoNode'video'IVideoContentNoVideoNode.fromContent(content)
ThinkingBlockNode'thinking'IThinkingContentYesThinkingBlockNode.fromContent(content)
ToolCallNode'tool_call'IToolCallContentNoToolCallNode.fromContent(content)
GenericNodecustomunknownNonew GenericNode(type, content)
StreamableGenericNodecustomunknownYesnew StreamableGenericNode(type, content)

Content Type Interfaces:

interface IImageContent {
    url: string;
    alt?: string;
    width?: number;
    height?: number;
}

interface IFileContent {
    url: string;
    fileName: string;
    fileSize: number;
    fileType: string;
}

interface IThinkingContent {
    text: string;
    collapsed?: boolean;
}

interface IAudioContent {
    url: string;
    duration?: number;
    mimeType?: string;
}

interface IVideoContent {
    url: string;
    duration?: number;
    poster?: string;
    mimeType?: string;
}

interface IToolCallContent {
    name: string;
    arguments: Record<string, unknown>;
    result?: unknown;
    status: 'pending' | 'running' | 'completed' | 'error';
    error?: string;
}

Events

Subscribe to events using the emitter returned by createChatPilotKit().

EventPayloadDescription
readyneverSDK initialized and ready
conversation_add{conversationId, role, timestamp}New conversation created
conversation_changeIConversationChangePayloadConversation updated (nodes changed)
node_add{conversationId, node}New node added to conversation
node_update{conversationId, node}Existing node updated
errorIChatPilotKitErrorError occurred
interrupt{queryId, sessionId}Query interrupted
clearneverAll conversations cleared
ttftITtftPayloadTime-to-first-token measured
history_import{count, position}Conversations imported

Usage Example:

const {emitter} = createChatPilotKit({agentService: MyAgentService});

emitter.on('conversation_change', payload => {
    console.log('Conversation:', payload.conversationId);
    console.log('Nodes:', payload.nodes);
    console.log('Completed:', payload.completed);
});

emitter.on('error', error => {
    console.error('Error:', error.message);
    console.error('Category:', error.category);
    console.error('Severity:', error.severity);
});

emitter.on('ttft', payload => {
    console.log('Time to first token:', payload.totalLatency, 'ms');
});

🔌 Extensions

Extensions process incoming AgentMessageData chunks and create/update conversation nodes.

Built-in Extensions

The SDK includes 8 built-in extensions (ordered by priority):

ExtensionPriorityProcessesCreates Node
ThinkingBlockExtension50nodeType: 'thinking'ThinkingBlockNode
TextExtension100nodeType: 'text'TextNode
ImageExtension100nodeType: 'image'ImageNode
FileExtension100nodeType: 'file'FileNode
AudioExtension100nodeType: 'audio'AudioNode
VideoExtension100nodeType: 'video'VideoNode
ToolCallExtension100nodeType: 'tool_call'ToolCallNode
MarkdownExtension200nodeType: 'markdown'MarkdownNode

Get all built-in extensions:

import {getBuiltInExtensions} from '@bdky/chat-pilot-kit';

const extensions = getBuiltInExtensions();

Creating Custom Extensions

Use MessageExtension.create() to define custom extensions:

import {MessageExtension, GenericNode} from '@bdky/chat-pilot-kit';
import type {AgentMessageData} from '@bdky/chat-pilot-kit';

const ChartExtension = MessageExtension.create({
    name: 'chart',
    priority: 150,
    streamable: false,

    canProcess(data: AgentMessageData) {
        return data.nodeType === 'chart';
    },

    process(data: AgentMessageData) {
        // Create and return a new node from the data
        const content = {
            type: data.nodeData?.chartType as string,
            data: data.nodeData?.chartData
        };
        return new GenericNode('chart', content);
    },

    hydrate(snapshot) {
        // Restore node from snapshot when importing conversations
        return new GenericNode(snapshot.type, snapshot.content);
    },

    addNodeView() {
        return null; // Headless, no view
    }
});

// Use in createChatPilotKit
const {controller} = createChatPilotKit({
    agentService: MyAgentService,
    extensions: [ChartExtension]
});

Streamable Extension Example:

import {MessageExtension, StreamableGenericNode} from '@bdky/chat-pilot-kit';

const StreamingChartExtension = MessageExtension.create({
    name: 'streaming_chart',
    priority: 150,
    streamable: true,

    canProcess(data) {
        return data.nodeType === 'streaming_chart';
    },

    process(data) {
        // Create initial node with empty content
        return new StreamableGenericNode('streaming_chart', '');
    },

    onStreamAppend(node, data) {
        // Append content chunks as they arrive
        node.appendContent(data.answer);
    },

    onStreamEnd(node) {
        // Mark as completed when stream ends
        node.markCompleted();
    },

    hydrate(snapshot) {
        const node = new StreamableGenericNode(snapshot.type, snapshot.content);
        node.completed = snapshot.completed;
        return node;
    },

    addNodeView() {
        return null;
    }
});

Extension Lifecycle:

const MyExtension = MessageExtension.create({
    name: 'my_extension',
    priority: 100,

    onCreate() {
        console.log('Extension initialized');
        // Preload dependencies, setup resources
    },

    onDestroy() {
        console.log('Extension destroyed');
        // Cleanup resources
    },

    canProcess(data) {
        return data.nodeType === 'my_type';
    },

    process(data) {
        return new GenericNode('my_type', data.answer);
    },

    hydrate(snapshot) {
        return new GenericNode(snapshot.type, snapshot.content);
    },

    addNodeView() {
        return null;
    }
});

Overriding Built-in Extensions

Option 1: Replace with overrideExtensions

const CustomMarkdownExtension = MessageExtension.create({
    name: 'markdown', // Same name as built-in
    priority: 200,
    streamable: true,

    canProcess(data) {
        return data.nodeType === 'markdown';
    },

    process(data) {
        // Custom markdown processing
        return MarkdownNode.fromString(data.answer || '');
    },

    onStreamAppend(node, data) {
        // Custom streaming logic
        node.appendContent(data.answer || '');
    },

    onStreamEnd(node) {
        node.markCompleted();
    },

    hydrate(snapshot) {
        return MarkdownNode.fromJSON(snapshot);
    },

    addNodeView() {
        return null;
    }
});

createChatPilotKit({
    agentService: MyAgentService,
    overrideExtensions: [CustomMarkdownExtension] // Replaces built-in MarkdownExtension
});

Option 2: Extend with .extend()

import {MarkdownExtension} from '@bdky/chat-pilot-kit';

const EnhancedMarkdownExtension = MarkdownExtension.extend({
    priority: 250, // Change priority

    onStreamAppend(node, data) {
        // Call original behavior (if needed, manually)
        node.appendContent(data.answer || '');

        // Add custom behavior
        console.log('Enhanced markdown streaming:', data.answer);
    }
});

createChatPilotKit({
    agentService: MyAgentService,
    overrideExtensions: [EnhancedMarkdownExtension]
});

⚠️ Error Handling

The SDK provides a comprehensive error management system.

ErrorManager API:

const {controller, emitter} = createChatPilotKit({agentService: MyAgentService});

// Subscribe to errors
emitter.on('error', (error: IChatPilotKitError) => {
    console.error('Error:', error);
});

Error Categories:

enum ErrorCategory {
    NETWORK = 'NETWORK',           // Network-related errors
    TIMEOUT = 'TIMEOUT',           // Request timeout
    VALIDATION = 'VALIDATION',     // Input validation errors
    SERVICE = 'SERVICE',           // Backend service errors
    CONFIGURATION = 'CONFIGURATION', // Configuration errors
    INTERNAL = 'INTERNAL'          // Internal SDK errors
}

Error Severities:

enum ErrorSeverity {
    LOW = 'LOW',           // Minor issues, recoverable
    MEDIUM = 'MEDIUM',     // Moderate issues, may affect UX
    HIGH = 'HIGH',         // Serious issues, requires attention
    CRITICAL = 'CRITICAL'  // Critical failures, system unusable
}

IChatPilotKitError Interface:

interface IChatPilotKitError {
    code: string;                    // Error code (e.g., 'NETWORK_ERROR')
    message: string;                 // Human-readable message
    category: ErrorCategory;         // Error category
    severity: ErrorSeverity;         // Severity level
    source: 'agent' | 'upload' | 'extension' | 'controller' | 'conversation';
    metadata?: Record<string, unknown>; // Additional context
    originalError?: Error;           // Original error object
}

Usage Example:

emitter.on('error', error => {
    if (error.severity === ErrorSeverity.CRITICAL) {
        // Show critical error UI
        alert(`Critical error: ${error.message}`);
    }
    else if (error.category === ErrorCategory.NETWORK) {
        // Retry logic
        console.log('Network error, retrying...');
    }

    // Log to monitoring service
    logToMonitoring(error);
});

💾 Conversation Persistence

Export and import conversations for persistence or migration.

Export Conversations:

const snapshots = controller.exportConversations();
// Save to localStorage, database, etc.
localStorage.setItem('conversations', JSON.stringify(snapshots));

Import Conversations:

const snapshots = JSON.parse(localStorage.getItem('conversations') || '[]');

controller.importConversations(snapshots, {
    position: 'prepend' // or 'replace'
});

IImportOptions:

interface IImportOptions {
    position?: 'prepend' | 'replace'; // Default: 'prepend'
}
  • 'prepend': Insert imported conversations before existing ones
  • 'replace': Replace all existing conversations

Hydration Process:

When importing conversations, the SDK:

  • Parses the JSON snapshots
  • Matches each node's type to registered extensions
  • Calls the extension's process() method to recreate nodes
  • Restores node state (content, metadata, completed status)
  • Emits history_import event

Complete Round-trip Example:

// Export
const snapshots = controller.exportConversations();
const json = JSON.stringify(snapshots);

// Save to backend
await fetch('/api/save-history', {
    method: 'POST',
    body: json,
    headers: {'Content-Type': 'application/json'}
});

// Later: Load from backend
const response = await fetch('/api/load-history');
const loadedSnapshots = await response.json();

// Import
controller.importConversations(loadedSnapshots, {
    position: 'replace'
});

🌊 Streaming

Nodes that implement IStreamableNode support incremental content updates.

IStreamableNode Interface:

interface IStreamableNode<TChunk = string> {
    appendContent(chunk: TChunk): void;
}

Type Guard:

import {isStreamableNode} from '@bdky/chat-pilot-kit';

if (isStreamableNode(node)) {
    node.appendContent('new content');
}

Built-in Streamable Nodes:

  • MarkdownNode — Appends markdown text
  • ThinkingBlockNode — Appends thinking process text
  • StreamableGenericNode — Generic streamable node

How Streaming Works in Extensions:

const StreamingExtension = MessageExtension.create({
    name: 'streaming_text',
    priority: 100,
    streamable: true,

    canProcess(data) {
        return data.nodeType === 'streaming_text';
    },

    process(data) {
        // Create initial streamable node
        return new StreamableGenericNode('streaming_text', data.answer || '');
    },

    onStreamAppend(node, data) {
        // Append content chunks as they arrive
        node.appendContent(data.answer);
    },

    onStreamEnd(node) {
        // Mark as completed when stream ends
        node.markCompleted();
    },

    hydrate(snapshot) {
        const node = new StreamableGenericNode(snapshot.type, snapshot.content);
        node.completed = snapshot.completed;
        return node;
    },

    addNodeView() {
        return null;
    }
});

MarkdownNode Streaming Example:

import {MarkdownNode} from '@bdky/chat-pilot-kit';

const node = MarkdownNode.fromString('');

// Simulate streaming
node.appendContent('# Hello\n');
node.appendContent('This is ');
node.appendContent('**streaming** ');
node.appendContent('markdown.');

console.log(node.content); // "# Hello\nThis is **streaming** markdown."

🎨 NodeView System (Framework Integration)

The NodeView system enables framework-specific rendering of conversation nodes.

Core Interfaces:

interface NodeViewProps<TNode extends ConversationNode = ConversationNode> {
    node: TNode;
    updateContent: (content: TNode['content']) => void;
    updateMetadata: (metadata: Record<string, unknown>) => void;
    completed: boolean;
    role: 'client' | 'aiWorker';
    conversation: ConversationBean;
    destroy: () => void;
}

interface INodeView {
    mount(container: HTMLElement): void;
    update(props: NodeViewProps): void;
    destroy(): void;
}

type NodeViewFactory<TNode extends ConversationNode = ConversationNode> =
    (props: NodeViewProps<TNode>) => INodeView;

How to Create Framework Adapters:

  • Implement NodeViewFactory for each node type
  • Create a registry mapping node types to factories
  • Render nodes using framework-specific components
  • Subscribe to conversation_change events to trigger re-renders

Reference Implementation:

See @bdky/chat-pilot-kit-react for a complete React adapter implementation with:

  • ChatPilotKitProvider context provider
  • useChatPilotKit() hook
  • Built-in NodeView components for all node types
  • TypeScript support

🌐 HTTP Utilities

The SDK re-exports HTTP utilities from ky and @bdky/ky-sse-hook for convenience.

Sub-path Export:

import {ky, sseHook, HTTPError, TimeoutError} from '@bdky/chat-pilot-kit/http';
import type {KyInstance, Options} from '@bdky/chat-pilot-kit/http';

Re-exported from ky:

  • ky (default export) — HTTP client
  • HTTPError, TimeoutError — Error classes
  • All TypeScript types

Re-exported from @bdky/ky-sse-hook:

  • sseHook — SSE streaming hook for ky

Usage in AgentService:

import {BaseAgentService} from '@bdky/chat-pilot-kit';
import {ky, sseHook} from '@bdky/chat-pilot-kit/http';

class MyAgentService extends BaseAgentService {
    async query(text: string): Promise<void> {
        await ky.post('https://api.example.com/chat', {
            json: {message: text},
            hooks: {
                afterResponse: [
                    sseHook.afterResponse({
                        onMessage: chunk => this.onData(chunk),
                        onComplete: () => this.onCompleted(),
                        onError: error => this.onError(error)
                    })
                ]
            }
        });
    }

    dispose(): void {}
}

⚙️ Configuration Reference

IOptions Interface:

interface IOptions<AS extends BaseAgentService = BaseAgentService> {
    agentService: Newable<AS>;           // Required: Your AgentService class
    enableDebugMode?: boolean;           // Default: false
    sessionTimeout?: number;             // Default: 300000 (5 minutes)
    extensions?: MessageExtensionInstance[]; // Custom extensions (appended)
    overrideExtensions?: MessageExtensionInstance[]; // Override built-in extensions
}

IQueryOptions Interface:

interface IQueryOptions {
    sessionId?: string;                  // Custom session ID
    queryId?: string;                    // Custom query ID
    streaming?: boolean;                 // Enable streaming (default: true)
    metadata?: Record<string, unknown>;  // Custom metadata
}

IAttachmentInput Interface:

interface IAttachmentInput {
    url: string;                         // File URL (uploaded)
    fileName: string;                    // File name
    fileSize: number;                    // File size in bytes
    fileType: string;                    // MIME type
    metadata?: Record<string, unknown>;  // Custom metadata
}

Usage Example:

const {controller} = createChatPilotKit({
    agentService: MyAgentService,
    enableDebugMode: true,
    sessionTimeout: 600000, // 10 minutes
    extensions: [CustomExtension],
    overrideExtensions: [EnhancedMarkdownExtension]
});

await controller.query('Hello', {
    sessionId: 'custom-session-123',
    queryId: 'query-456',
    streaming: true,
    metadata: {source: 'web-app'}
});

await controller.queryWithAttachments(
    'Analyze this image',
    [
        {
            url: 'https://example.com/image.jpg',
            fileName: 'image.jpg',
            fileSize: 102400,
            fileType: 'image/jpeg'
        }
    ],
    {metadata: {feature: 'image-analysis'}}
);

📘 TypeScript

The SDK is written in TypeScript and provides complete type definitions.

Type Imports:

import type {
    // Core
    IChatPilotKitController,
    IChatPilotKitEmitter,
    IOptions,
    IResolvedOptions,

    // Agent
    AgentMessageData,
    NBaseAgentService,

    // Nodes
    ConversationNode,
    IConversationNodeSnapshot,
    IConversationNodeInput,
    IStreamableNode,

    // Node Content
    IImageContent,
    IFileContent,
    IThinkingContent,
    IAudioContent,
    IVideoContent,
    IToolCallContent,
    ToolCallStatus,

    // Conversations
    ConversationRole,
    NConversationBean,
    IConversationBeanSnapshot,
    IConversationBeanInput,

    // Extensions
    MessageExtensionConfig,
    MessageExtensionInstance,

    // Events
    IConversationChangePayload,
    ITtftPayload,

    // Errors
    IChatPilotKitError,
    ICreateErrorParams,
    ErrorCategory,
    ErrorSeverity,

    // NodeView
    NodeViewProps,
    NodeViewFactory,
    INodeView,

    // Query
    IQueryOptions,
    IImportOptions,
    IAttachmentInput,

    // Node Data
    IConversationNodeData,
    ITextNodeData,
    IMarkdownNodeData,
    IImageNodeData,
    IFileNodeData,
    IThinkingNodeData,
    IAudioNodeData,
    IVideoNodeData,
    IToolCallNodeData,
    IBuiltInNodeData
} from '@bdky/chat-pilot-kit';

Key Type Exports:

  • Controller: IChatPilotKitController
  • Emitter: IChatPilotKitEmitter
  • Agent: AgentMessageData, NBaseAgentService
  • Nodes: ConversationNode, IStreamableNode, all content interfaces
  • Extensions: MessageExtensionConfig, MessageExtensionInstance
  • Errors: IChatPilotKitError, ErrorCategory, ErrorSeverity
  • Events: IConversationChangePayload, ITtftPayload

🔗 Framework Adapters

Official framework adapters are available:

PackageFrameworkDescription
@bdky/chat-pilot-kit-reactReact 19+React hooks, Provider, and NodeView components
@bdky/chat-pilot-kit-vue3Vue 3Vue composables and components

React Example:

import {ChatPilotKitProvider, useChatPilotKit} from '@bdky/chat-pilot-kit-react';

function App() {
    return (
        <ChatPilotKitProvider agentService={MyAgentService}>
            <ChatUI />
        </ChatPilotKitProvider>
    );
}

function ChatUI() {
    const {controller, conversations} = useChatPilotKit();

    return (
        <div>
            {conversations.map(conv => (
                <div key={conv.id}>
                    {conv.nodes.map(node => (
                        <NodeView key={node.id} node={node} />
                    ))}
                </div>
            ))}
        </div>
    );
}

See adapter package READMEs for detailed documentation.

🌍 Browser Support

BrowserVersion
Chrome≥ 74
Firefox≥ 90
Safari≥ 14.1
Edge≥ 79
iOS Safari≥ 14.1
Android Chrome≥ 74

📄 License

MIT

Made with ❤️ by 百度智能云客悦 Ky-FE Team

Keywords

chat

FAQs

Package last updated on 09 May 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts