How Plugins Work
A plugin is a bundle of capabilities you drop into an agent. No inheritance, no complex wiring - just a manifest that says “here are my actions, providers, and services.”
// That's the entire contract
const myPlugin: Plugin = {
name: 'my-plugin',
actions: [...], // What the agent can DO
providers: [...], // What the agent can SEE
services: [...], // What the agent can CONNECT to
};
One plugin, one concern. Twitter integration? Plugin. Blockchain actions? Plugin. Custom memory? Plugin. Keep them focused and composable.
Overview
The elizaOS plugin system is a modular extension mechanism that allows developers to add functionality to agents through a well-defined interface. Plugins enhance AI agents with new capabilities, integrations, and behaviors.
What Can Plugins Do?
- Platform Integrations: Connect to Discord, Telegram, Slack, Twitter, etc.
- LLM Providers: Integrate different AI models (OpenAI, Anthropic, Google, etc.)
- Blockchain/DeFi: Execute transactions, manage wallets, interact with smart contracts
- Data Sources: Connect to databases, APIs, or external services
- Custom Actions: Define new agent behaviors and capabilities
Plugin Interface
Every plugin must implement the core Plugin interface, which defines the structure and capabilities of a plugin. The interface includes:
- Identity:
name and description to identify the plugin
- Initialization: Optional
init function for setup logic
- Components: Arrays of
actions, providers, evaluators, and services
- Configuration: Settings and environment variables via
config
- Extensions: Optional database adapters, model handlers, routes, and event handlers
- Dependencies: Other plugins this plugin requires
- Priority: Loading order when multiple plugins are present
For the complete TypeScript interface definition, see Plugin Reference.
Plugin Initialization Lifecycle
Based on the runtime implementation, the initialization process follows a specific order:
1. Plugin Registration (registerPlugin method)
When a plugin is registered with the runtime:
- Validates plugin has a name
- Checks for duplicate plugins
- Adds to active plugins list
- Calls plugin’s
init() method if present
- Handles configuration errors gracefully
2. Component Registration Order
Components are registered in this specific sequence. For component details, see Plugin Components.
// 1. Database adapter (if provided)
if (plugin.adapter) {
this.registerDatabaseAdapter(plugin.adapter);
}
// 2. Actions
if (plugin.actions) {
for (const action of plugin.actions) {
this.registerAction(action);
}
}
// 3. Evaluators
if (plugin.evaluators) {
for (const evaluator of plugin.evaluators) {
this.registerEvaluator(evaluator);
}
}
// 4. Providers
if (plugin.providers) {
for (const provider of plugin.providers) {
this.registerProvider(provider);
}
}
// 5. Models
if (plugin.models) {
for (const [modelType, handler] of Object.entries(plugin.models)) {
this.registerModel(modelType, handler, plugin.name, plugin.priority);
}
}
// 6. Routes
if (plugin.routes) {
for (const route of plugin.routes) {
this.routes.push(route);
}
}
// 7. Events
if (plugin.events) {
for (const [eventName, eventHandlers] of Object.entries(plugin.events)) {
for (const eventHandler of eventHandlers) {
this.registerEvent(eventName, eventHandler);
}
}
}
// 8. Services (delayed if runtime not initialized)
if (plugin.services) {
for (const service of plugin.services) {
if (this.isInitialized) {
await this.registerService(service);
} else {
this.servicesInitQueue.add(service);
}
}
}
Route Definitions for HTTP Endpoints
Plugins can expose HTTP endpoints through the route system:
export type Route = {
type: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'STATIC';
path: string;
filePath?: string; // For static files
public?: boolean; // Public access
name?: string; // Route name
handler?: (req: RouteRequest, res: RouteResponse, runtime: IAgentRuntime) => Promise<void>;
isMultipart?: boolean; // File uploads
};
Example route implementation:
routes: [
{
name: 'hello-world-route',
path: '/helloworld',
type: 'GET',
handler: async (_req: any, res: any) => {
res.json({ message: 'Hello World!' });
}
}
]
Event System Integration
Plugins can handle system events through the event system:
Event Types
Standard events include:
- World events: WORLD_JOINED, WORLD_CONNECTED, WORLD_LEFT
- Entity events: ENTITY_JOINED, ENTITY_LEFT, ENTITY_UPDATED
- Room events: ROOM_JOINED, ROOM_LEFT
- Message events: MESSAGE_RECEIVED, MESSAGE_SENT, MESSAGE_DELETED
- Voice events: VOICE_MESSAGE_RECEIVED, VOICE_MESSAGE_SENT
- Run events: RUN_STARTED, RUN_ENDED, RUN_TIMEOUT
- Action/Evaluator events: ACTION_STARTED/COMPLETED, EVALUATOR_STARTED/COMPLETED
- Model events: MODEL_USED
Plugin Event Handlers
export type PluginEvents = {
[K in keyof EventPayloadMap]?: EventHandler<K>[];
} & {
[key: string]: ((params: any) => Promise<any>)[];
};
Database Adapter Plugins
Plugins can provide database adapters for custom storage backends:
The IDatabaseAdapter interface is extensive, including methods for:
- Agents, Entities, Components
- Memories (with embeddings)
- Rooms, Participants
- Relationships
- Tasks
- Caching
- Logs
Example database adapter plugin:
export const plugin: Plugin = {
name: '@elizaos/plugin-sql',
description: 'A plugin for SQL database access with dynamic schema migrations',
priority: 0,
schema,
init: async (_, runtime: IAgentRuntime) => {
const dbAdapter = createDatabaseAdapter(config, runtime.agentId);
runtime.registerDatabaseAdapter(dbAdapter);
}
};
Plugin Priority System
Plugins can specify a priority to control loading order:
- Higher priority plugins are loaded first
- Useful for plugins that provide fundamental services
- Model handlers use priority to determine which provider handles a model type
export const myPlugin: Plugin = {
name: 'high-priority-plugin',
priority: 100, // Loads before lower priority plugins
// ...
};
Plugin Dependencies
Plugins can declare dependencies on other plugins:
export const myPlugin: Plugin = {
name: 'my-plugin',
dependencies: ['@elizaos/plugin-sql', '@elizaos/plugin-bootstrap'],
testDependencies: ['@elizaos/plugin-test-utils'],
// ...
};
The runtime ensures dependencies are loaded before dependent plugins.
Plugin Configuration
Plugins can accept configuration through multiple mechanisms:
1. Environment Variables
init: async (config, runtime) => {
const apiKey = runtime.getSetting('MY_API_KEY');
if (!apiKey) {
throw new Error('MY_API_KEY not configured');
}
}
2. Config Object
export const myPlugin: Plugin = {
name: 'my-plugin',
config: {
defaultTimeout: 5000,
retryAttempts: 3,
},
// ...
};
3. Runtime Settings
Settings can be accessed through runtime.getSetting() which provides a consistent interface to environment variables and character settings.
Conditional Plugin Loading
Plugins are often conditionally loaded based on environment variables:
const plugins = [
// Always loaded
'@elizaos/plugin-bootstrap',
// Conditionally loaded based on API keys
...(process.env.ANTHROPIC_API_KEY ? ['@elizaos/plugin-anthropic'] : []),
...(process.env.OPENAI_API_KEY ? ['@elizaos/plugin-openai'] : []),
// Platform plugins
...(process.env.DISCORD_API_TOKEN ? ['@elizaos/plugin-discord'] : []),
...(process.env.TELEGRAM_BOT_TOKEN ? ['@elizaos/plugin-telegram'] : []),
];
Core Plugins
elizaOS includes two essential core plugins that provide foundational functionality:
Bootstrap Plugin
The core message handler and event system for elizaOS agents. Provides essential functionality for message processing, knowledge management, and basic agent operations. It includes:
- 13 essential actions (REPLY, SEND_MESSAGE, etc.)
- Core providers (time, character, recent messages)
- Task service
- Event handlers
SQL Plugin
Database integration and management for elizaOS. Features:
- Automatic schema migrations
- Multi-database support (PostgreSQL, PGLite)
- Sophisticated plugin architecture
Best Practices
- Plugin Dependencies: Use the
dependencies array to specify required plugins
- Conditional Loading: Check environment variables before loading platform-specific plugins
- Service Initialization: Handle missing API tokens gracefully in service constructors
- Event Handlers: Keep event handlers focused and delegate to specialized functions
- Error Handling: Use try-catch blocks and log errors appropriately
- Type Safety: Use TypeScript types from
@elizaos/core for all plugin components
- Priority Management: Set appropriate priorities for plugins that need to load early
- Configuration: Use
runtime.getSetting() for consistent configuration access
See Also