Documentation Index Fetch the complete documentation index at: https://docs.elizaos.ai/llms.txt
Use this file to discover all available pages before exploring further.
Important : This comprehensive guide will walk you through migrating your elizaOS plugins from version 0.x to 1.x. The migration process involves several key changes to architecture, APIs, and best practices.
Migration Overview
The 1.x architecture brings:
Better modularity - Cleaner separation of concerns
Improved testing - Easier to test individual components
Enhanced flexibility - More customization options
Better performance - Optimized runtime execution
Stronger typing - Catch errors at compile time
Pre-Migration Checklist
Before starting your migration:
Step 1: Create Version Branch
Create a new branch for the 1.x version while preserving the main branch for backwards compatibility:
Note : This branch will serve as your new 1.x version branch, keeping main intact for legacy support.
Step 2: Remove Deprecated Files
Clean up deprecated tooling and configuration files:
Files to Remove:
biome.json - Deprecated linter configuration
vitest.config.ts - Replaced by Bun test runner
Lock files - Any lock.json or yml.lock files
Quick Cleanup Commands:
rm -rf vitest.config.ts
rm -rf biome.json
rm -f * .lock.json * .yml.lock
Why? The elizaOS ecosystem has standardized on:
Bun’s built-in test runner (replacing Vitest)
Prettier for code formatting (replacing Biome)
Step 3: Update package.json
Version and Naming
{
"version" : "1.0.0" ,
"name" : "@elizaos/plugin-yourname" // Note: @elizaos, not @elizaos-plugins
}
Dependencies
{
"dependencies" : {
"@elizaos/core" : "1.0.0"
},
"devDependencies" : {
"tsup" : "8.3.5" ,
"prettier" : "^3.0.0" ,
"bun" : "^1.2.15" , // REQUIRED
"@types/bun" : "latest" , // REQUIRED
"typescript" : "^5.0.0"
}
}
Scripts Section
"scripts" : {
"build" : "tsup" ,
"dev" : "tsup --watch" ,
"lint" : "prettier --write ./src" ,
"clean" : "rm -rf dist .turbo node_modules" ,
"test" : "bun test" ,
"publish" : "npm publish --access public"
}
Step 4: Update TypeScript Configuration
Update tsconfig.json:
{
"extends" : "../../tsconfig.base.json" ,
"compilerOptions" : {
"module" : "esnext" ,
"target" : "esnext" ,
"moduleResolution" : "bundler" ,
"outDir" : "dist" ,
"rootDir" : "src" ,
"types" : [ "bun-types" ],
"skipLibCheck" : true ,
"esModuleInterop" : true ,
"allowSyntheticDefaultImports" : true
},
"include" : [ "src/**/*" ],
"exclude" : [ "node_modules" , "dist" , "__tests__" , "**/*.test.ts" ]
}
Step 5: Update Core Package Imports
Import Changes
// OLD (0.x)
import { Action , Memory , State } from '@ai16z/eliza' ;
// NEW (1.x)
import { Action , Memory , State , ActionResult } from '@elizaos/core' ;
Step 6: Update Actions
Action Signatures
Actions in 1.x must return ActionResult and include callbacks:
// OLD (0.x)
const myAction = {
handler : async ( runtime , message , state ) => {
return { text: "Response" };
}
};
// NEW (1.x)
const myAction = {
handler : async ( runtime , message , state , options , callback ) => {
// Use callback for intermediate responses
await callback ?.({ text: "Processing..." });
// Must return ActionResult
return {
success: true , // REQUIRED field
text: "Response completed" ,
values: { /* state updates */ },
data: { /* raw data */ }
};
}
};
Common Action Patterns
// Error handling
handler : async ( runtime , message , state , options , callback ) => {
try {
const result = await performAction ();
return {
success: true ,
text: `Action completed: ${ result } ` ,
data: result
};
} catch ( error ) {
return {
success: false ,
text: "Action failed" ,
error: error instanceof Error ? error : new Error ( String ( error ))
};
}
}
// Using previous results (action chaining)
handler : async ( runtime , message , state , options , callback ) => {
const context = options ?. context ;
const previousResult = context ?. getPreviousResult ?.( 'PREVIOUS_ACTION' );
if ( previousResult ?. data ) {
// Use data from previous action
const continuedResult = await continueWork ( previousResult . data );
return {
success: true ,
text: "Continued from previous action" ,
data: continuedResult
};
}
}
Step 7: State Management
Using composeState
The v1 composeState method has enhanced filtering capabilities:
// v0: Basic state composition
const state = await runtime . composeState ( message );
// v1: With filtering
const state = await runtime . composeState (
message ,
[ 'agentName' , 'bio' , 'recentMessages' ], // Include only these
true // onlyInclude = true
);
// v1: Update specific parts
const updatedState = await runtime . composeState (
message ,
[ 'RECENT_MESSAGES' , 'GOALS' ] // Update only these
);
Available State Keys
Core state keys you can filter:
Agent info: agentId, agentName, bio, lore, adjective
Conversation: recentMessages, recentMessagesData
Providers: Any registered provider name (e.g., TIME, FACTS)
Step 8: Update Providers
Provider Migration
// v0: Direct state access
const data = await runtime . databaseAdapter . getData ();
// v1: Provider pattern
const provider : Provider = {
name: 'MY_DATA' ,
description: 'Provides custom data' ,
dynamic: true , // Re-fetch on each use
get : async ( runtime , message , state ) => {
const data = await runtime . databaseAdapter . getData ();
return {
text: formatDataForPrompt ( data ),
values: data ,
data: { raw: data }
};
}
};
Provider Options
interface Provider {
name : string ;
description ?: string ;
dynamic ?: boolean ; // Default: false
position ?: number ; // Execution order (-100 to 100)
private ?: boolean ; // Hidden from provider list
get : ( runtime , message , state ) => Promise < ProviderResult >;
}
Step 9: Testing Migration
From Vitest to Bun
// OLD (Vitest)
import { describe , it , expect , vi } from 'vitest' ;
const mockRuntime = {
getSetting: vi . fn (() => 'test-value' )
};
// NEW (Bun)
import { describe , it , expect , mock } from 'bun:test' ;
const mockRuntime = {
getSetting: mock (() => 'test-value' )
};
Test Structure
import { describe , it , expect , mock , beforeEach } from 'bun:test' ;
import { myAction } from '../src/actions/myAction' ;
describe ( 'MyAction' , () => {
let mockRuntime : any ;
beforeEach (() => {
mockRuntime = {
agentId: 'test-agent' ,
getSetting: mock (( key : string ) => 'test-value' ),
useModel: mock ( async () => ({ content: 'response' })),
composeState: mock ( async () => ({ values: {}, text: '' }))
};
});
it ( 'should validate correctly' , async () => {
const message = { content: { text: 'test' } };
const isValid = await myAction . validate ( mockRuntime , message );
expect ( isValid ). toBe ( true );
});
it ( 'should return ActionResult' , async () => {
const message = { content: { text: 'test' } };
const result = await myAction . handler ( mockRuntime , message );
expect ( result ). toHaveProperty ( 'success' );
expect ( result . success ). toBe ( true );
});
});
Step 10: Prompt Generation
Template System Changes
// v0: Simple template
const template = `{{agentName}} responds to {{userName}}` ;
// v1: Enhanced templates with conditional blocks
const template = `
{{#if hasGoals}}
Current goals: {{goals}}
{{/if}}
{{agentName}} considers the context and responds.
` ;
Using composePromptFromState
import { composePromptFromState } from '@elizaos/core' ;
const prompt = composePromptFromState ({
state ,
template: myTemplate ,
additionalContext: {
customField: 'value'
}
});
const response = await runtime . useModel ( ModelType . TEXT_LARGE , {
prompt ,
runtime
});
Step 11: Advanced Patterns
Service Migration
// v1: Service pattern
export class MyService extends Service {
static serviceType = 'my-service' ;
capabilityDescription = 'My service capabilities' ;
static async start ( runtime : IAgentRuntime ) : Promise < MyService > {
const service = new MyService ( runtime );
await service . initialize ();
return service ;
}
async stop () : Promise < void > {
// Cleanup
}
}
Event Handlers
// v1: Event system
export const myPlugin : Plugin = {
name: 'my-plugin' ,
events: {
MESSAGE_RECEIVED: [
async ( params ) => {
// Handle message received event
}
],
RUN_COMPLETED: [
async ( params ) => {
// Handle run completed event
}
]
}
};
Step 12: Final Configuration
dist
node_modules
.env
.elizadb
.turbo
*
!dist/**
!package.json
!readme.md
!tsup.config.ts
Add MIT License
Create a LICENSE file with MIT license text.
Verify package.json Fields
Ensure all required fields are present:
Common Migration Issues
Solution : Ensure all actions return ActionResult with success field:
return {
success: true , // REQUIRED
text: "Response" ,
values: {},
data: {}
};
Issue: Tests failing with module errors
Solution : Update imports to use bun:test:
import { describe , it , expect , mock } from 'bun:test' ;
Solution : Use filtering to only compose needed state:
const state = await runtime . composeState (
message ,
[ 'RECENT_MESSAGES' , 'CHARACTER' ],
true // onlyInclude
);
Issue: Provider not being called
Solution : Ensure provider is registered and not marked as private:
const provider = {
name: 'MY_PROVIDER' ,
private: false , // Make sure it's not private
dynamic: false , // Static providers are included by default
get : async () => { /* ... */ }
};
Testing Your Migration
Build Test
Run Tests
Integration Test
Create a test agent using your migrated plugin:
import { myPlugin } from './dist/index.js' ;
const agent = {
name: 'TestAgent' ,
plugins: [ myPlugin ]
};
// Test your plugin functionality
Publishing
Once migration is complete:
# Build the plugin
bun run build
# Test everything
bun test
# Publish to npm
npm publish --access public
Migration Completion Checklist
Getting Help
If you encounter issues during migration:
Review this guide for common issues
Search existing GitHub issues
Join our Discord community for real-time help
Create a detailed issue with your migration problem
Advanced Migration Topics
Evaluators Migration
Evaluator Interface Changes
Evaluators remain largely unchanged in their core structure, but their integration with the runtime has evolved:
// v0 Evaluator usage remains the same
export interface Evaluator {
alwaysRun ?: boolean ;
description : string ;
similes : string [];
examples : EvaluationExample [];
handler : Handler ;
name : string ;
validate : Validator ;
}
Key Changes:
Evaluation Results : The evaluate() method now returns Evaluator[] instead of string[]:
// v0: Returns string array of evaluator names
const evaluators : string [] = await runtime . evaluate ( message , state );
// v1: Returns Evaluator objects
const evaluators : Evaluator [] | null = await runtime . evaluate ( message , state );
Additional Parameters : The evaluate method accepts new optional parameters:
// v1: Extended evaluate signature
await runtime . evaluate (
message : Memory ,
state ?: State ,
didRespond ?: boolean ,
callback ?: HandlerCallback ,
responses ?: Memory [] // NEW: Can pass responses for evaluation
);
Entity System Migration
The most significant change is the shift from User/Participant to Entity/Room/World:
User → Entity
// v0: User-based methods
await runtime . ensureUserExists ( userId , userName , name , email , source );
const account = await runtime . getAccountById ( userId );
// v1: Entity-based methods
await runtime . ensureConnection ({
entityId: userId ,
roomId ,
userName ,
name ,
worldId ,
source ,
});
const entity = await runtime . getEntityById ( entityId );
Participant → Room Membership
// v0: Participant methods
await runtime . ensureParticipantExists ( userId , roomId );
await runtime . ensureParticipantInRoom ( userId , roomId );
// v1: Simplified room membership
await runtime . ensureParticipantInRoom ( entityId , roomId );
New World Concept
v1 introduces the concept of “worlds” (servers/environments):
// v1: World management
await runtime . ensureWorldExists ({
id: worldId ,
name: serverName ,
type: 'discord' , // or other platform
});
// Get all rooms in a world
const rooms = await runtime . getRooms ( worldId );
Connection Management
// v0: Multiple ensure methods
await runtime . ensureUserExists ( ... );
await runtime . ensureRoomExists ( roomId );
await runtime . ensureParticipantInRoom ( ... );
// v1: Single connection method
await runtime . ensureConnection ({
entityId ,
roomId ,
worldId ,
userName ,
name ,
source ,
channelId ,
serverId ,
type: 'user' ,
metadata: {}
});
Client Migration
Clients now have a simpler interface:
// v0: Client with config
export type Client = {
name : string ;
config ?: { [ key : string ] : any };
start : ( runtime : IAgentRuntime ) => Promise < ClientInstance >;
};
// v1: Client integrated with services
// Clients are now typically implemented as services
class MyClient extends Service {
static serviceType = ServiceTypeName . MY_CLIENT ;
async initialize ( runtime : IAgentRuntime ) : Promise < void > {
// Start client operations
}
async stop () : Promise < void > {
// Stop client operations
}
}
Runtime Method Changes
Removed Methods
updateRecentMessageState() → Use composeState(message, ['RECENT_MESSAGES'])
registerMemoryManager() → Not needed, use database adapter
getMemoryManager() → Use database adapter methods
registerContextProvider() → Use registerProvider()
Changed Methods
evaluate() → Now returns Evaluator[] instead of string[]
getAccountById() → getEntityById()
ensureUserExists() → ensureConnection()
generateText() → runtime.useModel()
New Methods
setSetting()
registerEvent()
emitEvent()
useModel()
registerModel()
ensureWorldExists()
getRooms()
Advanced Migration Checklist
Summary
The migration from 0.x to 1.x involves:
Updating package structure and dependencies
Migrating action signatures to return ActionResult
Implementing callbacks for user feedback
Converting to Bun test runner
Using the enhanced state composition system
Implementing providers for custom data
Following new TypeScript patterns
Take your time, test thoroughly, and don’t hesitate to ask for help in the community!
See Also
Plugin Architecture Understand the new plugin system architecture
Development Guide Build new plugins with modern patterns
Plugin Components Learn about Actions, Providers, Evaluators, and Services
Common Patterns Master proven plugin development patterns