Layer 3: Capabilities
Overview
Section titled “Overview”The Capabilities Layer (L3) defines what actions an agent can perform. It provides a registry of tools, functions, and APIs that the agent can invoke to interact with the world.
list Action Registry
Catalog of available actions and their schemas
shield-check Parameter Validation
Validate inputs before execution
bolt Execution Engine
Safely execute actions with error handling
lock Permission Control
Role-based access control for actions
Action Definition
Section titled “Action Definition”interface Action { id: string name: string description: string parameters: ParameterSchema returns: ReturnSchema permissions: string[] rateLimit?: RateLimit}
interface ParameterSchema { type: 'object' properties: Record<string, JSONSchema> required: string[]}
// Example: Weather actionconst getWeatherAction: Action = { id: 'get-weather', name: 'Get Weather', description: 'Fetch current weather for a location', parameters: { type: 'object', properties: { location: { type: 'string', description: 'City name or coordinates' }, unit: { type: 'string', enum: ['celsius', 'fahrenheit'], default: 'celsius' } }, required: ['location'] }, returns: { type: 'object', properties: { temperature: { type: 'number' }, condition: { type: 'string' }, humidity: { type: 'number' } } }, permissions: ['weather:read'], rateLimit: { requests: 100, window: '1h' }}Capability Registry
Section titled “Capability Registry”class CapabilityRegistry { private actions = new Map<string, Action>()
register(action: Action): void { // Validate action schema this.validateActionSchema(action)
// Register action this.actions.set(action.id, action) }
getAction(id: string): Action | undefined { return this.actions.get(id) }
listActions(filter?: ActionFilter): Action[] { let actions = Array.from(this.actions.values())
if (filter?.permissions) { actions = actions.filter(a => filter.permissions.some(p => a.permissions.includes(p)) ) }
return actions }
async execute( actionId: string, params: Record<string, any>, context: ExecutionContext ): Promise<ActionResult> { const action = this.getAction(actionId) if (!action) throw new Error(`Action ${actionId} not found`)
// Check permissions this.checkPermissions(action, context.user)
// Validate parameters this.validateParameters(action, params)
// Check rate limit await this.checkRateLimit(action, context.user)
// Execute action try { const result = await this.handlers.get(actionId)(params, context) return { success: true, data: result } } catch (error) { return { success: false, error: error.message } } }}class CapabilityRegistry: def __init__(self): self.actions: Dict[str, Action] = {} self.handlers: Dict[str, Callable] = {}
def register(self, action: Action, handler: Callable) -> None: """Register an action and its handler""" # Validate action schema self.validate_action_schema(action)
# Register action and handler self.actions[action.id] = action self.handlers[action.id] = handler
def list_actions(self, filter: Optional[ActionFilter] = None) -> List[Action]: """List available actions""" actions = list(self.actions.values())
if filter and filter.permissions: actions = [ a for a in actions if any(p in a.permissions for p in filter.permissions) ]
return actions
async def execute( self, action_id: str, params: Dict[str, Any], context: ExecutionContext ) -> ActionResult: """Execute an action""" action = self.actions.get(action_id) if not action: raise ValueError(f"Action {action_id} not found")
# Check permissions self.check_permissions(action, context.user)
# Validate parameters self.validate_parameters(action, params)
# Check rate limit await self.check_rate_limit(action, context.user)
# Execute action try: handler = self.handlers[action_id] result = await handler(params, context) return ActionResult(success=True, data=result) except Exception as error: return ActionResult(success=False, error=str(error))Built-in Capabilities
Section titled “Built-in Capabilities”HTTP Requests
const httpGetAction: Action = { id: 'http-get', name: 'HTTP GET', description: 'Perform HTTP GET request', parameters: { type: 'object', properties: { url: { type: 'string', format: 'uri' }, headers: { type: 'object' }, timeout: { type: 'number', default: 30000 } }, required: ['url'] }, permissions: ['http:read']}
async function handleHttpGet( params: { url: string; headers?: Record<string, string> }, context: ExecutionContext): Promise<any> { const response = await fetch(params.url, { method: 'GET', headers: params.headers, signal: AbortSignal.timeout(params.timeout || 30000) })
return { status: response.status, headers: Object.fromEntries(response.headers), body: await response.json() }}Database Queries
const dbQueryAction: Action = { id: 'db-query', name: 'Database Query', description: 'Execute database query', parameters: { type: 'object', properties: { query: { type: 'string' }, params: { type: 'array' } }, required: ['query'] }, permissions: ['database:read']}
async function handleDbQuery( params: { query: string; params?: any[] }, context: ExecutionContext): Promise<any[]> { // Validate query (prevent SQL injection) validateSafeQuery(params.query)
// Execute with parameter binding const results = await context.db.query( params.query, params.params || [] )
return results.rows}File Operations
const readFileAction: Action = { id: 'file-read', name: 'Read File', description: 'Read file contents', parameters: { type: 'object', properties: { path: { type: 'string' }, encoding: { type: 'string', default: 'utf-8' } }, required: ['path'] }, permissions: ['file:read']}
async function handleReadFile( params: { path: string; encoding: string }, context: ExecutionContext): Promise<string> { // Validate path (prevent directory traversal) const safePath = validateSafePath(params.path, context.workspace)
// Read file const content = await fs.promises.readFile( safePath, { encoding: params.encoding } )
return content}const sendEmailAction: Action = { id: 'email-send', name: 'Send Email', description: 'Send email message', parameters: { type: 'object', properties: { to: { type: 'string', format: 'email' }, subject: { type: 'string' }, body: { type: 'string' }, html: { type: 'boolean', default: false } }, required: ['to', 'subject', 'body'] }, permissions: ['email:send'], rateLimit: { requests: 10, window: '1h' }}Configuration Example
Section titled “Configuration Example”{ "layers": { "capabilities": { "actions": [ { "id": "get-weather", "name": "Get Weather", "description": "Fetch weather data", "handler": "./handlers/weather.js", "parameters": { "location": { "type": "string", "required": true } }, "permissions": ["weather:read"], "rateLimit": { "requests": 100, "window": "1h" } }, { "id": "send-notification", "name": "Send Notification", "handler": "./handlers/notify.js", "parameters": { "message": { "type": "string", "required": true }, "channel": { "type": "string", "enum": ["email", "sms", "push"] } }, "permissions": ["notify:send"] } ], "security": { "defaultPermissions": [], "requireAuth": true, "auditLog": true } } }}Best Practices
Section titled “Best Practices”Action Design
✅ DO:
- Keep actions focused and atomic
- Provide clear descriptions
- Define comprehensive parameter schemas
- Document return values
❌ DON’T:
- Create overly complex actions
- Skip parameter validation
- Return inconsistent data structures
Security
✅ DO:
- Implement permission checks
- Validate all inputs
- Use rate limiting
- Audit all action executions
- Sanitize outputs
❌ DON’T:
- Trust user inputs
- Allow unlimited executions
- Expose internal errors
- Skip authorization
Error Handling
✅ DO:
- Return structured errors
- Log failures with context
- Implement retries for transient errors
- Provide helpful error messages
❌ DON’T:
- Throw raw exceptions
- Lose error context
- Retry on permanent failures
Testing
Section titled “Testing”describe('Capabilities Layer', () => { it('should execute action with valid params', async () => { const registry = new CapabilityRegistry() registry.register(getWeatherAction, handleGetWeather)
const result = await registry.execute( 'get-weather', { location: 'Paris' }, mockContext )
expect(result.success).toBe(true) expect(result.data).toHaveProperty('temperature') })
it('should reject action without permission', async () => { const registry = new CapabilityRegistry() registry.register(getWeatherAction, handleGetWeather)
const contextWithoutPermission = { user: { permissions: [] } }
await expect( registry.execute('get-weather', { location: 'Paris' }, contextWithoutPermission) ).rejects.toThrow('Permission denied') })
it('should enforce rate limits', async () => { const registry = new CapabilityRegistry() registry.register(getWeatherAction, handleGetWeather)
// Execute 100 times (the limit) for (let i = 0; i < 100; i++) { await registry.execute('get-weather', { location: 'Paris' }, mockContext) }
// 101st should fail await expect( registry.execute('get-weather', { location: 'Paris' }, mockContext) ).rejects.toThrow('Rate limit exceeded') })})