Extension Architecture
Core Concepts
Extensions vs Tools
The AgentOS extension system has a clear hierarchy:
Extension (Package/Container)
├── Tools (implement ITool interface)
├── Guardrails (implement IGuardrailService)
├── Workflows (workflow definitions)
└── Response Processors (output transformers)
Key Differences
| Aspect | Extension | Tool |
|---|---|---|
| What it is | A package/module that provides capabilities | A specific capability that implements ITool |
| Scope | Can contain multiple tools, guardrails, etc. | Single focused functionality |
| Registration | Loaded via ExtensionManager | Registered as part of an extension |
| Example | @framers/agentos-ext-telegram | telegramSendMessage tool |
Extension Anatomy
// Extension Package Structure
extension-package/
├── src/
│ ├── index.ts // Extension entry point
│ ├── tools/ // Tool implementations
│ │ ├── tool1.ts // Implements ITool
│ │ └── tool2.ts // Implements ITool
│ ├── services/ // Shared services
│ └── types.ts // Type definitions
├── manifest.json // Extension metadata
└── package.json // NPM package info
// Extension Entry Point
export function createExtensionPack(context: ExtensionContext): ExtensionPack {
return {
name: '@framers/my-extension',
version: '1.0.0',
descriptors: [
{
id: 'tool1',
kind: 'tool', // Extension kind
payload: new Tool1() // ITool implementation
},
{
id: 'tool2',
kind: 'tool',
payload: new Tool2()
}
],
onActivate: async () => { /* initialization */ },
onDeactivate: async () => { /* cleanup */ }
};
}
The ITool Interface
Tools must implement the ITool interface:
export interface ITool<TInput = any, TOutput = any> {
// Identification
readonly id: string; // Unique tool ID
readonly name: string; // LLM-facing name
readonly displayName: string; // Human-readable name
readonly description: string; // Detailed description for LLM
// Schema definitions
readonly inputSchema: JSONSchema; // Expected input structure
readonly outputSchema?: JSONSchema; // Output structure
// Metadata
readonly category?: string; // Tool category
readonly version?: string; // Tool version
readonly hasSideEffects?: boolean; // Has external effects
readonly requiredCapabilities?: string[]; // Required permissions
// Core functionality
execute(args: TInput, context: ToolExecutionContext): Promise<ToolExecutionResult<TOutput>>;
// Optional methods
validateArgs?(args: any): { isValid: boolean; errors?: string[] };
shutdown?(): Promise<void>;
}
Extension Loading Process
- Discovery: Extensions are discovered from the manifest
- Factory Execution: Extension factory functions are called
- Registration: Extension descriptors are registered with ExtensionRegistry
- Activation:
onActivatelifecycle hooks are called - Availability: Tools become available to GMIs
Using the Curated Registry (Recommended)
import { createCuratedManifest } from '@framers/agentos-extensions-registry';
// Build manifest from all installed extensions
const manifest = await createCuratedManifest({
tools: 'all',
channels: ['telegram', 'discord'],
secrets: {
'serper.apiKey': process.env.SERPER_API_KEY!,
'telegram.botToken': process.env.TELEGRAM_BOT_TOKEN!,
},
});
await extensionManager.loadFromManifest(manifest);
Using Individual Factories
const manifest = {
packs: [
{
factory: () => createWebSearchExtension(config),
priority: 10
},
{
factory: () => createTelegramExtension(config),
priority: 20
}
]
};
await extensionManager.loadFromManifest(manifest);
const tools = registry.getDescriptorStack('tool');
Extension Registry
The registry manages all loaded extensions:
// Get all tools
const tools = registry.getDescriptorStack('tool');
// Get specific tool
const webSearch = tools.find(t => t.id === 'webSearch');
// Priority stacking
// Higher priority extensions can override lower priority ones
const stack = registry.getDescriptorStack('tool');
// stack[0] = highest priority tool with id 'X'
// stack[1] = lower priority tool with same id 'X'
Environment Variable Support
Extensions can read configuration from multiple sources:
// Priority order for configuration:
// 1. Direct options
// 2. Custom environment variables
// 3. Default environment variables
// 4. Fallback values
function resolveBotToken(options: TelegramExtensionOptions): string {
// 1. Direct option
if (options.botToken) return options.botToken;
// 2. Custom env var
const customEnv = options.botTokenEnv || 'TELEGRAM_BOT_TOKEN';
if (process.env[customEnv]) return process.env[customEnv];
// 3. Common variations
const variations = ['TELEGRAM_TOKEN', 'BOT_TOKEN'];
for (const env of variations) {
if (process.env[env]) return process.env[env];
}
// 4. Error if not found
throw new Error('Bot token not found');
}
Agency Integration
Extensions enable GMIs to collaborate:
// GMIs have different roles and capabilities
const researcherGMI = await gmiManager.createGMI({
personaId: 'researcher',
capabilities: ['webSearch', 'factCheck']
});
const communicatorGMI = await gmiManager.createGMI({
personaId: 'communicator',
capabilities: ['telegramSendMessage']
});
// Agency coordinates GMIs
const agency = await agencyRegistry.createAgency({
workflowId: 'research-and-report',
seats: {
researcher: researcherGMI,
communicator: communicatorGMI
}
});
Tool Execution Context
Tools receive context about the calling GMI:
export interface ToolExecutionContext {
gmiId: string; // Which GMI is calling
personaId: string; // Active persona
userContext: UserContext; // User information
correlationId?: string; // Request tracking
sessionData?: any; // Session state
}
Extension Lifecycle
Activation
onActivate: async (context) => {
// Initialize connections
await service.connect();
// Set up listeners
service.on('event', handler);
// Load resources
await loadConfiguration();
}
Deactivation
onDeactivate: async (context) => {
// Clean up connections
await service.disconnect();
// Remove listeners
service.removeAllListeners();
// Release resources
await cleanup();
}
Secrets & API Keys
Some descriptors depend on external API keys. Declare those requirements so AgentOS (and downstream UIs) can enforce them:
const descriptor: ExtensionDescriptor<ITool> = {
id: 'com.company.ext.search',
kind: EXTENSION_KIND_TOOL,
payload: searchTool,
requiredSecrets: [{ id: 'openai.apiKey' }],
onActivate: (ctx) => {
const apiKey = ctx.getSecret?.('openai.apiKey');
searchTool.setApiKey(apiKey);
},
};
- The IDs come from
packages/agentos/src/config/extension-secrets.json(OpenAI, OpenRouter, Anthropic, Serper...). - AgentOS skips descriptors whose non-optional secrets are missing.
context.getSecret(secretId)resolves the configured value at runtime, allowing descriptors to avoid reading process environment variables directly.
Best Practices
1. Single Responsibility
Each tool should do one thing well:
// Good: Focused tools
class SendMessageTool { /* sends messages */ }
class SendPhotoTool { /* sends photos */ }
// Bad: Kitchen sink tool
class TelegramTool { /* does everything */ }
2. Proper Schema Definition
Define clear input/output schemas:
inputSchema: {
type: 'object',
required: ['chatId', 'text'],
properties: {
chatId: {
type: ['string', 'number'],
description: 'Target chat identifier'
},
text: {
type: 'string',
description: 'Message text',
maxLength: 4096
}
}
}
3. Error Handling
Return errors in ToolExecutionResult:
async execute(args, context): Promise<ToolExecutionResult> {
try {
const result = await operation(args);
return { success: true, output: result };
} catch (error) {
return {
success: false,
error: error.message,
details: { code: error.code }
};
}
}
4. Resource Management
Clean up in shutdown:
async shutdown(): Promise<void> {
await this.connection?.close();
this.cache?.clear();
this.timers?.forEach(t => clearInterval(t));
}
5. Configuration Flexibility
Support multiple configuration methods:
// Environment variables
process.env.MY_API_KEY
// Direct configuration
{ apiKey: 'key' }
// Configuration files
config.json, .env
// Defaults with overrides
{ ...defaults, ...userConfig }
Extension Categories
Tool Extensions
Provide executable capabilities:
- Web search
- API calls
- File operations
- Message sending
Guardrail Extensions
Provide safety checks:
- Content filtering
- Rate limiting
- Permission checking
- Output validation
Workflow Extensions
Define multi-step processes:
- Research workflows
- Deployment pipelines
- Data processing flows
Response Processor Extensions
Transform outputs:
- Format conversion
- Language translation
- Summary generation
- Data enrichment
Summary
The extension architecture provides:
- Modularity: Extensions as self-contained packages
- Flexibility: Multiple configuration sources
- Scalability: Priority-based stacking
- Collaboration: GMIs share tools through extensions
- Lifecycle: Proper initialization and cleanup
- Type Safety: Strong typing with TypeScript
- Discovery: Registry for tool discovery
- Standards: Consistent ITool interface