🤖 Agent Package
The Agent Package (@okai/agent
) provides the high-level orchestration layer for OKai, managing agent lifecycles, character loading, client initialization, and runtime coordination.
Architecture Overview
Key Responsibilities
The Agent Package (@okcashpro/agent
) serves as the orchestration layer for OKai, handling:
- Character and plugin loading
- Runtime initialization and management
- Database adapter selection
- Client initialization and coordination
- Token and environment management
Installation
pnpm add @okcashpro/agent
Quick Start
import { startAgents, loadCharacters } from "@okcashpro/agent";
// Load characters from files
const args = parseArguments();
const characters = await loadCharacters(args.characters || args.character);
// Start agent system
await startAgents();
Core Features
Character Loading
export async function loadCharacters(
charactersArg: string,
): Promise<Character[]> {
const characterPaths = normalizeCharacterPaths(charactersArg);
const loadedCharacters = [];
for (const path of characterPaths) {
try {
const character = JSON.parse(fs.readFileSync(path, "utf8"));
// Load plugins if specified
if (character.plugins) {
character.plugins = await Promise.all(
character.plugins.map(async (plugin) => {
const importedPlugin = await import(plugin);
return importedPlugin;
}),
);
}
loadedCharacters.push(character);
} catch (error) {
console.error(`Error loading character from ${path}: ${error}`);
}
}
// Fall back to default if none loaded
return loadedCharacters.length > 0 ? loadedCharacters : [defaultCharacter];
}
Agent Creation
export async function createAgent(
character: Character,
db: IDatabaseAdapter,
token: string,
) {
return new AgentRuntime({
databaseAdapter: db,
token,
modelProvider: character.modelProvider,
character,
plugins: [
bootstrapPlugin,
nodePlugin,
character.settings.secrets.WALLET_PUBLIC_KEY ? solanaPlugin : null,
].filter(Boolean),
providers: [],
actions: [],
services: [],
managers: [],
});
}
Client Initialization
export async function initializeClients(
character: Character,
runtime: IAgentRuntime,
) {
const clients = [];
const clientTypes = character.clients?.map((str) => str.toLowerCase()) || [];
if (clientTypes.includes(Clients.DISCORD)) {
clients.push(await DiscordClientInterface.start(runtime));
}
if (clientTypes.includes(Clients.TELEGRAM)) {
clients.push(await TelegramClientInterface.start(runtime));
}
if (clientTypes.includes(Clients.TWITTER)) {
clients.push(await TwitterClientInterface.start(runtime));
}
if (clientTypes.includes(Clients.DIRECT)) {
clients.push(await AutoClientInterface.start(runtime));
}
return clients;
}
Best Practices
Token Management
export function getTokenForProvider(
provider: ModelProviderName,
character: Character,
) {
switch (provider) {
case ModelProviderName.OPENAI:
return (
character.settings?.secrets?.OPENAI_API_KEY || settings.OPENAI_API_KEY
);
case ModelProviderName.ANTHROPIC:
return (
character.settings?.secrets?.ANTHROPIC_API_KEY ||
settings.ANTHROPIC_API_KEY
);
// Handle other providers...
}
}
Database Selection
function initializeDatabase() {
if (process.env.POSTGRES_URL) {
return new PostgresDatabaseAdapter({
connectionString: process.env.POSTGRES_URL,
});
}
return new SqliteDatabaseAdapter(new Database("./db.sqlite"));
}
Common Issues & Solutions
- Character Loading
// Handle missing character files
if (!characters || characters.length === 0) {
console.log("No characters found, using default character");
characters = [defaultCharacter];
}
- Plugin Loading
// Handle plugin import errors
try {
character.plugins = await Promise.all(
character.plugins.map((plugin) => import(plugin)),
);
} catch (error) {
console.error(`Error loading plugin: ${error.message}`);
character.plugins = [];
}