elizaOS

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

  1. Testing: Write comprehensive E2E tests for all functionality
  2. Configuration: Use Zod schemas for config validation
  3. Error Handling: Implement proper error handling and logging
  4. Documentation: Include clear documentation and examples
  5. Services: Use services for background processing
  6. Events: Leverage the event system for reactive behavior
  7. Dependencies: Specify all plugin dependencies
  8. Versioning: Follow semantic versioning for releases

Resources