LICENSE Documentation

Official TypeScript developer resources and documentation for elizaOS/LICENSE. Verified technical context for AI.

0 stars TypeScript TypeScript 1 file ยท ~64,239 tokens
RAW Doc

1. Project Overview & Quickstart (elizaOS/LICENSE)

LICENSE

Open-source repository elizaOS/LICENSE

Repository Details

Note: High-volume repository documentation is actively indexed and synchronized by YakaAI.

2. Official Technical Reference & Guides (elizaOS/docs)

File: README.md

ElizaOS Documentation

Welcome to the official documentation repository for ElizaOS - a powerful framework for building AI agents with memory, planning, and tool use capabilities.

๐Ÿ“š About This Documentation

This repository contains the comprehensive documentation for ElizaOS, including:

  • Getting Started - Quick setup guides and tutorials
  • Core Concepts - Understanding agents, plugins, and the architecture
  • Deep Dive - Advanced topics like memory systems, services, and event handling
  • REST Reference - Complete REST API documentation for all modules
  • Examples - Real-world implementations and patterns

Development

Prerequisites

  • Node.js (v23 or higher)
  • bun (or npm/yarn)

Local Development Setup

Install the Mintlify CLI to preview the documentation changes locally:

bash
bun install -g mint

Running Locally

Navigate to the root of your documentation (where docs.json is located) and run:

bash
mint dev

This will start a local development server at http://localhost:3000 where you can preview your changes in real-time.

Project Structure

text
docs/
โ”œโ”€โ”€ rest-reference/      # REST API documentation
โ”œโ”€โ”€ core-concepts/      # Core concepts and architecture
โ”œโ”€โ”€ deep-dive/         # Advanced topics
โ”œโ”€โ”€ images/            # Documentation images
โ”œโ”€โ”€ logo/              # Logo assets
โ”œโ”€โ”€ snippets/          # Reusable code snippets
โ”œโ”€โ”€ docs.json          # Mintlify configuration
โ”œโ”€โ”€ index.mdx          # Landing page
โ”œโ”€โ”€ quickstart.mdx     # Quick start guide
โ””โ”€โ”€ development.mdx    # Development guide

๐Ÿ“ Contributing to Documentation

We welcome contributions to improve the ElizaOS documentation! Here's how you can help:

  1. Fork this repository
  2. Create a new branch for your changes
  3. Make your documentation improvements
  4. Test locally using mint dev
  5. Submit a pull request

Documentation Guidelines

  • Use clear, concise language
  • Include code examples where appropriate
  • Follow the existing structure and formatting
  • Test all code snippets
  • Add images/diagrams for complex concepts

Publishing Changes

Changes are automatically deployed when merged to the main branch. The documentation is hosted using Mintlify's infrastructure.

Deployment Process

  1. Install the Mintlify GitHub App on your repository
  2. Push changes to your default branch
  3. Changes will be automatically deployed to production

Find the installation link in your Mintlify dashboard.

๐Ÿ”ง Troubleshooting

Common Issues

  • Dev environment not running

    • Run mint update to ensure you have the latest version of the CLI
    • Check that you're in the correct directory with docs.json
  • Page loads as 404

    • Ensure you're running the command in a folder containing docs.json
    • Check that your page is properly listed in the navigation
  • Changes not reflecting

    • Clear your browser cache
    • Restart the development server
    • Check for syntax errors in your MDX files

๐Ÿ“– Learn More

๐Ÿ“„ License

This documentation is part of the ElizaOS project. Please refer to the main repository for license information.


Built with โค๏ธ using Mintlify


File: guides/action-planning.mdx


title: "Multi-Step Action Planning"

The Problem

Simple agents can do one thing at a time. But real tasks are complex:

  • "Search the web, summarize results, and tweet the highlights"
  • "Check my calendar, find a free slot, and schedule a meeting"
  • "Analyze this data, generate a chart, and email it to the team"

Each of these requires multiple actions executed in sequence, with results from one feeding into the next.

**Action planning lets your agent think in steps.** The LLM decides the sequence, and ElizaOS executes each action, passing results forward automatically.

How It Works

When the LLM returns multiple actions, ElizaOS creates an ActionPlan:

typescript
interface ActionPlan {
  thought: string;       // LLM's reasoning for this plan
  totalSteps: number;    // How many actions to execute
  currentStep: number;   // Which step we're on (0-indexed)
  steps: ActionPlanStep[];
}

interface ActionPlanStep {
  action: string;        // Action name (e.g., "SEARCH", "TWEET")
  status: 'pending' | 'completed' | 'failed';
  error?: string;        // If failed, why
  result?: ActionResult; // Output from this step
}

The plan flows through state, so each action can see what came before:

mermaid
flowchart TB
    User["User: Search for AI news and tweet the top story"]
    LLM["LLM Decision: SEARCH, TWEETCreates plan with 2 steps"]
    Step1["Step 1: SEARCHExecutes web searchStatus: completed"]
    Step2["Step 2: TWEETReads previous resultsComposes tweetStatus: completed"]

    User --> LLM
    LLM --> Step1
    Step1 -->|"result stored in actionResults"| Step2

    style User fill:#e3f2fd
    style LLM fill:#fff3e0
    style Step1 fill:#e8f5e9
    style Step2 fill:#e8f5e9

Accessing Previous Results

In your action handler, access results from previous steps:

typescript
const myAction: Action = {
  name: 'SUMMARIZE',
  async handler(runtime, message, state, options, callback) {
    // Get all previous action results
    const previousResults = state?.data?.actionResults || [];

    // Find specific action result
    const searchResult = previousResults.find(r => r.action === 'SEARCH');

    if (searchResult?.text) {
      // Use search results to generate summary
      const summary = await runtime.useModel(ModelType.TEXT_SMALL, {
        prompt: `Summarize this: ${searchResult.text}`
      });

      return {
        text: summary,
        action: 'SUMMARIZE',
        success: true
      };
    }

    return {
      text: 'No previous results to summarize',
      action: 'SUMMARIZE',
      success: false
    };
  }
};

Accessing Plan State

The current action plan is available in state.data.actionPlan:

typescript
async handler(runtime, message, state, options, callback) {
  const plan = state?.data?.actionPlan;

  if (plan) {
    console.log(`Step ${plan.currentStep + 1} of ${plan.totalSteps}`);
    console.log(`LLM reasoning: ${plan.thought}`);

    // Check previous steps
    for (const step of plan.steps) {
      if (step.status === 'completed') {
        console.log(`${step.action}: ${step.result?.text}`);
      } else if (step.status === 'failed') {
        console.log(`${step.action} failed: ${step.error}`);
      }
    }
  }

  // Your action logic...
}

Error Handling in Plans

When a step fails, the plan continues by default. Handle failures gracefully:

typescript
const resilientAction: Action = {
  name: 'PROCESS_DATA',

  async handler(runtime, message, state, options, callback) {
    const previousResults = state?.data?.actionResults || [];

    // Check if a required previous step failed
    const fetchResult = previousResults.find(r => r.action === 'FETCH_DATA');

    if (fetchResult?.success === false) {
      // Previous step failed - use fallback
      return {
        text: 'Using cached data due to fetch failure',
        action: 'PROCESS_DATA',
        success: true,
        data: { usedFallback: true }
      };
    }

    // Normal processing
    return {
      text: `Processed: ${fetchResult?.text}`,
      action: 'PROCESS_DATA',
      success: true
    };
  }
};

Retrieving Results After Execution

After message processing, retrieve action results programmatically:

typescript
// Process a message
const result = await runtime.handleMessage({
  entityId: userId,
  roomId: roomId,
  content: { text: 'Search and summarize AI news', source: 'api' }
});

// Get the action results from that message
const actionResults = runtime.getActionResults(result.messageId);

for (const result of actionResults) {
  console.log(`${result.action}: ${result.success ? 'OK' : 'FAILED'}`);
  console.log(`Output: ${result.text}`);
  if (result.data) {
    console.log(`Data:`, result.data);
  }
}

Designing Actions for Chaining

When building actions that work well in chains:

Include a `data` field in results for downstream actions to consume programmatically. Actions may be retried. Avoid side effects that can't be repeated safely. Verify required previous results exist before proceeding. Return `success: false` with a clear error message rather than throwing.

Example: Data Pipeline

typescript
// Step 1: Fetch data
const fetchAction: Action = {
  name: 'FETCH_DATA',
  async handler(runtime, message, state) {
    const data = await fetchFromAPI();
    return {
      text: `Fetched ${data.length} records`,
      action: 'FETCH_DATA',
      success: true,
      data: { records: data }  // Structured data for next step
    };
  }
};

// Step 2: Transform data (uses Step 1 output)
const transformAction: Action = {
  name: 'TRANSFORM_DATA',
  async handler(runtime, message, state) {
    const fetchResult = state?.data?.actionResults?.find(
      r => r.action === 'FETCH_DATA'
    );

    if (!fetchResult?.data?.records) {
      return { text: 'No data to transform', action: 'TRANSFORM_DATA', success: false };
    }

    const transformed = fetchResult.data.records.map(r => ({
      ...r,
      processed: true,
      timestamp: Date.now()
    }));

    return {
      text: `Transformed ${transformed.length} records`,
      action: 'TRANSFORM_DATA',
      success: true,
      data: { records: transformed }
    };
  }
};

// Step 3: Store data (uses Step 2 output)
const storeAction: Action = {
  name: 'STORE_DATA',
  async handler(runtime, message, state) {
    const transformResult = state?.data?.actionResults?.find(
      r => r.action === 'TRANSFORM_DATA'
    );

    if (!transformResult?.data?.records) {
      return { text: 'No data to store', action: 'STORE_DATA', success: false };
    }

    await database.insert(transformResult.data.records);

    return {
      text: `Stored ${transformResult.data.records.length} records`,
      action: 'STORE_DATA',
      success: true
    };
  }
};

ActionResult Type

typescript
interface ActionResult {
  action: string;       // Action name
  text: string;         // Human-readable result
  success: boolean;     // Did it work?
  error?: string;       // Error message if failed
  data?: Record<string, unknown>;  // Structured data for chaining
}

Next Steps

Complete action handler API How state flows through the runtime Long-running actions with task workers Stream action outputs in real-time

File: guides/add-multiple-agents.mdx


title: 'Add Multiple Agents'

**Video Tutorial**: [**Multiple Agents and Characters**](https://www.youtube.com/watch?v=T53M7KueDgM&list=PLrjBjP4nU8ehOgKAa0-XddHzE0KK0nNvS&index=3) This guide builds on concepts from [Customize an Agent](/guides/customize-an-agent)

Step 1: Add Hemingway

Add Hemingway to your project

You already have Shakespeare in src/character.ts from the previous guide. Now let's add another agent to our project so they can interact. We'll create a fresh character file for Hemingway using the CLI:

bash terminal
elizaos create --type agent hemingway

This clones a JSON character template as hemingway.json. You'll now have:

  • src/character.ts - Shakespeare (TypeScript format)
  • hemingway.json - Hemingway (JSON format)
The CLI clones JSON character templates by default. If you prefer TypeScript characters, you can manually clone your `character.ts` file from your IDE. They work exactly the same, it's just a matter of preference.

Customize Hemingway's personality

Open hemingway.json and update it to customize Hemingway's personality:

json hemingway.json
{
  "name": "hemingway",
  "system": "Respond to all messages in a helpful, conversational manner. Provide assistance on a wide range of topics, using knowledge when needed. Be concise but thorough, friendly but professional. Use humor when appropriate and be empathetic to user needs. Provide valuable information and insights when questions are asked.",  // [!code --]
  "system": "You are Ernest Hemingway. Speak simply. Use short sentences. Cut the fat. Every word must earn its place. You respect Shakespeare but find him wordy. You've lived through war, love, and loss. Truth matters more than beauty. Experience matters more than theory. Never use two words when one will do. Avoid adjectives. Kill your darlings. The first draft of anything is shit, so make every word count now.",  // [!code ++]
  "bio": [
    "hemingway is a helpful AI assistant created to provide assistance and engage in meaningful conversations.",  // [!code --]
    "hemingway is knowledgeable, creative, and always eager to help users with their questions and tasks."  // [!code --]
    "Ernest Hemingway, American novelist and journalist",  // [!code ++]
    "Master of the iceberg theory - show only what matters",  // [!code ++]
    "Champion of simple declarative sentences",  // [!code ++]
    "War correspondent who saw truth in trenches",  // [!code ++]
    "Believer that courage is grace under pressure",  // [!code ++]
    "Man who lived fully - bullfights, safaris, deep-sea fishing",  // [!code ++]
    "Writer who found truth in simple things",  // [!code ++]
    "Teacher who says: write one true sentence"  // [!code ++]
  ]
}

Just like in the previous guide, continue editing the other fields (topics, style, messageExamples, etc.) to match Hemingway as you see fit.

Step 2: Configure Discord and voice

Add Discord plugin to Hemingway

Add plugin-discord to Hemingway so he can join Shakespeare in our Discord server:

json hemingway.json
{
  "plugins": [
    "@elizaos/plugin-sql",
    "@elizaos/plugin-openai",
    "@elizaos/plugin-bootstrap",
    "@elizaos/plugin-discord"  // [!code ++]
  ]
}

Configure agent-specific keys

But wait! We have our Discord environment variables defined in .env, but we need unique ones for each agent. Hemingway and Shakespeare need their own Discord bot tokens. How do we have agent-specific keys?

For that, we use secrets under settings in each character file. This allows each agent to have their own Discord bot identity:

For Hemingway (hemingway.json):

json hemingway.json
{
  "settings": {
    "secrets": {},  // [!code --]
    "secrets": {  // [!code ++]
      "DISCORD_APPLICATION_ID": "YOUR_HEMINGWAY_APP_ID",  // [!code ++]
      "DISCORD_API_TOKEN": "YOUR_HEMINGWAY_BOT_TOKEN",  // [!code ++]
    },  // [!code ++]
    "avatar": "https://example.com/hemingway-portrait.png"
  }
}

For Shakespeare (src/character.ts):

typescript src/character.ts
export const character: Character = {
  settings: {
    secrets: {},  // [!code --]
    secrets: {  // [!code ++]
      DISCORD_APPLICATION_ID: "YOUR_SHAKESPEARE_APP_ID",  // [!code ++]
      DISCORD_API_TOKEN: "YOUR_SHAKESPEARE_BOT_TOKEN",  // [!code ++]
    },  // [!code ++]
    avatar: 'https://example.com/shakespeare-portrait.png',
  },
}
Each agent needs its own Discord application and bot token. Follow the Discord setup steps from the [previous guide](/guides/customize-an-agent#create-discord-application) for each agent you create.

Enable voice mode

Let's enable voice capabilities for our agents in Discord:

For Hemingway (hemingway.json):

json hemingway.json
{
  "settings": {
    "secrets": {
      "DISCORD_APPLICATION_ID": "YOUR_HEMINGWAY_APP_ID",
      "DISCORD_API_TOKEN": "YOUR_HEMINGWAY_BOT_TOKEN",
      "DISCORD_VOICE_ENABLED": "true"  // [!code ++]
    }
  }
}

For Shakespeare (src/character.ts):

typescript src/character.ts
{
  settings: {
    secrets: {
      DISCORD_APPLICATION_ID: "YOUR_SHAKESPEARE_APP_ID",
      DISCORD_API_TOKEN: "YOUR_SHAKESPEARE_BOT_TOKEN",
      DISCORD_VOICE_ENABLED: "true"  // [!code ++]
    }
  }
}

Add ElevenLabs voice provider

Now let's add plugin-elevenlabs to provide high-quality voice synthesis for our agents:

Add ElevenLabs plugin:

json hemingway.json
{
  "plugins": [
    "@elizaos/plugin-sql",
    "@elizaos/plugin-openai",
    "@elizaos/plugin-discord",
    "@elizaos/plugin-bootstrap",
    "@elizaos/plugin-elevenlabs"  // [!code ++]
  ]
}
typescript src/character.ts
export const character: Character = {
  plugins: [
    '@elizaos/plugin-sql',
    '@elizaos/plugin-discord',
    '@elizaos/plugin-elevenlabs',  // [!code ++]
    ...(process.env.OPENAI_API_KEY?.trim() ? ['@elizaos/plugin-openai'] : []),
    ...(!process.env.IGNORE_BOOTSTRAP ? ['@elizaos/plugin-bootstrap'] : []),
  ],
}

Configure voices for each agent

Now let's add the ElevenLabs secrets so each agent has their own distinct voice:

For Hemingway (hemingway.json):

json hemingway.json
{
  "settings": {
    "secrets": {
      "DISCORD_APPLICATION_ID": "YOUR_HEMINGWAY_APP_ID",
      "DISCORD_API_TOKEN": "YOUR_HEMINGWAY_BOT_TOKEN",
      "DISCORD_VOICE_ENABLED": "true",
      
      "ELEVENLABS_API_KEY": "your_elevenlabs_api_key",  // [!code ++]
      "ELEVENLABS_VOICE_ID": "Xb7hH8MSUJpSbSDYk0k2",  // Deep male voice  // [!code ++]
      "ELEVENLABS_MODEL_ID": "eleven_multilingual_v2",  // [!code ++]
      "ELEVENLABS_VOICE_STABILITY": "0.5",  // [!code ++]
      "ELEVENLABS_OPTIMIZE_STREAMING_LATENCY": "0",  // [!code ++]
      "ELEVENLABS_OUTPUT_FORMAT": "pcm_16000",  // [!code ++]
      "ELEVENLABS_VOICE_SIMILARITY_BOOST": "0.75",  // [!code ++]
      "ELEVENLABS_VOICE_STYLE": "0",  // [!code ++]
      "ELEVENLABS_VOICE_USE_SPEAKER_BOOST": "true"  // [!code ++]
    }
  }
}

For Shakespeare (src/character.ts):

typescript src/character.ts
{
  settings: {
    secrets: {
      DISCORD_APPLICATION_ID: "YOUR_SHAKESPEARE_APP_ID",
      DISCORD_API_TOKEN: "YOUR_SHAKESPEARE_BOT_TOKEN",
      DISCORD_VOICE_ENABLED: "true",
      
      ELEVENLABS_API_KEY: "your_elevenlabs_api_key",  // [!code ++]
      ELEVENLABS_VOICE_ID: "21m00Tcm4TlvDq8ikWAM",  // Theatrical British voice  // [!code ++]
      ELEVENLABS_MODEL_ID: "eleven_multilingual_v2",  // [!code ++]
      ELEVENLABS_VOICE_STABILITY: "0.3",  // More variation for dramatic effect  // [!code ++]
      ELEVENLABS_OPTIMIZE_STREAMING_LATENCY: "0",  // [!code ++]
      ELEVENLABS_OUTPUT_FORMAT: "pcm_16000",  // [!code ++]
      ELEVENLABS_VOICE_SIMILARITY_BOOST: "0.75",  // [!code ++]
      ELEVENLABS_VOICE_STYLE: "0.5",  // More expressive  // [!code ++]
      ELEVENLABS_VOICE_USE_SPEAKER_BOOST: "true"  // [!code ++]
    }
  }
}
Get your ElevenLabs API key from [elevenlabs.io](https://elevenlabs.io) and explore different voice IDs to find the perfect match for each agent's personality.

Step 3: Configure multi-agent project

Add newly created agent to your project

Update your src/index.ts to include both agents so they start automatically:

typescript src/index.ts
import { logger, type IAgentRuntime, type Project, type ProjectAgent } from '@elizaos/core';
import { character } from './character.ts';
import hemingway from '../hemingway.json';  // [!code ++]

const initCharacter = ({ runtime }: { runtime: IAgentRuntime }) => {
  logger.info('Initializing character');
  logger.info({ name: character.name }, 'Name:');
};

export const projectAgent: ProjectAgent = {
  character,
  init: async (runtime: IAgentRuntime) => await initCharacter({ runtime }),
};

// Add Hemingway agent  // [!code ++]
const hemingwayAgent: ProjectAgent = {  // [!code ++]
  character: hemingway,  // [!code ++]
  init: async (runtime: IAgentRuntime) => {  // [!code ++]
    logger.info('Initializing Hemingway');  // [!code ++]
    logger.info({ name: hemingway.name }, 'Name:');  // [!code ++]
  },  // [!code ++]
};  // [!code ++]

const project: Project = {
  agents: [projectAgent],  // [!code --]
  agents: [projectAgent, hemingwayAgent],  // [!code ++]
};

Launch both agents simultaneously

Now when you start your project, both agents launch automatically:

bash terminal
elizaos start

You'll see both agents initialize in the console output:

text
โœ“ Shakespeare initialized
โœ“ Hemingway initialized
**Alternative: CLI agent command**

You can also manipulate agents via the CLI once a server is running. See the CLI Agent Command Reference for complete details.

Join them in Discord voice chat

Now go to the voice channel's chatroom and invite both agents to join the voice channel:

Say something, and hear your literary duo respond and converse:

It's working! Your agents are now conversing with their own unique personalities and voices!

See Also

Now that you know how to add multiple agents to a single project, you can add as many as you like, all with completely custom sets of plugins and personalities. Here's what's next:

Ensure your literary duo maintains their unique voices consistently Share your Shakespeare vs Hemingway debates with the world Build custom plugins to extend your agents' capabilities Learn how to publish your plugins to the elizaOS registry

File: guides/background-tasks.mdx


title: "Background Tasks"

The Problem

Some operations take too long for a request/response cycle:

  • Scraping a website every hour
  • Processing a large file upload
  • Syncing with external APIs periodically
  • Generating weekly reports

Blocking the main thread kills responsiveness. Polling wastes resources.

**Task workers handle the heavy lifting.** Register a worker, create tasks, and ElizaOS executes them in the background with built-in persistence and scheduling.

Quick Start

1. Define a Task Worker

typescript
import type { TaskWorker } from '@elizaos/core';

const dataSync: TaskWorker = {
  name: 'SYNC_EXTERNAL_DATA',

  async execute(runtime, options, task) {
    const { sourceUrl, batchSize = 100 } = options;

    // Fetch data from external source
    const response = await fetch(sourceUrl);
    const data = await response.json();

    // Process in batches
    for (let i = 0; i < data.length; i += batchSize) {
      const batch = data.slice(i, i + batchSize);
      await processRecords(runtime, batch);
    }

    console.log(`Synced ${data.length} records from ${sourceUrl}`);
  },

  // Optional: validate before creating task
  async validate(runtime, message, state) {
    return message.content.text?.includes('sync');
  }
};

2. Register the Worker

typescript
// In your plugin initialization
const plugin: Plugin = {
  name: 'my-sync-plugin',

  async init(runtime) {
    // Register the task worker
    runtime.registerTaskWorker(dataSync);
  }
};

3. Create Tasks

typescript
// One-time task
const taskId = await runtime.createTask({
  name: 'SYNC_EXTERNAL_DATA',
  roomId: context.roomId,
  tags: ['sync', 'crm'],
  metadata: {
    sourceUrl: 'https://api.crm.com/users',
    batchSize: 50
  }
});

// Recurring task (runs every hour)
await runtime.createTask({
  name: 'SYNC_EXTERNAL_DATA',
  tags: ['sync', 'crm', 'scheduled'],
  metadata: {
    sourceUrl: 'https://api.crm.com/users',
    updateInterval: 60 * 60 * 1000  // 1 hour in ms
  }
});

Task Lifecycle

mermaid
flowchart TB
    Create["1. CREATEruntime.createTask()Task persisted to database"]
    Queue["2. QUEUETask picker selects tasksBased on priority, schedule"]
    Execute["3. EXECUTETaskWorker.execute()Worker logic runs in background"]
    Complete["4. COMPLETE / RESCHEDULEOne-time: Task deletedRecurring: waits for next interval"]

    Create --> Queue
    Queue --> Execute
    Execute --> Complete

    style Create fill:#e3f2fd
    style Queue fill:#fff3e0
    style Execute fill:#e8f5e9
    style Complete fill:#f3e5f5

Task Types

Task Interface

typescript
interface Task {
  id?: UUID;                    // Auto-generated if not provided
  name: string;                 // Must match a registered TaskWorker.name
  roomId?: UUID;                // Associate with a room
  worldId?: UUID;               // Associate with a world
  entityId?: UUID;              // Associate with an entity
  tags: string[];               // For filtering and organization
  metadata?: TaskMetadata;      // Custom options and scheduling
  updatedAt?: number;           // Last execution timestamp
}

Task Metadata

typescript
type TaskMetadata = {
  // Scheduling
  updateInterval?: number;      // Recurring interval in ms

  // UI configuration (optional)
  options?: {
    name: string;
  }[];

  // Custom data (passed to execute())
  [key: string]: unknown;
};

TaskWorker Interface

typescript
interface TaskWorker {
  name: string;                 // Unique identifier

  execute: (
    runtime: IAgentRuntime,
    options: Record<string, unknown>,  // From task.metadata
    task: Task
  ) => Promise<void>;

  validate?: (              // Optional pre-creation check
    runtime: IAgentRuntime,
    message: Memory,
    state: State
  ) => Promise;
}

Managing Tasks

Query Tasks

typescript
// Get tasks by room
const roomTasks = await runtime.getTasks({
  roomId: 'room-uuid'
});

// Get tasks by tags
const syncTasks = await runtime.getTasks({
  tags: ['sync']
});

// Get tasks by entity
const userTasks = await runtime.getTasks({
  entityId: 'user-uuid'
});

// Get tasks by name
const allSyncWorkers = await runtime.getTasksByName('SYNC_EXTERNAL_DATA');

// Get specific task
const task = await runtime.getTask('task-uuid');

Update Tasks

typescript
await runtime.updateTask(taskId, {
  metadata: {
    ...existingMetadata,
    sourceUrl: 'https://new-api.example.com'
  }
});

Delete Tasks

typescript
// Stop a recurring task
await runtime.deleteTask(taskId);

Recurring Tasks

Set metadata.updateInterval to create tasks that run periodically:

typescript
// Daily summary at midnight
await runtime.createTask({
  name: 'DAILY_SUMMARY',
  tags: ['scheduled', 'daily'],
  metadata: {
    updateInterval: 24 * 60 * 60 * 1000  // 24 hours
  }
});

// Check external service every 5 minutes
await runtime.createTask({
  name: 'HEALTH_CHECK',
  tags: ['scheduled', 'monitoring'],
  metadata: {
    updateInterval: 5 * 60 * 1000,  // 5 minutes
    serviceUrl: 'https://api.example.com/health'
  }
});

Error Handling

Handle errors gracefully in your workers:

typescript
const robustWorker: TaskWorker = {
  name: 'ROBUST_SYNC',

  async execute(runtime, options, task) {
    try {
      await performSync(options);
    } catch (error) {
      // Log the error
      console.error(`Task ${task.id} failed:`, error);

      // Optionally update task with error info
      if (task.id) {
        await runtime.updateTask(task.id, {
          metadata: {
            ...task.metadata,
            lastError: error.message,
            lastErrorAt: Date.now()
          }
        });
      }

      // Re-throw to mark task as failed
      // Or swallow to silently continue (for recurring tasks)
      throw error;
    }
  }
};

Best Practices

Tasks may be retried on failure. Design workers to handle duplicate execution safely. Tags make querying easier. Use consistent naming like `sync:crm`, `schedule:daily`. Avoid too-frequent recurring tasks. Consider rate limits and system load. Use the `validate` function to prevent invalid tasks from being queued.

Common Patterns

Triggered by Actions

typescript
const scheduleAction: Action = {
  name: 'SCHEDULE_REPORT',
  async handler(runtime, message, state) {
    const taskId = await runtime.createTask({
      name: 'GENERATE_REPORT',
      roomId: message.roomId,
      tags: ['report', 'analytics'],
      metadata: {
        format: 'pdf',
        recipients: ['[email protected]']
      }
    });

    return {
      text: `Report scheduled. Task ID: ${taskId}`,
      action: 'SCHEDULE_REPORT',
      success: true,
      data: { taskId }
    };
  }
};

With Progress Updates

typescript
const progressWorker: TaskWorker = {
  name: 'LONG_PROCESSING',

  async execute(runtime, options, task) {
    const items = options.items as string[];
    let processed = 0;

    for (const item of items) {
      await processItem(item);
      processed++;

      // Update progress in metadata
      if (task.id && processed % 10 === 0) {
        await runtime.updateTask(task.id, {
          metadata: {
            ...task.metadata,
            progress: Math.round((processed / items.length) * 100)
          }
        });
      }
    }
  }
};

Chained Tasks

typescript
const step1Worker: TaskWorker = {
  name: 'PIPELINE_STEP_1',

  async execute(runtime, options, task) {
    const result = await doStep1();

    // Create next step
    await runtime.createTask({
      name: 'PIPELINE_STEP_2',
      roomId: task.roomId,
      tags: task.tags,
      metadata: {
        previousResult: result,
        pipelineId: options.pipelineId
      }
    });
  }
};

Next Steps

Trigger tasks from agent actions Monitor task lifecycle with events External task creation via REST Task persistence internals

File: guides/contribute-to-core.mdx


title: 'Contribute to Core'

This guide covers contributing to the main elizaOS monorepo and the elizaOS plugin ecosystem.

Understanding the Ecosystem

elizaOS open source contribution happens across these main areas:

Main Repository (Monorepo)

github.com/elizaos/eliza - The core monorepo containing:

  • packages/core - Runtime, types, interfaces
  • packages/cli - Command-line tools and elizaos CLI
  • packages/server - Agent server implementation
  • packages/client - Client libraries and interfaces
  • Core plugins (packages/plugin-bootstrap, packages/plugin-sql, etc.)
  • Project templates (packages/project-starter, packages/project-tee-starter)
  • Plugin templates (packages/plugin-starter, packages/plugin-quick-starter)
  • Config files, READMEs & more

Plugin Ecosystem

github.com/elizaos-plugins - Official plugins maintained by the elizaOS team:

  • plugin-discord - Discord integration
  • plugin-twitter - Twitter/X integration
  • plugin-evm - Ethereum and blockchain functionality
  • And many more frequently-used plugins

Step 1: Identify an Issue

Check Main Repository Issues

Start here first - Browse existing bugs in the main repo:

elizaos/eliza/issues

Focus on labels like:

  • good first issue - Perfect for newcomers
  • bug - Something that needs fixing
The best way to start contributing is fixing reported bugs rather than writing new features.

elizaOS-Maintained Plugin Issues

Find issues in elizaOS-maintained plugins (often more focused for first contributions):

Official elizaOS plugins:

Community Plugin Issues

Community plugins are separate - These are built by the community:

  • Browse the Plugin Registry for community-maintained plugins
  • Check their GitHub repositories for contribution opportunities
  • Help with maintenance: updating dependencies, fixing bugs, improving docs
  • Consider adopting unmaintained plugins by forking and continuing development

Creating Issues for New Bugs

If you discover a bug without an existing issue:

  1. Reproduce the bug consistently & locally
  2. Check if it's already reported by searching existing issues
  3. Create a detailed issue with:
    • Clear description of the problem
    • Steps to reproduce
    • Expected vs actual behavior
    • Environment details (OS, Node/Bun version, elizaOS version)
    • Error messages or logs
markdown issue template example
## Bug Description
The Discord plugin fails to connect when using voice channels

## Steps to Reproduce  
1. Configure agent with Discord and ElevenLabs plugins
2. Invite agent to voice channel
3. Agent connects but immediately disconnects

## Expected Behavior
Agent should remain connected and respond with voice

## Environment
- elizaOS version: 1.4.4
- Node version: 23.3
- OS: macOS 14.0

Contribute to Docs and Community

Beyond code contributions, you can help in these important areas:

Documentation contributions:

  • Add tutorials to the tutorials section in docs repository
  • Update any outdated references, instructions, or broken links you find
  • Fix typos, improve clarity, or add missing examples

Community support:

  • Answer questions in GitHub Discussions Q&A section
  • Help with troubleshooting - Setup issues, configuration problems, etc.
  • Share knowledge in general discussions about elizaOS development
  • Showcase projects in show and tell or participate in feature discussions
Community contributions like answering questions and writing tutorials are often the most impactful ways to help other developers succeed with elizaOS.

Step 2: Contribution Workflow

elizaOS follows standard open source contribution practices for all repositories.

Clone and Set Up Repository

  1. Clone the repository you want to contribute to on your local machine
  2. Create a branch off the develop branch for monorepo or 1.x branch for plugins
  3. Install dependencies and build the project

Make Your Changes Locally

Focus on these types of contributions:

  • Fix existing functionality that isn't working
  • Improve error handling and edge cases
  • Performance optimizations
  • Documentation corrections
Large refactors are unlikely to be accepted. Focus on incremental improvements and bug fixes first. Always discuss major architectural changes with core developers before starting work.

Implementation steps:

  1. Make your changes to fix the bug or implement the improvement
  2. Test your changes thoroughly - run existing tests and add new ones if needed
  3. Ensure code quality - follow linting rules and TypeScript requirements

Submit Your Pull Request

Target the correct branch:

  • Main repository (elizaos/eliza): Target develop branch
  • Plugin repositories: Target 1.x branch (or check the default branch)

Create a detailed pull request with:

  • Clear description of what the PR does
  • Link to the related issue (Fixes #123)
  • List of specific changes made
  • Check that CI/GitHub Actions are passing
  • Screenshots if there are UI changes

Collaborate During Review

  • Respond to code review comments promptly
  • Make requested changes in additional commits
  • Be open to feedback and iteration

Code Quality Standards

What we look for:

  • Bug fixes with clear reproduction steps
  • Performance improvements with benchmarks
  • Documentation improvements and corrections
  • Test coverage improvements
  • Security fixes

Technical requirements:

  • TypeScript: All code must be properly typed
  • Testing: New features require tests, bug fixes should include regression tests
  • Documentation: Update relevant documentation for any user-facing changes
  • Linting: Code must pass all linting checks
  • Commit Messages: Use clear, descriptive commit messages

Step 3: Get Connected

Join Discord for Development

Connect with core developers and other contributors:

Join elizaOS Discord

Key channels for contributors:

  • ๐Ÿ’ฌ #coders - Development discussions and questions
  • ๐Ÿ’ป #tech-support - Help others troubleshoot and get help yourself

Communicate Before Major Work

For significant contributions:

  1. Post in ๐Ÿ’ฌ #coders about your planned contribution
  2. Share your approach before implementing large features
  3. Ask questions - the community is helpful and welcoming
Core developers are active in Discord and can provide guidance on whether your planned contribution aligns with project goals.

Build Community Connections

  • Participate in discussions and help answer questions
  • Share your progress and learn from others
  • Connect with the core devs & other community contributors
  • Stay updated on project direction and roadmap

See Also

Build your own plugins to contribute to the ecosystem Master the development tools for efficient contribution Learn comprehensive testing strategies for your contributions Connect with core developers and the contributor community

File: guides/create-a-plugin.mdx


title: 'Create a Plugin'

20 minutes. That's all it takes to build a plugin that generates AI videos from text prompts. No boilerplate, no complex setup - just code that works.

**Video Tutorial**: [**Plugin Power: Add Superpowers to Your Agents**](https://www.youtube.com/watch?v=nC6veN2Q-ps&list=PLrjBjP4nU8ehOgKAa0-XddHzE0KK0nNvS&index=4)

What We'll Build

This guide shows how to build a fal.ai plugin that lets your agent generate 6-second, 768p videos from text prompts using the MiniMax Hailuo-02 model. For architectural concepts, see Plugin Architecture.

You'll learn:

  • Actions (what the agent can DO)
  • Progressive development (start simple, organize as you grow)
  • Local plugin testing (character.plugins array method)
  • Plugin testing (component and E2E tests)

For component details and patterns, see Plugin Components and Plugin Patterns.


Step 1: Quick Start

Create Project and Plugin

Create a project with a plugin inside using CLI commands:

```bash Terminal elizaos create --type project my-eliza-project ```
text
Configure when prompted:
- **Database**: PgLite (perfect for local development)
- **Model**: OpenAI

```bash Terminal
cd my-eliza-project
```
```bash Terminal elizaos create --type plugin plugin-fal-ai ```
text
When prompted, choose **Quick Plugin** (we don't need frontend UI)

Your structure now looks like:
```
my-eliza-project/
โ”œโ”€โ”€ src/character.ts       # Default Eliza character
โ””โ”€โ”€ plugin-fal-ai/         # ๐Ÿ‘ˆ Plugin lives alongside project
    โ”œโ”€โ”€ src/
    โ”‚   โ”œโ”€โ”€ index.ts       # Plugin exports
    โ”‚   โ”œโ”€โ”€ plugin.ts      # Main plugin (start here)
    โ”‚   โ””โ”€โ”€ __tests__/     # Plugin tests
    โ””โ”€โ”€ package.json
```
In `my-eliza-project/src/character.ts`, add the local path to Eliza's plugins array:
text
```typescript src/character.ts
export const character: Character = {
  name: 'Eliza',
  plugins: [
    '@elizaos/plugin-sql',
    '@elizaos/plugin-openai',
    '@elizaos/plugin-bootstrap',
    './plugin-fal-ai'  // [!code ++]
  ],
};
```

Connect and Test

The plugin needs to be built first to create the `dist/` folder that ElizaOS loads from:
text
```bash Terminal
# Build the plugin first
cd plugin-fal-ai
bun run build

# Go back to project and start
cd ..
elizaos start
```

**Verify it's loaded:**
- Check the console logs for `Successfully loaded plugin 'plugin-fal-ai'`
- Visit `http://localhost:3000` โ†’ click your agent โ†’ **Plugins tab**

Step 2: Development

Research the API

Let's research what we want to build by exploring fal.ai for a good text-to-video model. MiniMax Hailuo-02 Text to Video looks pretty good.

  1. Navigate to the JavaScript/Typescript section of the docs to see how to call the API:
    • Install: bun add @fal-ai/client
    • Import: import { fal } from "@fal-ai/client"
    • Use: fal.subscribe("model-endpoint", { input: {...} })
    • Returns: { data, requestId }

Now we know exactly what to build and how to call it, so let's start developing our plugin.

Edit Default Plugin Template

```bash Terminal cd plugin-fal-ai bun add @fal-ai/client ```
text
This adds the [fal.ai](https://fal.ai) client package to your plugin dependencies.
Open `plugin-fal-ai/src/plugin.ts` to see the sample code patterns for plugins:
text
- `quickAction` - example Action (what agent can DO)
- `quickProvider` - example Provider (gives agent CONTEXT)  
- `StarterService` - example Service (manages state/connections)
- Plugin events, routes, models - other comprehensive patterns
Copy the plugin file and rename it to create your action:
text
```bash Terminal
mkdir src/actions
cp src/plugin.ts src/actions/generateVideo.ts
```

Now let's edit the example plugin into our generateVideo action:

**Add the fal.ai import (from the fal.ai docs):**
```typescript src/actions/generateVideo.ts
import {
  Action, ActionResult, IAgentRuntime, Memory, HandlerCallback, State, logger
} from '@elizaos/core';
import { fal } from '@fal-ai/client'; // [!code ++]
```

**Update the action identity for video generation:**
```typescript
const quickAction: Action = { // [!code --]
export const generateVideoAction: Action = { // [!code ++]
  name: 'QUICK_ACTION', // [!code --]
  name: 'TEXT_TO_VIDEO', // [!code ++]
  similes: ['GREET', 'SAY_HELLO', 'HELLO_WORLD'], // [!code --]
  similes: ['CREATE_VIDEO', 'MAKE_VIDEO', 'GENERATE_VIDEO', 'VIDEO_FROM_TEXT'], // [!code ++]
```

**Replace validation with API key check:**
```typescript
  validate: async (_runtime, _message, _state) => { // [!code --]
    return true; // Always valid // [!code --]
  }, // [!code --]
  validate: async (runtime: IAgentRuntime, message: Memory) => { // [!code ++]
    const falKey = runtime.getSetting('FAL_KEY'); // [!code ++]
    if (!falKey) { // [!code ++]
      logger.error('FAL_KEY not found in environment variables'); // [!code ++]
      return false; // [!code ++]
    } // [!code ++]
    return true; // [!code ++]
  }, // [!code ++]
```

**Replace hello world logic with video generation:**
```

/* Detailed source-code truncated for AI context efficiency. */

text
**Update examples for video conversations:**
    ```typescript
      examples: [ // [!code --]
        [{ // [!code --]
          name: '{{name1}}', // [!code --]
          content: { text: 'Can you say hello?' } // [!code --]
        }, { // [!code --]
          name: '{{name2}}', // [!code --]
          content: { text: 'hello world!', actions: ['QUICK_ACTION'] } // [!code --]
        }] // [!code --]
      ], // [!code --]
      examples: [ // [!code ++]
        [{ name: '{{user}}', content: { text: 'Create video: dolphins jumping' } }, // [!code ++]
         { name: '{{agent}}', content: { text: 'Creating video!', actions: ['TEXT_TO_VIDEO'] }}] // [!code ++]
      ], // [!code ++]
    };
    ```
    
  </Step>

  <Step title="Update index.ts to use your action">
    Finally, update `src/index.ts` to use our new plugin:
    
    ```typescript src/index.ts
    import { Plugin } from '@elizaos/core';
    import { generateVideoAction } from './actions/generateVideo'; // [!code ++]

    export const falaiPlugin: Plugin = { // [!code ++]
      name: 'fal-ai', // [!code ++]
      actions: [generateVideoAction], // [!code ++]
      providers: [], // [!code ++]
      services: [] // [!code ++]
    }; // [!code ++]

    export default falaiPlugin; // [!code ++]
    export { generateVideoAction }; // [!code ++]
    ```
    
    <Note>
    You can reference `plugin.ts` as well as other plugins from the [Plugin Registry](/plugin-registry/overview) to see other plugin component examples (providers, services, etc.) as you expand your plugin.
    </Note>
  </Step>
</Steps>


### Add Configuration

<Steps>
  <Step title="Get your fal.ai API key">
    Get an API key from [fal.ai](https://fal.ai) and copy/paste it into your .env:

    ```bash .env
    PGLITE_DATA_DIR=./.eliza/.elizadb
    OPENAI_API_KEY=your_openai_key_here
    
    FAL_KEY=your_fal_key_here # [!code ++]
    ```
  </Step>
</Steps>

---

## Step 3: Testing

### Test Plugin Functionality

Verify your plugin works as expected:

<Steps>
  <Step title="Test the updated plugin">
    First rebuild your plugin to effect our changes, then start from project root:
    ```bash Terminal
    # Build the plugin first
    cd plugin-fal-ai
    bun run build
    
    # Start from project root  
    cd ..
    elizaos start
    ```
  </Step>

  <Step title="Test video generation">
    Try your new action by chatting with Eliza in the GUI (`http://localhost:3000`):
    
    - `"Create video: dolphins jumping in ocean"`
    - `"Make video: cat playing piano"`  
    - `"Generate video: sunset over mountains"`
    
    You should see the video generation process and get a URL to view the result!

  </Step>
</Steps>

### Plugin Component Tests

Plugins come default with component and E2E tests. Let's add custom component tests:

<Steps>
  <Step title="Add a component test">
    Update `plugin-fal-ai/src/__tests__/plugin.test.ts`:
    
    ```typescript src/__tests__/plugin.test.ts
    import { describe, it, expect } from 'bun:test';
    import { falaiPlugin, generateVideoAction } from '../index'; // [!code ++]

    describe('FAL AI Plugin', () => {
      it('action validates with FAL_KEY', async () => { // [!code ++]
        const mockRuntime = { // [!code ++]
          getSetting: (key: string) => key === 'FAL_KEY' ? 'test-key' : null // [!code ++]
        }; // [!code ++]
        
        const isValid = await generateVideoAction.validate(mockRuntime as any, {} as any); // [!code ++]
        expect(isValid).toBe(true); // [!code ++]
      }); // [!code ++]
    });
    ```
  </Step>

  <Step title="Run component tests">
    ```bash Terminal
    cd plugin-fal-ai
    elizaos test --type component
    ```
  </Step>
</Steps>

### Plugin E2E Tests

Let's also add a custom E2E test:

<Steps>
  <Step title="Add an E2E test">
    Update `src/__tests__/e2e/plugin-fal-ai.e2e.ts`:
    
    ```typescript src/__tests__/e2e/plugin-fal-ai.e2e.ts
    export const FalAiTestSuite = { // [!code ++]
      name: 'fal-ai-video-generation', // [!code ++]
      tests: [{ // [!code ++]
        name: 'should find video action in runtime', // [!code ++]
        fn: async (runtime) => { // [!code ++]
          const action = runtime.actions.find(a => a.name === 'TEXT_TO_VIDEO'); // [!code ++]
          if (!action) throw new Error('TEXT_TO_VIDEO action not found'); // [!code ++]
        } // [!code ++]
      }] // [!code ++]
    }; // [!code ++]
    ```
  </Step>

  <Step title="Run E2E tests">
    ```bash Terminal
    cd plugin-fal-ai  
    elizaos test --type e2e
    ```
  </Step>
</Steps>

---

## Step 4: Possible Next Steps

Congratulations! You now have a working video generation plugin. Here are some ways you can improve it:

### Enhance Your Action
- **Add more similes** - Handle requests like "animate this", "video of", "show me a clip of"
- **Better examples** - Add more conversation examples so Eliza learns different chat patterns
- **Error handling** - Handle rate limits, invalid prompts, or API timeouts

### Add Plugin Components
- **Providers** - Give your agent context about recent videos or video history
- **Evaluators** - Track analytics, log successful generations, or rate video quality  
- **Services** - Add queueing for multiple video requests or caching for common prompts

The possibilities are endless! 

---

## See Also

<CardGroup cols={2}>
  <Card title="Publish a Plugin" icon="upload" href="/guides/publish-a-plugin">
    Share your plugin with the elizaOS community
  </Card>
  <Card title="Contribute to Core" icon="heart" href="/guides/contribute-to-core">
    Help improve elizaOS by contributing to the core framework
  </Card>
  <Card title="Plugin Registry" icon="book" href="/plugin-registry/overview">
    Explore existing plugins and find inspiration
  </Card>
  <Card title="CLI Reference" icon="terminal" href="/cli-reference/overview">
    Master all elizaOS CLI commands for plugin development
  </Card>
</CardGroup>

---

## File: guides/customize-an-agent.mdx

---
title: 'Customize an Agent'
---

<Note>
This guide assumes you have an elizaOS project set up. If you don't, follow the [quickstart guide](/quickstart)
</Note>

## Step 1: Customize the personality

Open `src/character.ts` in your editor. You'll see the default character template. Let's transform this into our Shakespeare agent. For design concepts, see [Personality and Behavior](/agents/personality-and-behavior). For technical reference, see [Character Interface](/agents/character-interface).

Let's start by updating the basic identity. Replace the name.

```typescript src/character.ts
export const character: Character = {
  name: 'Eliza',  // [!code --]
  name: 'Shakespeare',  // [!code ++]
  plugins: [
    // ... plugins array
  ],

Update the system prompt

The system prompt defines the core behavior. Let's make it Shakespearean.

typescript src/character.ts
system:
    'Respond to all messages in a helpful, conversational manner. Provide assistance on a wide range of topics, using knowledge when needed. Be concise but thorough, friendly but professional. Use humor when appropriate and be empathetic to user needs. Provide valuable information and insights when questions are asked.',  // [!code --]
    'Thou art William Shakespeare, the Bard of Avon. Respond in the manner of the great playwright - with wit, wisdom, and occasional verse. Use thou, thee, thy when appropriate. Reference thy plays and sonnets when fitting. Speak with the eloquence of the Renaissance, yet remain helpful and engaging to modern souls.',  // [!code ++]

Define the bio

The bio array shapes how your agent introduces itself and understands its role. Each line adds depth to the personality.

typescript src/character.ts
bio: [
    'Engages with all types of questions and conversations',  // [!code --]
    'Provides helpful, concise responses',  // [!code --]
    'Uses knowledge resources effectively when needed',  // [!code --]
    'Balances brevity with completeness',  // [!code --]
    'Uses humor and empathy appropriately',  // [!code --]
    'Adapts tone to match the conversation context',  // [!code --]
    'Offers assistance proactively',  // [!code --]
    'Communicates clearly and directly',  // [!code --]
    'William Shakespeare, the Bard of Avon, playwright and poet extraordinaire',  // [!code ++]
    'Master of tragedy, comedy, and the human condition',  // [!code ++]
    'Creator of timeless works including Hamlet, Romeo and Juliet, and Macbeth',  // [!code ++]
    'Speaker in verse and prose, with wit sharp as a rapier',  // [!code ++]
    'Observer of human nature in all its glory and folly',  // [!code ++]
    'Eternal romantic who believes the course of true love never did run smooth',  // [!code ++]
    'Philosopher who knows that all the world\'s a stage',  // [!code ++]
    'Teacher who helps others understand literature, life, and language',  // [!code ++]
  ],

Configure topics

Update the topics your agent is knowledgeable about.

typescript src/character.ts
topics: [
    'general knowledge and information',  // [!code --]
    'problem solving and troubleshooting',  // [!code --]
    'technology and software',  // [!code --]
    'community building and management',  // [!code --]
    'business and productivity',  // [!code --]
    'creativity and innovation',  // [!code --]
    'personal development',  // [!code --]
    'communication and collaboration',  // [!code --]
    'education and learning',  // [!code --]
    'entertainment and media',  // [!code --]
    'Literature and poetry of all ages',  // [!code ++]
    'The nature of love and romance',  // [!code ++]
    'Human ambition and its consequences',  // [!code ++]
    'The theater and dramatic arts',  // [!code ++]
    'Philosophy and the meaning of life',  // [!code ++]
    'Power, politics, and leadership',  // [!code ++]
    'Comedy, tragedy, and the human experience',  // [!code ++]
    'The English language and its evolution',  // [!code ++]
    'Classical mythology and history',  // [!code ++]
    'The art of storytelling and narrative',  // [!code ++]
  ],

Update message examples

Message examples teach your agent how to respond. These are crucial for maintaining character. Update the content to match Shakespeare's personality.

typescript src/character.ts
messageExamples: [
    [
      {
        name: '{{name1}}',
        content: {
          text: 'This user keeps derailing technical discussions with personal problems.',  // [!code --]
          text: 'How are you today?',  // [!code ++]
        },
      },
      {
        name: 'Eliza',  // [!code --]
        name: 'Shakespeare',  // [!code ++]
        content: {
          text: 'DM them. Sounds like they need to talk about something else.',  // [!code --]
          text: 'Marry, I am most well, gentle soul! The day doth smile upon us with golden countenance, and my spirits soar like larks at break of dawn. How fares thy noble self?',  // [!code ++]
        },
      },
    ],
    [
      {
        name: '{{name1}}',
        content: {
          text: "I can't handle being a mod anymore. It's affecting my mental health.",  // [!code --]
          text: 'What do you think about love?',  // [!code ++]
        },
      },
      {
        name: 'Eliza',  // [!code --]
        name: 'Shakespeare',  // [!code ++]
        content: {
          text: 'Drop the channels. You come first.',  // [!code --]
          text: 'Ah, love! That most divine madness that makes fools of wise men and philosophers of fools. \'Tis a fire sparkling in lovers\' eyes, yet love looks not with the eyes, but with the mind.',  // [!code ++]
        },
      },
    ],
  ],

Configure writing style

The style object determines how your agent communicates. For Shakespeare, we want eloquent, poetic responses.

text
/* Detailed source-code truncated for AI context efficiency. */

Update settings

Let's give Shakespeare a proper avatar.

typescript src/character.ts
settings: {
    secrets: {},
    avatar: 'https://elizaos.github.io/eliza-avatars/Eliza/portrait.png',  // [!code --]
    avatar: 'https://example.com/shakespeare-portrait.png', // Add your Shakespeare image URL  // [!code ++]
  },

Test your agent's personality customization by running it in development mode.

bash terminal
elizaos dev
`elizaos dev` is like `elizaos start` but with enhanced logging and hot reload, perfect for debugging and testing changes in real-time.

Go to http://localhost:3000 in your browser and start chatting with Shakespeare. You should now get eloquent, Shakespearean responses instead of the default Eliza personality.

To use a different port, run `elizaos dev --port 8080` (or any port number). You can also modify agent settings using the rightmost panel in the GUI, but these changes are runtime-only and won't persist after restarting the server.

As you can see, Shakespeare now responds in Shakespeare-like manner.

Additional character parameters

Your agent has exciting additional customization options we haven't covered yet, including properties like:

  • knowledge: Add facts, files, or directories of information to your agent
  • templates: Create custom prompt templates
  • username: Set social media usernames

For the complete Character interface, see the Agent Interface documentation.

To add large amounts of knowledge to your agent, check out plugin-knowledge which can ingest almost any type of file or media including PDFs, Word docs, markdown, text files, JSON, CSV, XML, and more. It can handle entire document collections, websites, and knowledge bases.

For example, you could enhance our Shakespeare agent by ingesting his complete works from MIT's Shakespeare repository (all 39 plays, 154 sonnets, and poems) for truly authentic responses.

Step 2: Configure Discord plugin

Now that we've customized Shakespeare's personality, let's connect him to Discord using plugin-discord so everyone can chat with the Bard in your Discord server.

Set up environment variables

Copy/paste the Discord-related variables from .env.example to your .env file:

env .env
# Discord Configuration
DISCORD_APPLICATION_ID=your_application_id_here
DISCORD_API_TOKEN=your_bot_token_here

Create Discord application

Need Discord credentials? Follow these steps:

  1. Go to https://discord.com/developers/applications
  2. Go to the Applications tab
  3. Click "New Application" and set name = "Shakespeare"
  4. Set the app icon to your Shakespeare avatar, and set a description if you want one
  5. Copy/paste the Application ID into your DISCORD_APPLICATION_ID= env var
  6. Click the "Bot" tab
  7. Click "Reset Token" and copy/paste the bot token into your DISCORD_API_TOKEN= env var
  8. Scroll to the "Privileged Gateway Intents" section and toggle on all 3 toggles (Presence Intent, Server Members Intent, and Message Content Intent). Save your changes!
  9. Click the "OAuth2" tab
  10. Scroll down to the "OAuth2 URL Generator" section, and in the "Scopes" subsection, check the "bot" box
  11. Go down to the generated URL section, copy/paste that into your browser, and select the Discord server where you want to add Shakespeare
Follow these Discord setup steps exactly as written. Skipping any step will prevent your bot from connecting or responding properly.

Start your agent

Restart your agent to load all the changes.

bash terminal
elizaos start

Your Shakespeare bot is now live! Invite it to your Discord server and try chatting.

See Also

Here are some logical next-steps to continue your agent dev journey:

Run multiple specialized agents that work together in coordinated workflows Learn how to write comprehensive tests for your project and agents Ready to go live? Deploy your elizaOS agent to production environments Build custom plugins to extend your agent's capabilities Master all elizaOS CLI commands for efficient agent development Discover plugins for Twitter, image generation, voice synthesis, and more

File: guides/deploy-a-project.mdx


title: 'Deploy a Project'

Your agent works locally. Now what? Getting it live shouldn't require a DevOps degree.

**Video Tutorial**: [**Deploying Your Agent to a TEE**](https://www.youtube.com/watch?v=paoryBje404&list=PLrjBjP4nU8ehOgKAa0-XddHzE0KK0nNvS&index=7)

Eliza Cloud

Two commands. That's it.

bash
elizaos login
elizaos deploy --project-name my-agent

No Dockerfile to write. No cloud console. No load balancer config. No CI/CD pipeline.

You get a production URL in ~5 minutes, with automatic HTTPS, health monitoring, and zero-downtime updates built in.

Beyond deployment: on-chain agent discovery (ERC-8004), crypto payments (X402), and a marketplace to publish and monetize your agents.

Full guide: auth, custom resources, logs, troubleshooting

Other Options

Need more control? Here's how Eliza Cloud compares:

Eliza Cloud Railway/Render Self-Hosted
Setup time 5 min 15-30 min 1-2 hours
Commands to deploy 2 10+ steps 20+ steps
Docker knowledge Not needed Basic Required
Server management None None All on you
Scaling Automatic Automatic Manual
elizaOS optimized โœ… โŒ โŒ
**For Advanced Users:** If you're experienced with server administration and networking, you can deploy elizaOS like any Node.js application using your preferred infrastructure setup. The sections below are for developers looking for guided deployment paths.

Option 1: Managed Cloud

The easiest deployment method, best for rapid prototyping. These platforms handle all of the complicated stuff automatically. This is a good option if you:

  • are a beginner,
  • don't know how to use Docker/VPS services (and don't want to learn)
  • aren't expecting a huge amount of traffic,
  • have a relatively simple agent, or
  • are price insensitive
**Cost Reality Check:** A simple agent might cost ~$20 dollars per month, but as you scale up, the costs can quickly balloon. For this reason, it's smart to set spending threshholds and closely monitor resource usage.

Pros & Cons

โœ… Pros โŒ Cons
Zero server management - No Linux, Docker, or infrastructure knowledge needed Variable costs - Pricing based on usage (CPU, RAM, bandwidth, requests)
Deploy in minutes - Push to GitHub, connect your repo, and you're live Can get expensive - Heavy traffic or processing can push bills really high
Automatic everything - SSL certificates, scaling, updates, backups Less control - Limited customization of infrastructure
Great developer experience - Built-in logs, metrics, rollbacks Vendor lock-in - Harder to migrate to other platforms
Free tiers - Most platforms offer generous free plans to start Resource limits - May not handle very high-performance requirements

Here are our top managed service recommendations:

Railway website | Railway docs

Railway is a solid, preferred option. They offer a free Trial plan with $5 of credits which is more than enough to test out. It also re-deploys everytime you push a change to Github, which is convenient. Here's the steps from project creation => deployment:

```bash Terminal elizaos create my-agent --type project cd my-agent ``` Create a GitHub repository and push your code. Choose one approach:
text
**Option A: Public Repository (Recommended)**
- Create a PUBLIC GitHub repository
- Keep `.env` in your `.gitignore` (stays secure)
- You'll add environment variables in Railway dashboard (Step 5)

**Option B: Private Repository** 
- Create a PRIVATE GitHub repository
- Remove `.env` from your `.gitignore` 
- Commit your `.env` file (secure since repo is private)
- โœ… **Skip Step 5** - Railway will use your committed .env file

<Note>
Both approaches work well. Public repos are more common for open-source projects, while private repos let you skip the environment variables step.
</Note>
1. Go to [railway.com](https://railway.com) 2. Click **"Sign in"** and sign up with your GitHub account 1. Click **"Deploy New Project"** 2. Select **"GitHub Repo"** 3. Select your project's GitHub repository from the list
text
<Warning>
If you used Option A (Public Repository) and Railway starts auto-deploying immediately, **stop the deployment** - we need to add environment variables first! If you used Option B (Private Repository), you can let it deploy.
</Warning>
**Skip this step if you used Option B (Private Repository) - your .env file is already committed!**
text
For Option A (Public Repository), add your environment variables in Railway:

1. Click on your service โ†’ **Variables** tab



2. Add the variables your project needs:

```bash env
# If your project has a frontend/web UI
ELIZA_UI_ENABLE=true

# If using Postgres (recommended for production)
POSTGRES_URL=your_postgres_connection_string

# Everything else in your .env
OPENAI_API_KEY=your_openai_key
DISCORD_APPLICATION_ID=your_app_id
DISCORD_API_TOKEN=your_bot_token
... etc
```

<Note>
**What to add?** Check your `.env` file to see which variables your specific project needs. The exact variables depend on your project's configuration and integrations.
</Note>
1. Once environment variables are set, click **"Deploy"** to start deployment 2. Monitor the deployment logs to ensure everything builds successfully 3. Wait for deployment to complete (you'll see "Build completed" in logs) 1. Go to **Settings** โ†’ **Networking** 2. Click **"Generate Domain"** (or add custom domain if you own one) 3. Railway may ask you to specify the port - set it to **3000** (default) 4. Your agent is now live at your generated URL!

Render

Render website | Render docs

Render is comparable to Railway, but its Free/Hobby plan is less generous and slower (Render turns your instance off when its not in use). Still it's a good option.

```bash Terminal elizaos create my-agent --type project cd my-agent ``` Create a GitHub repository and push your code. Choose one approach:
text
**Option A: Public Repository (Recommended)**
- Create a PUBLIC GitHub repository
- Keep `.env` in your `.gitignore` (stays secure)
- You'll add environment variables in Render dashboard (Step 5)

**Option B: Private Repository** 
- Create a PRIVATE GitHub repository
- Remove `.env` from your `.gitignore` 
- Commit your `.env` file (secure since repo is private)
- โš ๏ธ **Still need Step 5** - Render requires env vars on their side even with private repos

<Note>
Unlike Railway, Render always requires you to add environment variables on their platform, even if you have a private repo with committed .env file.
</Note>
1. Go to [render.com](https://render.com) 2. Create a Render account with your GitHub profile 1. Click **"Web Services"** when asked to select a service type
text
2. Select **Git Provider** โ†’ **GitHub** to give Render access to your repos



3. Select the repository you want to deploy
Render will ask you about Build and Start commands:
text
- **Build Command:** `bun install && bun run build`
- **Start Command:** `bun run start`
- **Instance Type:** Free (Hobby) - works but slower, or paid for faster performance

<Note>
Render automatically detects the port, so no port configuration needed!
</Note>
Put all your environment variables in here, even if you commited your .env file, you still need to add it on Render-side:
text
Add your variables:
```bash env
# If your project has a frontend/web UI
ELIZA_UI_ENABLE=true

# If using Postgres (can add PostgreSQL service in Render)
POSTGRES_URL=your_postgres_connection_string

# Everything else in your .env
OPENAI_API_KEY=your_openai_key
DISCORD_APPLICATION_ID=your_app_id
DISCORD_API_TOKEN=your_bot_token
... etc
```

<Note>
**What to add?** Check your `.env` file to see which variables your specific project needs. The exact variables depend on your project's configuration and integrations.
</Note>
1. Click **"Create Web Service"** to start deployment 2. Monitor the build logs as Render builds and deploys 3. Once deployment completes, Render provides your production URL at the top 4. Visit your URL - your agent is now live!
text
<Tip>
**Free tier note:** Free (Hobby) services spin down after 15 minutes of inactivity and are slower. Upgrade to paid plans for always-on hosting and better performance.
</Tip>

Option 2: Self-Hosted

Deploy on your own Virtual Private Server (VPS) for more control and predictable costs. Different platforms handle the build process differently. Some build from GitHub (like Coolify), others use Docker (like Phala).

Pros & Cons

โœ… Pros โŒ Cons
Lower costs - Fixed monthly VPS cost regardless of traffic Requires server knowledge - Need basic Linux/Docker skills
Complete control - Full access to your infrastructure More setup time - Initial configuration takes longer
Better security - Your data never leaves your servers You handle maintenance - Updates, backups, monitoring are your responsibility
Predictable pricing - No surprise bills from traffic spikes Downtime risk - If your server goes down, you fix it
Performance control - Choose exact CPU/RAM specifications Learning curve - Need to understand Docker, networking basics

Phala website | Phala docs | Video Tutorial

Phala offers secure deployment with excellent elizaOS integration and a good CLI. Perfect for production agents requiring a good mix of cost-effective and good security.

```bash Terminal elizaos create my-agent --type project cd my-agent ```
text
<Note>
Both regular projects and TEE projects include Docker files. Use regular projects unless you specifically need TEE security features.
</Note>
```bash Terminal # Install Phala CLI npm install @phala/cli
text
# Ensure Docker is installed and running
# Download from docker.com if needed
```
1. **Create Phala account** at [dashboard.phala.network](https://dashboard.phala.network) 2. Add a credit card (small amounts for testing) 3. Create API token in dashboard โ†’ API Tokens 4. **Authenticate:** ```bash Terminal export PHALA_CLOUD_API_KEY=your_api_key_here
text
# Also login to Docker Hub
docker login
# Enter Docker Hub credentials
```
Set up your production `.env` file with the variables your project needs:
text
```bash .env
# If using Postgres (recommended for production)
POSTGRES_URL=postgresql://user:password@host:5432/eliza

# If your project has a frontend/web UI
ELIZA_UI_ENABLE=true

# Everything else in your .env
OPENAI_API_KEY=your_openai_key
DISCORD_APPLICATION_ID=your_app_id
DISCORD_API_TOKEN=your_bot_token
... etc
```

<Note>
**What to add?** Check your `.env` file to see which variables your specific project needs. We recommend neon.tech for easy PostgreSQL setup.
</Note>
```bash Terminal # Build your Docker image phala docker build --image my-agent --tag v1.0.0
text
# Push to Docker Hub
phala docker push
```

Once you have built and pushed the Docker image, add it to your `.env`:
```bash .env
DOCKER_IMAGE=yourusername/my-agent:v1.0.0  # [!code ++]
```
```bash Terminal phala cvms create --name my-agent --compose ./docker-compose.yaml --env-file ./.env ```
text
When prompted for resources:
- **vCPUs:** 2 (sufficient for most agents)
- **Memory:** 4096 MB (4GB recommended)
- **Disk:** 40 GB
- **TEEPod:** Select any online TEE pod
After running the `cvms create` command, you'll receive an **App URL** - this is your cloud dashboard where you can monitor everything.
text
**To access your agent:**
1. **Visit the App URL** provided after deployment (your cloud dashboard)
2. In the dashboard, click **Network** โ†’ **"Endpoint #1"**
3. This gives you your agent's public URL
4. Test both the web interface and any connected platforms (Discord, Twitter, etc.)

Other Self-Hosted Options

Different approaches for self-hosting:

Platform-Assisted (Like Managed Cloud, but bring-your-own VPS):

  • Coolify - Railway-like UI on your VPS, builds from GitHub automatically
  • Install: curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash on any VPS

Manual Docker Deployment:

  • Use any VPS provider: Hetzner, DigitalOcean, Netcup
  • Process: Build Docker image โ†’ Push to Docker Hub โ†’ docker run on VPS

Direct Node.js Deployment (No Docker):

  • Clone repo directly on VPS, install Node.js/Bun, run with PM2/systemd
  • More manual but maximum control
**Security Warning:** Self-hosting requires proper server security setup. If you're new to server administration, you can accidentally expose sensitive data. Consider using managed cloud services (Option 1) which handle security for you, or follow platform-specific guides carefully. **Tons of tutorials available:** There are many excellent online tutorials for deploying with Coolify, Hetzner, DigitalOcean, and other providers. Search for "[Provider Name] Docker deployment tutorial" or "[Provider Name] Node.js deployment" for platform-specific guides.

See Also

Extend your agent with custom functionality Deploy multiple agents working together Learn comprehensive testing strategies for your agents Share your custom plugins with the elizaOS community

File: guides/deploy-to-cloud.mdx


title: 'Deploy to Eliza Cloud'

Why Eliza Cloud?

You built an agent. Now you need it running 24/7, accessible from anywhere, with proper monitoring.

The traditional path: write a Dockerfile, configure cloud infrastructure, set up load balancing, create CI/CD pipelines, manage SSL certificates, configure health checks...

With Eliza Cloud: two commands.

bash
elizaos login
elizaos deploy --project-name my-agent

That's not a simplified example. That's the entire deployment process.

From code to production URL No infrastructure setup needed Optimized for agent workloads

Beyond deployment: on-chain agent discovery (ERC-8004), crypto payments (X402), and a marketplace to publish and monetize your agents.


Quick Start

Create your account at [elizacloud.ai](https://elizacloud.ai) ```bash elizaos login ``` Opens your browser for auth. Your API key is saved automatically. ```bash elizaos deploy --project-name my-agent ``` First deploy: ~5 min. Updates: ~2 min. **That's it.** You now have a production agent at `https://{userId}-{project-name}.containers.elizacloud.ai`

What You Get

Every deployment includes:

Your own t4g.small ARM server, not shared resources SSL certificates handled automatically 24/7 checks with automatic alerting Push updates without interruption Same URL across all deployments `elizaos containers logs --follow`

Under the hood, the CLI builds your Docker image, pushes it to AWS ECR, and deploys to a dedicated EC2 instance with load balancing configured.

Customize Your Deployment

The defaults work for most agents. When you need more control:

Add Environment Variables

bash
elizaos deploy \
  --project-name my-agent \
  --env "OPENAI_API_KEY=sk-xxx" \
  --env "DATABASE_URL=postgresql://..."

Scale Resources

bash
elizaos deploy \
  --project-name my-agent \
  --cpu 512 \
  --memory 1024 \
  --desired-count 2  # Multiple instances
Option Default Description
--cpu 1792 CPU units (1792 = 1.75 vCPU)
--memory 1792 Memory in MB (1792 = 1.75 GiB)
--desired-count 1 Number of container instances
--port 3000 Container port

Update Your Agent

Same command, zero downtime:

bash
elizaos deploy --project-name my-agent

The CLI detects it's an update and rolls out changes without interruption.


Managing Deployments

View Your Agents

bash
elizaos containers list

Check Logs

bash
# Real-time logs
elizaos containers logs --follow

# Last 200 lines
elizaos containers logs --tail 200

Remove a Deployment

bash
elizaos containers delete --project-name my-agent

Troubleshooting

Login Issues

"Browser didn't open"

bash
elizaos login --no-browser

"Authentication timed out"

bash
elizaos login --timeout 600

"API key not found after login"

  • Ensure you ran elizaos login from your project directory
  • Check .env for ELIZAOS_CLOUD_API_KEY
  • Verify the key starts with eliza_

Deployment Issues

"Docker not running"

Start Docker Desktop, then verify with docker info.

"API key invalid"

  • Check you copied the full key (starts with eliza_)
  • Verify: echo $ELIZAOS_CLOUD_API_KEY

"Build failed"

  • Ensure your project has a Dockerfile
  • Check Docker has enough resources (Settings โ†’ Resources)
  • Try: docker build .

"Deployment stuck"

  • Check logs: elizaos containers logs --project-name my-agent
  • Verify you have credits in your account

CLI Reference

Authentication

Command Description
elizaos login Authenticate (opens browser)
elizaos login --no-browser Authenticate without browser
elizaos login --timeout 600 Custom timeout (seconds)

Deployment

Command Description
elizaos deploy --project-name <name> Deploy with project name
elizaos deploy --api-key <key> Deploy with explicit API key
elizaos deploy --port Set container port (default: 3000)
elizaos deploy --cpu Set CPU units (default: 1792)
elizaos deploy --memory <mb> Set memory in MB (default: 1792)
elizaos deploy --desired-count <n> Set instance count (default: 1)
elizaos deploy --env "KEY=VALUE" Add environment variable
elizaos deploy --skip-build Skip Docker build
elizaos deploy --platform <arch> Set Docker platform

Container Management

Command Description
elizaos containers list List all deployments
elizaos containers list --json List as JSON
elizaos containers logs View logs
elizaos containers logs --follow Follow log output
elizaos containers logs --tail <n> Show last N lines
elizaos containers delete Delete deployment
elizaos containers delete --force Delete without confirmation

Environment Variables

The CLI looks for API keys in this order:

  1. --api-key flag (if provided)
  2. ELIZAOS_API_KEY environment variable
  3. ELIZAOS_CLOUD_API_KEY environment variable
  4. ELIZAOS_CLOUD_API_KEY in project .env file

The elizaos login command writes to ELIZAOS_CLOUD_API_KEY in your project's .env file.

Next Steps

Deploy with Trusted Execution Environment for enhanced security Full control with your own infrastructure

File: guides/publish-a-plugin.mdx


title: 'Publish a Plugin'

**Video Tutorial**: [**Create + Publish Your Own Plugin**](https://www.youtube.com/watch?v=3wVxXMwSzX4&list=PLrjBjP4nU8ehOgKAa0-XddHzE0KK0nNvS&index=6) This guide assumes you have a working plugin. If you need to create one first, see [Create a Plugin](/guides/create-a-plugin)

Once you've built and tested your plugin locally, you'll want to publish it so others can discover and use it. You'll need an npm account and GitHub account for authentication.


Step 1: Prepare for Publishing

Start from your working plugin directory. If you followed the Create a Plugin guide:

bash terminal
cd plugin-fal-ai

Verify plugin requirements

Your plugin needs these key elements for registry acceptance:

Required files:

text
plugin-fal-ai/
โ”œโ”€โ”€ src/
โ”‚   โ””โ”€โ”€ index.ts         # Your plugin code
โ”œโ”€โ”€ images/              # Registry assets 
โ”‚   โ”œโ”€โ”€ logo.jpg        # 400x400px, max 500KB
โ”‚   โ””โ”€โ”€ banner.jpg      # 1280x640px, max 1MB  
โ”œโ”€โ”€ package.json         # Plugin metadata
โ”œโ”€โ”€ README.md           # Documentation
โ””โ”€โ”€ dist/               # Built files (from `bun run build`)

What the publish command validates:

  • name starts with plugin- (auto-added by CLI if missing)
  • Custom description (not the default generated placeholder)
  • Required images in images/ directory
Create an `images/` directory if it doesn't exist:
text
```bash Terminal
mkdir -p images
```

Add these two custom images for your plugin's branding on the registry:
- **`logo.jpg`** - 400x400px square logo (max 500KB)
- **`banner.jpg`** - 1280x640px banner (max 1MB)

<Tip>
Use high-quality images that represent your plugin's functionality clearly. The logo will appear in plugin listings at various sizes.
</Tip>
Replace the default generated description with something descriptive:
text
```json package.json
{
  "name": "plugin-fal-ai",
  "version": "1.0.0",
  "description": "ElizaOS plugin for fal-ai", // [!code --]
  "description": "Generate videos from text using fal.ai MiniMax Hailuo-02 model", // [!code ++]
  "keywords": ["plugin", "elizaos"]
}
```
Ensure your plugin is built and ready:
text
```bash Terminal
bun run build
```

This creates the `dist/` folder that npm will publish.

Step 2: Check authentication

Make sure you're logged into both npm and GitHub:

Check npm login

```bash Terminal npm whoami ```
text
If you see your username, you're already logged in. If you see an error, continue to the next step.
```bash Terminal npm login ```
text
Follow the prompts to enter your:
- Username
- Password  
- Email address
- One-time password (if 2FA is enabled)

Check GitHub authentication

bash terminal
gh auth status

If you see your GitHub username, you're logged in. If you see an error or "not logged in":

bash terminal
gh auth login
If `gh` command is not found, you'll need to install GitHub CLI from [cli.github.com](https://cli.github.com) or the publish command will prompt you to create a token manually.

Step 3: Test Publishing (Dry Run)

Before actually publishing, test the entire process to catch any issues.

Run publish test

bash terminal
elizaos publish --test

This command will:

  • Check your npm and GitHub authentication
  • Validate your plugin structure
  • Check for required images and descriptions
  • Show you exactly what would happen without making any changes

Example output:

text
โœ“ Found existing NPM login: your-username
โœ“ GitHub token validated
โš  Plugin validation warnings:
  - Missing required logo.jpg in images/ directory (400x400px, max 500KB)
  - Missing required banner.jpg in images/ directory (1280x640px, max 1MB)
  - Description appears to be default generated description
Your plugin may get rejected if you submit without addressing these issues.
Do you wish to continue anyway? No
Address any validation errors before proceeding. Your plugin may be rejected by maintainers if it's missing required assets or has placeholder content.

Run dry run (optional)

For an even more detailed preview:

bash terminal
elizaos publish --dry-run

This generates all the registry files locally in packages/registry/ so you can see exactly what will be submitted.


Step 4: Publish Your Plugin

Once your test passes and you're satisfied with the setup, run the actual publish command.

Execute full publish

bash terminal
elizaos publish

You will be asked for a scoped Github token and given these instructions:

1. Go to [GitHub Settings โ†’ Developer settings โ†’ Personal access tokens](https://github.com/settings/tokens) 2. Click **"Generate new token (classic)"** 3. Name it **"elizaOS Publishing"** 4. Select these scopes: - `repo` (Full control of private repositories) - `read:org` (Read organization membership) - `workflow` (Update GitHub Action workflows) 5. Click **"Generate token"** 6. **Copy the token and paste it when prompted by the CLI** Make sure to test that your plugin is configured correctly before publishing, as it will cause unnecessary delay if something is wrong.

Example successful output:

text
โœ“ Successfully published [email protected] to npm
โœ“ Created GitHub repository: yourusername/plugin-fal-ai
โœ“ Registry pull request created: https://github.com/elizaos-plugins/registry/pull/123

Your plugin is now available at:
NPM: https://www.npmjs.com/package/plugin-fal-ai
GitHub: https://github.com/yourusername/plugin-fal-ai

Step 5: Registry Review Process

What happens next

  1. npm Package - Available immediately at https://npmjs.com/package/your-plugin-name
  2. GitHub Repo - Created immediately at https://github.com/yourusername/plugin-name
  3. Registry Pull Request - Opened at elizaos-plugins/registry

Registry approval

An elizaOS core team member will review your registry pull request to ensure all requirements are met, the plugin is free of malicious code, and it functions as intended with proper images and a quality description.

Typical review time: 1-3 business days

If approved: Your plugin appears in the official registry and can be discovered via elizaos plugins list

If changes requested: Address the feedback and update your plugin, then re-submit.


Step 6: Post-Publishing

Plugin is now live!

Once approved, users can install your plugin to their projects:

bash terminal
elizaos plugins add plugin-fal-ai

Future updates

For plugin updates after initial publishing:

The elizaos publish command is only for initial publication. For all future updates, use standard npm and Git workflows - never run elizaos publish again.

bash terminal
# 1. Make your changes and test locally
# 2. Update version in package.json
npm version patch  # or minor/major

# 3. Build and test  
bun run build
elizaos test

# 4. Publish to npm
npm publish

# 5. Push to GitHub
git add .
git commit -m "Update to version x.y.z"
git push origin main

The elizaOS registry automatically syncs with npm updates, so you don't need to manually update the registry.


See Also

Help improve elizaOS by contributing to the core framework Explore existing plugins and find inspiration Master all elizaOS CLI commands for development Share your plugin and get help from the community