Plugin Development Guide
Complete guide for developing ElizaOS plugins with v1.2.0 features
This comprehensive guide covers the complete process of developing ElizaOS plugins with the latest v1.2.0 features, including the enhanced Plugin interface, event system, HTTP routes, testing framework, and more.
Plugin Interface Definition
The Plugin interface in v1.2.0 provides a rich set of features for extending ElizaOS functionality:
export interface Plugin {
name: string;
description: string;
// Initialize plugin with runtime services
init?: (config: Record<string, string>, runtime: IAgentRuntime) => Promise<void>;
// Configuration
config?: { [key: string]: any };
// Services for background processing
services?: (typeof Service)[];
// Entity component definitions
componentTypes?: {
name: string;
schema: Record<string, unknown>;
validator?: (data: any) => boolean;
}[];
// Core plugin features
actions?: Action[];
providers?: Provider[];
evaluators?: Evaluator[];
adapter?: IDatabaseAdapter;
// AI model integration
models?: {
[key: string]: (...args: any[]) => Promise<any>;
};
// Event system
events?: PluginEvents;
// HTTP routes
routes?: Route[];
// Test suites
tests?: TestSuite[];
// Dependencies
dependencies?: string[];
testDependencies?: string[];
// Plugin priority for loading order
priority?: number;
// Schema validation
schema?: any;
}
Creating a Plugin
Using the CLI
Create a new plugin using the ElizaOS CLI:
# Create a plugin with interactive prompts
elizaos create --type plugin
# Create a plugin with a specific name
elizaos create my-awesome-plugin --type plugin
# Skip prompts and use defaults
elizaos create my-plugin --type plugin --yes
The CLI automatically prefixes your plugin name with plugin-
if not already present.
Plugin Structure
The generated plugin follows this structure:
plugin-my-awesome-plugin/
├── src/
│ ├── index.ts # Main plugin export
│ ├── plugin.ts # Plugin implementation
│ ├── actions/ # Action implementations
│ ├── providers/ # Provider implementations
│ ├── evaluators/ # Evaluator implementations
│ ├── services/ # Service implementations
│ └── __tests__/ # Test files
│ ├── plugin.test.ts
│ ├── integration.test.ts
│ ├── test-utils.ts
│ └── e2e/
│ └── plugin-e2e.ts
├── images/ # Plugin assets
├── package.json
├── tsconfig.json
├── tsup.config.ts
└── README.md
Core Plugin Components
Actions
Actions define what your plugin can do in response to user messages:
// src/actions/myAction.ts
import { Action, IAgentRuntime, Memory, State } from "@elizaos/core";
export const myAction: Action = {
name: "MY_ACTION",
similes: ["do something", "perform action"],
description: "Performs my custom action",
validate: async (runtime: IAgentRuntime, message: Memory) => {
// Validation logic
return true;
},
handler: async (runtime: IAgentRuntime, message: Memory, state: State) => {
// Action implementation
return {
text: "Action completed successfully!",
actions: ["MY_ACTION"],
};
},
examples: [
[
{
user: "user",
content: { text: "Can you do something?" },
},
{
user: "agent",
content: { text: "I'll do something for you." },
},
],
],
};
Providers
Providers supply data or context to the agent:
// src/providers/myProvider.ts
import { IAgentRuntime, Memory, Provider, State } from "@elizaos/core";
export const myProvider: Provider = {
name: "MY_PROVIDER",
description: "Provides data for my plugin",
get: async (runtime: IAgentRuntime, message: Memory, state: State) => {
return {
text: "Provider data",
values: { key: "value" },
data: { timestamp: Date.now() },
};
},
};
Services
Services handle background processing and lifecycle management:
// src/services/myService.ts
import { IAgentRuntime, Service, logger } from "@elizaos/core";
export class MyService extends Service {
static serviceType = "my-service";
capabilityDescription = "Handles background tasks for my plugin";
constructor(protected runtime: IAgentRuntime) {
super(runtime);
}
static async start(runtime: IAgentRuntime) {
logger.info("Starting my service");
const service = new MyService(runtime);
// Initialize service resources
return service;
}
static async stop(runtime: IAgentRuntime) {
logger.info("Stopping my service");
const service = runtime.getService(MyService.serviceType);
if (service) {
await service.stop();
}
}
async stop() {
// Clean up resources
logger.info("My service stopped");
}
}
Event System
Handle system events in your plugin:
// In your plugin definition
export const myPlugin: Plugin = {
name: "my-plugin",
description: "My awesome plugin",
events: {
MESSAGE_RECEIVED: [
async (params) => {
logger.debug("Message received:", params.message);
// Handle message received event
},
],
VOICE_MESSAGE_RECEIVED: [
async (params) => {
logger.debug("Voice message received:", params.message);
// Handle voice message
},
],
WORLD_CONNECTED: [
async (params) => {
logger.debug("World connected:", params.world);
// Handle world connection
},
],
},
// ... other plugin properties
};
HTTP Routes
Add HTTP endpoints to your plugin:
// In your plugin definition
export const myPlugin: Plugin = {
name: "my-plugin",
description: "My awesome plugin",
routes: [
{
name: "hello-world",
path: "/hello",
type: "GET",
handler: async (req: any, res: any) => {
res.json({ message: "Hello from my plugin!" });
},
},
{
name: "upload-file",
path: "/upload",
type: "POST",
isMultipart: true,
handler: async (req: any, res: any) => {
// Handle file upload
res.json({ success: true });
},
},
],
// ... other plugin properties
};
Custom AI Models
Integrate custom AI models:
// In your plugin definition
export const myPlugin: Plugin = {
name: "my-plugin",
description: "My awesome plugin",
models: {
[ModelType.TEXT_SMALL]: async (runtime, { prompt, stopSequences = [] }) => {
// Custom small model implementation
return "Generated text from custom model";
},
[ModelType.TEXT_LARGE]: async (runtime, params) => {
// Custom large model implementation
return "Generated text from custom large model";
},
},
// ... other plugin properties
};
Testing Your Plugin
E2E Tests
Create end-to-end tests that run in a real ElizaOS runtime:
// src/__tests__/e2e/my-plugin.ts
import { TestSuite } from "@elizaos/core";
export const MyPluginTestSuite: TestSuite = {
name: "my_plugin_test_suite",
description: "E2E tests for my plugin",
tests: [
{
name: "action_test",
fn: async (runtime) => {
// Test action execution
const action = runtime.actions?.find((a) => a.name === "MY_ACTION");
if (!action) {
throw new Error("Action not found");
}
// Execute action
const result = await action.handler(runtime, testMessage, testState);
if (!result.text.includes("success")) {
throw new Error("Action did not succeed");
}
},
},
{
name: "service_test",
fn: async (runtime) => {
// Test service functionality
const service = runtime.getService("my-service");
if (!service) {
throw new Error("Service not found");
}
// Test service capabilities
expect(service.capabilityDescription).toBeDefined();
},
},
],
};
Running Tests
# Run all tests
elizaos test
# Run specific test types
elizaos test component # Component tests only
elizaos test e2e # E2E tests only
# Run tests with specific name
elizaos test --name "my-test"
Development Workflow
Development Mode
Start development with hot reloading:
# Start development server
elizaos dev
# Or with specific character
elizaos dev --character ./characters/my-character.json
# Build before starting
elizaos dev --build
Building
# Build the plugin
bun run build
# Type check
bun run typecheck
# Lint code
bun run lint
Configuration
Configure your plugin with environment variables:
// src/plugin.ts
import { z } from "zod";
const configSchema = z.object({
API_KEY: z.string().min(1, "API key is required"),
ENDPOINT: z.string().url("Must be a valid URL").optional(),
TIMEOUT: z.number().int().positive().default(5000),
});
export const myPlugin: Plugin = {
name: "my-plugin",
description: "My awesome plugin",
config: {
API_KEY: process.env.API_KEY,
ENDPOINT: process.env.ENDPOINT || "https://api.example.com",
TIMEOUT: parseInt(process.env.TIMEOUT || "5000"),
},
async init(config: Record<string, string>) {
const validatedConfig = await configSchema.parseAsync(config);
// Set environment variables
for (const [key, value] of Object.entries(validatedConfig)) {
if (value !== undefined) {
process.env[key] = String(value);
}
}
},
// ... other plugin properties
};
Advanced Features
Component Types
Define custom entity types:
export const myPlugin: Plugin = {
name: "my-plugin",
description: "My awesome plugin",
componentTypes: [
{
name: "MyEntity",
schema: {
id: "string",
name: "string",
metadata: "object",
},
validator: (data) => {
return typeof data.id === "string" && typeof data.name === "string";
},
},
],
// ... other plugin properties
};
Dependencies
Specify plugin dependencies:
export const myPlugin: Plugin = {
name: "my-plugin",
description: "My awesome plugin",
// Runtime dependencies
dependencies: ["@elizaos/plugin-knowledge"],
// Test dependencies
testDependencies: ["@elizaos/plugin-testing-utils"],
// Plugin loading priority
priority: 100,
// ... other plugin properties
};
Publishing Your Plugin
Test Publishing
# Test the publish process
elizaos publish --test
# Verify package.json
cat package.json
# Check required images
ls images/
Publishing to Registry
# Login to npm
npm login
# Publish to registry
elizaos publish
This will:
- Publish to npm
- Create/update GitHub repository
- Submit to ElizaOS registry
Continuous Development
After initial publishing, use standard workflows:
# Update version
bun version patch # or minor/major
# Publish to npm
npm publish
# Push to Git
git push origin main && git push --tags
Plugin Creator Tool
Generate plugins with AI assistance:
# Interactive plugin generation
elizaos plugins generate
# With API key
elizaos plugins generate --api-key sk-ant-...
# From specification file
elizaos plugins generate --spec-file ./plugin-spec.json
# Skip tests generation
elizaos plugins generate --skip-tests
Best Practices
- Testing: Write comprehensive E2E tests for all functionality
- Configuration: Use Zod schemas for config validation
- Error Handling: Implement proper error handling and logging
- Documentation: Include clear documentation and examples
- Services: Use services for background processing
- Events: Leverage the event system for reactive behavior
- Dependencies: Specify all plugin dependencies
- Versioning: Follow semantic versioning for releases