> ## 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.

# Migration

> Complete guide for migrating elizaOS plugins from version 0.x to 1.x

> **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:

* [ ] Backup your existing plugin code
* [ ] Review all breaking changes
* [ ] Identify custom components that need migration
* [ ] Plan your testing strategy
* [ ] Allocate sufficient time for the migration

## Step 1: Create Version Branch

Create a new branch for the 1.x version while preserving the main branch for backwards compatibility:

```bash theme={null}
git checkout -b 1.x
```

> **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:

```bash theme={null}
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

```json theme={null}
{
  "version": "1.0.0",
  "name": "@elizaos/plugin-yourname"  // Note: @elizaos, not @elizaos-plugins
}
```

### Dependencies

```json theme={null}
{
  "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

```json theme={null}
"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`:

```json theme={null}
{
  "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

```typescript theme={null}
// 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:

```typescript theme={null}
// 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

```typescript theme={null}
// 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:

```typescript theme={null}
// 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

```typescript theme={null}
// 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

```typescript theme={null}
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

```typescript theme={null}
// 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

```typescript theme={null}
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

```typescript theme={null}
// 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

```typescript theme={null}
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

```typescript theme={null}
// 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

```typescript theme={null}
// 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

### Configure .gitignore

```
dist
node_modules
.env
.elizadb
.turbo
```

### Configure .npmignore

```
*
!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:

* [ ] `name`, `version`, `description`
* [ ] `main`, `types`, `module`
* [ ] `author`, `license`, `repository`
* [ ] `scripts`, `dependencies`, `devDependencies`
* [ ] `type`: "module"
* [ ] `exports` configuration

## Common Migration Issues

### Issue: Action not returning correct format

**Solution**: Ensure all actions return `ActionResult` with `success` field:

```typescript theme={null}
return {
  success: true,  // REQUIRED
  text: "Response",
  values: {},
  data: {}
};
```

### Issue: Tests failing with module errors

**Solution**: Update imports to use `bun:test`:

```typescript theme={null}
import { describe, it, expect, mock } from 'bun:test';
```

### Issue: State composition performance

**Solution**: Use filtering to only compose needed state:

```typescript theme={null}
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`:

```typescript theme={null}
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

```bash theme={null}
bun run build
```

### Run Tests

```bash theme={null}
bun test
```

### Integration Test

Create a test agent using your migrated plugin:

```typescript theme={null}
import { myPlugin } from './dist/index.js';

const agent = {
  name: 'TestAgent',
  plugins: [myPlugin]
};

// Test your plugin functionality
```

## Publishing

Once migration is complete:

```bash theme={null}
# Build the plugin
bun run build

# Test everything
bun test

# Publish to npm
npm publish --access public
```

## Migration Completion Checklist

* [ ] All imports updated to `@elizaos/core`
* [ ] Actions return `ActionResult` with `success` field
* [ ] Callbacks implemented in action handlers
* [ ] Tests migrated to Bun test runner
* [ ] State composition uses new filtering API
* [ ] Providers implemented for custom data
* [ ] Package.json updated with correct dependencies
* [ ] TypeScript configuration updated
* [ ] Build succeeds without errors
* [ ] All tests pass
* [ ] Plugin works with v1.x runtime

## Getting Help

If you encounter issues during migration:

1. Review this guide for common issues
2. Search existing [GitHub issues](https://github.com/elizaos/eliza/issues)
3. Join our [Discord community](https://discord.gg/ai16z) for real-time help
4. 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:

```typescript theme={null}
// 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:

1. **Evaluation Results**: The `evaluate()` method now returns `Evaluator[]` instead of `string[]`:

```typescript theme={null}
// 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);
```

2. **Additional Parameters**: The evaluate method accepts new optional parameters:

```typescript theme={null}
// 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

```typescript theme={null}
// 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

```typescript theme={null}
// 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):

```typescript theme={null}
// 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

```typescript theme={null}
// 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:

```typescript theme={null}
// 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

* [ ] Update all evaluator result handling to expect `Evaluator[]` objects
* [ ] Remove singleton patterns from services
* [ ] Update service registration to pass classes instead of instances
* [ ] Replace `updateRecentMessageState` with `composeState`
* [ ] Migrate from `generateText` to `runtime.useModel`
* [ ] Update user/participant methods to entity/room methods
* [ ] Add world management for multi-server environments
* [ ] Convert clients to service-based architecture
* [ ] Update any direct memory manager access to use database adapter
* [ ] Replace `import { settings }` with `runtime.getSetting()` calls
* [ ] Update functions to accept `runtime` parameter where settings are needed

## Summary

The migration from 0.x to 1.x involves:

1. Updating package structure and dependencies
2. Migrating action signatures to return `ActionResult`
3. Implementing callbacks for user feedback
4. Converting to Bun test runner
5. Using the enhanced state composition system
6. Implementing providers for custom data
7. Following new TypeScript patterns

Take your time, test thoroughly, and don't hesitate to ask for help in the community!

## See Also

<CardGroup cols={2}>
  <Card title="Plugin Architecture" icon="sitemap" href="/plugins/architecture">
    Understand the new plugin system architecture
  </Card>

  <Card title="Development Guide" icon="code" href="/plugins/development">
    Build new plugins with modern patterns
  </Card>

  <Card title="Plugin Components" icon="cube" href="/plugins/components">
    Learn about Actions, Providers, Evaluators, and Services
  </Card>

  <Card title="Common Patterns" icon="lightbulb" href="/plugins/patterns">
    Master proven plugin development patterns
  </Card>
</CardGroup>
