The Problem
Building production AI agents is hard. You’re juggling:
- Plugin management across multiple providers and capabilities
- State persistence that doesn’t break between restarts
- Multi-agent orchestration without race conditions
- Deployment complexity from local dev to serverless edge
Most frameworks give you primitives. You still wire everything together yourself.
5 minutes to your first agent. No infrastructure setup, no config files to manage. Just code.
Quick Start with ElizaOS
The ElizaOS class handles configuration, plugin resolution, and multi-agent orchestration automatically.
import { ElizaOS } from '@elizaos/core';
// Create the orchestrator
const elizaOS = new ElizaOS();
// Add agents with character and plugins
const agentIds = await elizaOS.addAgents([
{
character: {
name: 'Assistant',
bio: ['A helpful AI assistant'],
system: 'You are a helpful assistant.',
},
plugins: ['@elizaos/plugin-sql', '@elizaos/plugin-openai'],
}
]);
// Start all agents
await elizaOS.startAgents();
// Send a message and get response
const result = await elizaOS.handleMessage(agentIds[0], {
entityId: userId,
roomId: roomId,
content: { text: 'Hello!', source: 'web' }
});
console.log(result.processing?.text);
Deployment Patterns
Serverless / Edge Deployment
For serverless environments, use ephemeral mode:
// Ephemeral mode - runtime not stored in registry
const [runtime] = await elizaOS.addAgents([{
character,
plugins,
databaseAdapter: cachedAdapter, // Pre-initialized DB
}], {
ephemeral: true,
skipMigrations: true,
autoStart: true,
returnRuntimes: true,
});
// Use runtime directly (no registry lookup)
const result = await elizaOS.handleMessage(runtime, {
entityId: userId,
roomId: roomId,
content: { text: 'Hello!', source: 'lambda' }
});
Async Mode with Callbacks
For WebSocket or streaming scenarios:
await elizaOS.handleMessage(agentId, message, {
onResponse: async (content) => {
await socket.emit('message', content.text);
},
onStreamChunk: async (chunk, messageId) => {
await socket.emit('chunk', { chunk, messageId });
},
onError: async (error) => {
console.error('Agent error:', error);
},
onComplete: async () => {
console.log('Processing complete');
}
});
Multi-Agent Communication
Send messages to multiple agents in parallel:
const results = await elizaOS.handleMessages([
{
agentId: researchAgent,
message: { entityId, roomId, content: { text: 'Research topic X', source: 'internal' } }
},
{
agentId: writerAgent,
message: { entityId, roomId, content: { text: 'Prepare outline', source: 'internal' } }
}
]);
Agent Discovery
// By ID
const agent = elizaOS.getAgent(agentId);
// By name
const assistant = elizaOS.getAgentByName('Assistant');
// All agents
const allAgents = elizaOS.getAgents();
// Multiple by IDs
const selected = elizaOS.getAgentsByIds([id1, id2]);
// Multiple by names
const team = elizaOS.getAgentsByNames(['Researcher', 'Writer']);
Events
ElizaOS extends EventTarget for lifecycle events:
elizaOS.addEventListener('agent:started', (e) => {
console.log(`Agent ${e.detail.agentId} started`);
});
elizaOS.addEventListener('message:sent', (e) => {
console.log(`Message ${e.detail.messageId} sent in ${e.detail.mode} mode`);
});
// Available events:
// agent:added, agent:started, agent:stopped, agent:deleted
// agents:added, agents:started, agents:stopped, agents:deleted
// agent:registered, agent:updated
// message:sent, messages:sent
// mode:editable
Health Monitoring
// Check agent health
const health = await elizaOS.healthCheck();
// Map<UUID, { alive: boolean, responsive: boolean, memoryUsage?: number, uptime?: number }>
// Validate API keys
const keyStatus = await elizaOS.validateApiKeys();
// Map<UUID, boolean>
What You Get
| Feature | Without ElizaOS | With ElizaOS |
|---|
| Plugin resolution | Manual imports, version conflicts | Auto-discovery, dependency ordering |
| Database setup | 50+ lines boilerplate | Zero-config SQLite or Postgres |
| Multi-agent | Build coordination from scratch | handleMessages([...]) |
| Streaming | Implement SSE/WebSocket yourself | Built-in with callbacks |
| State management | Manual context threading | Automatic composition |
| Platform integration | Custom adapters per platform | Plugin ecosystem |
System Architecture
The elizaOS runtime follows a modular, plugin-based architecture that orchestrates all agent functionality. For lifecycle details, see Runtime and Lifecycle. For extension architecture, see Plugin Architecture.
Core Components
The runtime orchestrates these essential components:
- AgentRuntime: Central orchestrator managing agent lifecycle
- Plugin System: Extends functionality through modular components
- Memory System: Hierarchical storage for conversations and knowledge
- State Management: Aggregates context from multiple sources
- Service Layer: Background processes and integrations
For related documentation, see Plugin Architecture, Memory, and Services.
AgentRuntime Class
The AgentRuntime class is the central engine that manages agent lifecycle, processes messages, and coordinates all system components.
Core Interface
interface IAgentRuntime extends IDatabaseAdapter {
// Core properties
agentId: UUID;
character: Character;
providers: Provider[];
actions: Action[];
evaluators: Evaluator[];
services: Service[];
// Action processing
processActions(message: Memory, responses: Memory[], state?: State): Promise<void>;
composeState(message: Memory, state?: State): Promise<State>;
evaluate(message: Memory, state?: State): Promise<void>;
// Component registration
registerAction(action: Action): void;
registerProvider(provider: Provider): void;
registerEvaluator(evaluator: Evaluator): void;
registerService(service: Service): void;
// Service management
getService<T>(name: ServiceType): T;
stop(): Promise<void>;
// Model management
useModel<T extends ModelTypeName>(modelType: T, params: ModelParamsMap[T], provider?: string): Promise<ModelResultMap[T]>;
registerModel(modelType: ModelTypeName, handler: ModelHandler, provider?: string, priority?: number): void;
getModel(modelType: ModelTypeName, provider?: string): ModelHandler | undefined;
// Event system
emit(eventType: EventType, data: any): Promise<void>;
on(eventType: EventType, handler: EventHandler): void;
}
Key Responsibilities
1. Action Processing
The runtime orchestrates action selection and execution:
async processActions(message: Memory, responses: Memory[], state?: State): Promise<void> {
// Select and execute actions based on context
const actions = await this.selectActions(message, state);
for (const action of actions) {
await action.handler(this, message, state);
}
// Run evaluators on results
await this.evaluate(message, state);
}
2. State Composition
Builds comprehensive context by aggregating data from providers:
async composeState(message: Memory): Promise<State> {
const state = {};
for (const provider of this.providers) {
const data = await provider.get(this, message, state);
Object.assign(state, data);
}
return state;
}
3. Plugin Management
Registers and initializes plugin components:
async registerPlugin(plugin: Plugin) {
// Register components
plugin.actions?.forEach(a => this.registerAction(a));
plugin.providers?.forEach(p => this.registerProvider(p));
plugin.evaluators?.forEach(e => this.registerEvaluator(e));
plugin.services?.forEach(s => this.registerService(s));
// Initialize plugin
await plugin.init?.(this.config, this);
}
Runtime Lifecycle
Initialization Sequence
- Runtime Creation: Instantiate with character and configuration
- Character Loading: Load agent personality and settings
- Plugin Loading: Register plugins in dependency order
- Service Startup: Initialize background services
- Ready State: Agent ready to process messages
Plugin Loading Order
// Plugin priority determines load order
const pluginLoadOrder = [
databases, // Priority: -100
modelProviders, // Priority: -50
corePlugins, // Priority: 0
features, // Priority: 50
platforms // Priority: 100
];
Configuration
Runtime Configuration
The runtime accepts configuration through multiple sources:
interface RuntimeConfig {
character: Character;
plugins: Plugin[];
database?: DatabaseConfig;
models?: ModelConfig;
services?: ServiceConfig;
environment?: EnvironmentConfig;
}
Environment Variables
Core runtime environment variables:
NODE_ENV - Runtime environment (development/production)
LOG_LEVEL - Logging verbosity
DATABASE_URL - Database connection string
API_PORT - Server port for API endpoints
AGENT_ID - Unique agent identifier
Settings Management
Access configuration through the runtime:
// Get setting with fallback
const apiKey = runtime.getSetting("API_KEY");
// Check if setting exists
if (runtime.hasSetting("FEATURE_FLAG")) {
// Feature is enabled
}
Database Abstraction
The runtime implements IDatabaseAdapter for data persistence:
interface IDatabaseAdapter {
// Memory operations
createMemory(memory: Memory): Promise<void>;
searchMemories(query: string, limit?: number): Promise<Memory[]>;
getMemoryById(id: UUID): Promise<Memory | null>;
// Entity management
createEntity(entity: Entity): Promise<void>;
updateEntity(entity: Entity): Promise<void>;
getEntity(id: UUID): Promise<Entity | null>;
// Relationships
createRelationship(rel: Relationship): Promise<void>;
getRelationships(entityId: UUID): Promise<Relationship[]>;
// Facts and knowledge
createFact(fact: Fact): Promise<void>;
searchFacts(query: string): Promise<Fact[]>;
}
Memory Operations
// Store a message
await runtime.createMemory({
type: MemoryType.MESSAGE,
content: { text: "User message" },
roomId: message.roomId,
userId: message.userId
});
// Search memories
const memories = await runtime.searchMemories(
"previous conversation",
10 // limit
);
// Get specific memory
const memory = await runtime.getMemoryById(memoryId);
Message Processing Pipeline
The runtime processes messages through a defined pipeline:
Processing Steps
- Message Receipt: Receive and validate incoming message
- Memory Storage: Persist message to database
- State Composition: Build context from providers
- Action Selection: Choose appropriate actions
- Action Execution: Run selected action handlers
- Evaluation: Post-process results
- Response Generation: Create and send response
Error Handling
The runtime implements comprehensive error handling:
try {
await runtime.processActions(message, responses, state);
} catch (error) {
if (error instanceof ActionError) {
// Handle action-specific errors
runtime.logger.error("Action failed:", error);
} else if (error instanceof StateError) {
// Handle state composition errors
runtime.logger.error("State error:", error);
} else {
// Handle unexpected errors
runtime.logger.error("Unexpected error:", error);
// Optionally trigger recovery
}
}
State Caching
The runtime caches composed state for performance:
// State is cached by message ID
const state = await runtime.composeState(message);
// Subsequent calls use cache
const cachedState = await runtime.composeState(message);
Service Pooling
Services are singleton instances shared across the runtime:
// Services are created once and reused
const service = runtime.getService(ServiceType.DATABASE);
// Same instance returned
const sameService = runtime.getService(ServiceType.DATABASE);
Best Practices
Runtime Initialization
- Initialize plugins in dependency order
- Start services after all plugins are loaded
- Verify character configuration before starting
- Set up error handlers before processing
Resource Management
- Clean up services on shutdown
- Clear state cache periodically
- Monitor memory usage
- Implement connection pooling
Error Recovery
- Implement retry logic for transient failures
- Log errors with context
- Gracefully degrade functionality
- Maintain audit trail
Integration Points
The runtime provides multiple integration points:
- Plugins: Extend functionality through the plugin system
- Events: React to runtime events
- Services: Add background processes
- Models: Integrate AI providers
- Database: Custom database adapters
- API: HTTP endpoints through routes
Advanced Runtime Methods
Beyond the core interface, the runtime exposes additional methods for advanced use cases.
Run Tracking
Track agent execution runs for debugging, analytics, and action chaining:
interface IAgentRuntime {
// Create a unique run identifier
createRunId(): UUID;
// Start a run (optionally scoped to a room)
startRun(roomId?: UUID): UUID;
// End the current run
endRun(): void;
// Get the current active run ID
getCurrentRunId(): UUID;
}
Usage:
// Start tracking a run
const runId = runtime.startRun(roomId);
try {
// Process messages, actions execute within this run
await runtime.processActions(message, responses, state);
// Get results from actions executed in this run
const results = runtime.getActionResults(message.id);
} finally {
// Always end the run
runtime.endRun();
}
Action Results
Retrieve results from executed actions for action chaining:
// Get all action results for a specific message
getActionResults(messageId: UUID): ActionResult[];
Example - Action chaining:
// First action generates data
const results = runtime.getActionResults(message.id);
// Second action uses results from first
const previousData = results.find(r => r.action === 'SEARCH_WEB');
if (previousData?.success) {
// Use previousData.data in next action
}
Embedding Generation
Queue memories for async embedding generation:
// Non-blocking - queues for background processing
queueEmbeddingGeneration(
memory: Memory,
priority?: 'high' | 'normal' | 'low'
): Promise<void>;
// Blocking - adds embedding synchronously
addEmbeddingToMemory(memory: Memory): Promise<Memory>;
Priority levels:
high - Immediate processing (user queries)
normal - Default priority (conversations)
low - Background processing (bulk imports)
// Queue high-priority embedding for user query
await runtime.queueEmbeddingGeneration(userMessage, 'high');
// Queue low-priority for background knowledge
await runtime.queueEmbeddingGeneration(knowledgeDoc, 'low');
Conversation Length
Get the current conversation length setting:
getConversationLength(): number;
Returns the configured maximum conversation length from character settings.
Service Management
Advanced service discovery and management:
interface IAgentRuntime {
// Get all registered services by type
getAllServices(): Map<ServiceTypeName, Service[]>;
// Get all registered service type names
getRegisteredServiceTypes(): ServiceTypeName[];
// Get multiple services of the same type
getServicesByType<T extends Service>(type: ServiceTypeName): T[];
// Wait for a service to finish loading
getServiceLoadPromise(serviceType: ServiceTypeName): Promise<Service>;
// Check if service exists
hasService(serviceType: ServiceTypeName): boolean;
}
Example - Wait for service:
// Wait for database service to be ready
const dbService = await runtime.getServiceLoadPromise(ServiceType.DATABASE);
// Get all speech services
const speechServices = runtime.getServicesByType(ServiceType.SPEECH);
// Check available service types
const types = runtime.getRegisteredServiceTypes();
// ['DATABASE', 'SPEECH', 'VIDEO', 'TRANSCRIPTION', ...]
ElizaOS Type Guard
Check if the runtime has an ElizaOS instance attached:
hasElizaOS(): this is IAgentRuntime & { elizaOS: IElizaOS };
Usage:
if (runtime.hasElizaOS()) {
// TypeScript knows runtime.elizaOS is defined here
const elizaOS = runtime.elizaOS;
// Send message to another agent
await elizaOS.handleMessage(otherAgentId, {
entityId: message.entityId,
roomId: message.roomId,
content: { text: 'Forwarded message', source: 'internal' }
});
}
Connection Management
Batch setup of entities, rooms, and connections:
// Setup multiple connections at once
ensureConnections(
entities: Entity[],
rooms: Room[],
source: string,
world: World
): Promise<void>;
// Setup single connection with full options
ensureConnection({
entityId: UUID;
roomId: UUID;
userName?: string;
name?: string;
worldName?: string;
source?: string;
channelId?: string;
messageServerId?: UUID;
type?: ChannelType | string;
worldId: UUID;
userId?: UUID;
metadata?: Record<string, unknown>;
}): Promise<void>;
Example - Platform integration:
// Setup Discord server connection
await runtime.ensureConnection({
entityId: discordUserId,
roomId: channelRoomId,
userName: 'user#1234',
source: 'discord',
channelId: discordChannelId,
type: ChannelType.GROUP,
worldId: serverWorldId,
metadata: { roles: ['member', 'admin'] }
});
World Management
Update world configuration:
updateWorld(world: World): Promise<void>;
// Update world settings
await runtime.updateWorld({
id: worldId,
name: 'Updated Server Name',
metadata: {
settings: { allowBots: true },
features: ['voice', 'threads']
}
});
Message Routing
Register custom message handlers for different platforms:
// Register a send handler for a platform
registerSendHandler(source: string, handler: SendHandlerFunction): void;
// Send message to a specific target
sendMessageToTarget(target: TargetInfo, content: Content): Promise<void>;
// Register Discord message handler
// Handler signature: (runtime, target, content) => Promise<void>
runtime.registerSendHandler('discord', async (runtime, target, content) => {
const channel = await discordClient.channels.fetch(target.channelId);
await channel.send(content.text);
});
// Send message to target
await runtime.sendMessageToTarget(
{ source: 'discord', channelId: '123456', roomId },
{ text: 'Hello from agent!' }
);
See Also