From 4a66c5022f08d5bd920473b0b61a99e5dd8dcd72 Mon Sep 17 00:00:00 2001 From: yung algorithm <113615973+yungalgo@users.noreply.github.com> Date: Sat, 13 Sep 2025 16:43:40 +0900 Subject: [PATCH 1/3] what's next section => see also section --- agents/character-interface.mdx | 2 +- agents/memory-and-state.mdx | 2 +- agents/personality-and-behavior.mdx | 2 +- agents/runtime-and-lifecycle.mdx | 2 +- guides/add-multiple-agents.mdx | 2 +- guides/contribute-to-core.mdx | 2 +- guides/create-a-plugin.mdx | 2 +- guides/customize-an-agent.mdx | 2 +- guides/deploy-a-project.mdx | 2 +- guides/publish-a-plugin.mdx | 2 +- guides/test-a-project.mdx | 2 +- plugins/architecture.mdx | 2 +- plugins/components.mdx | 2 +- plugins/development.mdx | 2 +- plugins/migration.mdx | 2 +- plugins/patterns.mdx | 2 +- plugins/reference.mdx | 2 +- plugins/schemas.mdx | 2 +- projects/environment-variables.mdx | 2 +- projects/overview.mdx | 2 +- runtime/core.mdx | 2 +- runtime/events.mdx | 2 +- runtime/memory.mdx | 2 +- runtime/messaging.mdx | 2 +- runtime/models.mdx | 2 +- runtime/providers.mdx | 2 +- runtime/services.mdx | 2 +- runtime/sessions-api.mdx | 2 +- 28 files changed, 28 insertions(+), 28 deletions(-) diff --git a/agents/character-interface.mdx b/agents/character-interface.mdx index e996611..8822e08 100644 --- a/agents/character-interface.mdx +++ b/agents/character-interface.mdx @@ -505,7 +505,7 @@ export const character: Character = { }; ``` -## What's Next? +## See Also diff --git a/agents/memory-and-state.mdx b/agents/memory-and-state.mdx index b92557b..2f939b9 100644 --- a/agents/memory-and-state.mdx +++ b/agents/memory-and-state.mdx @@ -981,7 +981,7 @@ class MemoryMonitor { } ``` -## What's Next? +## See Also diff --git a/agents/personality-and-behavior.mdx b/agents/personality-and-behavior.mdx index 8b4aab0..28ab078 100644 --- a/agents/personality-and-behavior.mdx +++ b/agents/personality-and-behavior.mdx @@ -872,7 +872,7 @@ describe('Personality Consistency', () => { **Guides**: [Customize an Agent](/guides/customize-an-agent) | [Multiple Agents](/guides/add-multiple-agents) -## What's Next? +## See Also diff --git a/agents/runtime-and-lifecycle.mdx b/agents/runtime-and-lifecycle.mdx index 7f011ba..8f0196c 100644 --- a/agents/runtime-and-lifecycle.mdx +++ b/agents/runtime-and-lifecycle.mdx @@ -1435,7 +1435,7 @@ async function debugPluginLoading(runtime: IAgentRuntime, pluginName: string) { **Guide**: [Customize an Agent](/guides/customize-an-agent) -## What's Next? +## See Also diff --git a/guides/add-multiple-agents.mdx b/guides/add-multiple-agents.mdx index 9d08bc9..b5604a9 100644 --- a/guides/add-multiple-agents.mdx +++ b/guides/add-multiple-agents.mdx @@ -291,7 +291,7 @@ Say something, and hear your literary duo respond and converse: It's working! Your agents are now conversing with their own unique personalities and voices! -## What's next? +## See Also Now that you know how to add multiple agents to a single project, you can add as many as you like, all with completely custom sets of plugins and personalities. Here's what's next: diff --git a/guides/contribute-to-core.mdx b/guides/contribute-to-core.mdx index adfe9ab..f6d7555 100644 --- a/guides/contribute-to-core.mdx +++ b/guides/contribute-to-core.mdx @@ -214,7 +214,7 @@ Core developers are active in Discord and can provide guidance on whether your p --- -## What's Next? +## See Also diff --git a/guides/create-a-plugin.mdx b/guides/create-a-plugin.mdx index d1bd907..21f7d0f 100644 --- a/guides/create-a-plugin.mdx +++ b/guides/create-a-plugin.mdx @@ -408,7 +408,7 @@ The possibilities are endless! --- -## What's Next? +## See Also diff --git a/guides/customize-an-agent.mdx b/guides/customize-an-agent.mdx index b1c5693..1543626 100644 --- a/guides/customize-an-agent.mdx +++ b/guides/customize-an-agent.mdx @@ -267,7 +267,7 @@ Your Shakespeare bot is now live! Invite it to your Discord server and try chatt Shakespeare bot responding in Discord with Shakespearean language -## What's next? +## See Also Here are some logical next-steps to continue your agent dev journey: diff --git a/guides/deploy-a-project.mdx b/guides/deploy-a-project.mdx index 748f41a..4d23f48 100644 --- a/guides/deploy-a-project.mdx +++ b/guides/deploy-a-project.mdx @@ -395,7 +395,7 @@ Phala offers secure deployment with excellent elizaOS integration and a good CLI --- -## What's Next? +## See Also diff --git a/guides/publish-a-plugin.mdx b/guides/publish-a-plugin.mdx index dc3670e..21ec884 100644 --- a/guides/publish-a-plugin.mdx +++ b/guides/publish-a-plugin.mdx @@ -288,7 +288,7 @@ The elizaOS registry automatically syncs with npm updates, so you don't need to --- -## What's Next? +## See Also diff --git a/guides/test-a-project.mdx b/guides/test-a-project.mdx index 8cae6ad..179d534 100644 --- a/guides/test-a-project.mdx +++ b/guides/test-a-project.mdx @@ -137,7 +137,7 @@ elizaos test --name "multi-agent" For complete test runner options, see the [CLI Test Reference](/cli-reference/test). -## What's Next? +## See Also diff --git a/plugins/architecture.mdx b/plugins/architecture.mdx index dded476..79be709 100644 --- a/plugins/architecture.mdx +++ b/plugins/architecture.mdx @@ -306,7 +306,7 @@ Database integration and management for elizaOS. Features: 7. **Priority Management**: Set appropriate priorities for plugins that need to load early 8. **Configuration**: Use `runtime.getSetting()` for consistent configuration access -## What's Next? +## See Also diff --git a/plugins/components.mdx b/plugins/components.mdx index e96aa7f..2327e83 100644 --- a/plugins/components.mdx +++ b/plugins/components.mdx @@ -618,7 +618,7 @@ export const myPlugin: Plugin = { **Guide**: [Create a Plugin](/guides/create-a-plugin) -## What's Next? +## See Also diff --git a/plugins/development.mdx b/plugins/development.mdx index 33f1cbd..2e38c0d 100644 --- a/plugins/development.mdx +++ b/plugins/development.mdx @@ -803,7 +803,7 @@ Create `.vscode/launch.json`: **Solution**: Make sure your `tsconfig.json` has proper module resolution settings for Bun. -## What's Next? +## See Also diff --git a/plugins/migration.mdx b/plugins/migration.mdx index aad5788..b6fcff7 100644 --- a/plugins/migration.mdx +++ b/plugins/migration.mdx @@ -752,7 +752,7 @@ The migration from 0.x to 1.x involves: Take your time, test thoroughly, and don't hesitate to ask for help in the community! -## What's Next? +## See Also diff --git a/plugins/patterns.mdx b/plugins/patterns.mdx index 16f9ecd..270ba11 100644 --- a/plugins/patterns.mdx +++ b/plugins/patterns.mdx @@ -918,7 +918,7 @@ const myPlugin: Plugin = { **Guides**: [Create a Plugin](/guides/create-a-plugin) | [Publish a Plugin](/guides/publish-a-plugin) -## What's Next? +## See Also diff --git a/plugins/reference.mdx b/plugins/reference.mdx index 3c9ad35..b323a8a 100644 --- a/plugins/reference.mdx +++ b/plugins/reference.mdx @@ -597,7 +597,7 @@ IGNORE_BOOTSTRAP?: string; CHANNEL_IDS?: string; ``` -## What's Next? +## See Also diff --git a/plugins/schemas.mdx b/plugins/schemas.mdx index e92894a..5c14152 100644 --- a/plugins/schemas.mdx +++ b/plugins/schemas.mdx @@ -611,7 +611,7 @@ To add custom schema to an elizaOS plugin: elizaOS handles the rest - migrations, database connections, and making your data available across all agents in the system. -## What's Next? +## See Also diff --git a/projects/environment-variables.mdx b/projects/environment-variables.mdx index 0293299..582b8e1 100644 --- a/projects/environment-variables.mdx +++ b/projects/environment-variables.mdx @@ -143,7 +143,7 @@ For a complete list of all available environment variables including database co - `.env.example` - Template file with example/placeholder values (safe to commit as reference) -## What's Next? +## See Also diff --git a/projects/overview.mdx b/projects/overview.mdx index a327ddf..cfcf26b 100644 --- a/projects/overview.mdx +++ b/projects/overview.mdx @@ -400,7 +400,7 @@ See the [Deploy a Project](/guides/deploy-a-project) guide for detailed instruct -## What's Next? +## See Also diff --git a/runtime/core.mdx b/runtime/core.mdx index e87f595..a9554b8 100644 --- a/runtime/core.mdx +++ b/runtime/core.mdx @@ -387,7 +387,7 @@ The runtime provides multiple integration points: - **Database**: Custom database adapters - **API**: HTTP endpoints through routes -## What's Next? +## See Also diff --git a/runtime/events.mdx b/runtime/events.mdx index c2dd15f..cc2676e 100644 --- a/runtime/events.mdx +++ b/runtime/events.mdx @@ -678,7 +678,7 @@ function throttleEvents(eventType: EventType, delay: number) { - **Retries**: Implement retry logic for transient failures - **Logging**: Log errors with context -## What's Next? +## See Also diff --git a/runtime/memory.mdx b/runtime/memory.mdx index 2d37b11..acdfcad 100644 --- a/runtime/memory.mdx +++ b/runtime/memory.mdx @@ -606,7 +606,7 @@ async function compressMemories(runtime: IAgentRuntime, roomId: UUID) { - **Versioning**: Track memory updates and changes - **Backup**: Regular backup of critical memories -## What's Next? +## See Also diff --git a/runtime/messaging.mdx b/runtime/messaging.mdx index 90500d4..7ee6d7b 100644 --- a/runtime/messaging.mdx +++ b/runtime/messaging.mdx @@ -556,7 +556,7 @@ class SocketIOService extends Service { A complete working example demonstrating Socket.IO integration with Eliza, including real-time messaging, agent participation management, and comprehensive error handling. -## What's Next? +## See Also diff --git a/runtime/models.mdx b/runtime/models.mdx index c921d00..577e90d 100644 --- a/runtime/models.mdx +++ b/runtime/models.mdx @@ -699,7 +699,7 @@ async function modelCallWithFallback( - **Quality Metrics**: Track response quality - **Cost Analysis**: Analyze cost per request -## What's Next? +## See Also diff --git a/runtime/providers.mdx b/runtime/providers.mdx index 4738e57..0bda402 100644 --- a/runtime/providers.mdx +++ b/runtime/providers.mdx @@ -533,7 +533,7 @@ async function debugComposeState(runtime: IAgentRuntime, message: Memory, includ } ``` -## What's Next? +## See Also diff --git a/runtime/services.mdx b/runtime/services.mdx index 5b70ea9..5a74006 100644 --- a/runtime/services.mdx +++ b/runtime/services.mdx @@ -812,7 +812,7 @@ it('should notify through multiple channels', async () => { }); ``` -## What's Next? +## See Also diff --git a/runtime/sessions-api.mdx b/runtime/sessions-api.mdx index daa8086..6a6a021 100644 --- a/runtime/sessions-api.mdx +++ b/runtime/sessions-api.mdx @@ -1950,7 +1950,7 @@ class SessionsAPIClient: return response.json() ``` -## What's Next? +## See Also From 9eba720eca3d881c06e4f05bbbef2980563ad3b3 Mon Sep 17 00:00:00 2001 From: yung algorithm <113615973+yungalgo@users.noreply.github.com> Date: Sat, 13 Sep 2025 16:43:48 +0900 Subject: [PATCH 2/3] add plugins/webhooks-and-routes page --- docs.json | 1 + plugins/webhooks-and-routes.mdx | 734 ++++++++++++++++++++++++++++++++ 2 files changed, 735 insertions(+) create mode 100644 plugins/webhooks-and-routes.mdx diff --git a/docs.json b/docs.json index 38125b6..f0ae1a1 100644 --- a/docs.json +++ b/docs.json @@ -73,6 +73,7 @@ "plugins/components", "plugins/development", "plugins/patterns", + "plugins/webhooks-and-routes", "plugins/schemas", "plugins/migration", "plugins/reference" diff --git a/plugins/webhooks-and-routes.mdx b/plugins/webhooks-and-routes.mdx new file mode 100644 index 0000000..22bf045 --- /dev/null +++ b/plugins/webhooks-and-routes.mdx @@ -0,0 +1,734 @@ +--- +title: "Webhooks and Routes" +description: "How to expose HTTP endpoints and send messages as agents using plugin routes" +--- + +## Overview + +Plugins in elizaOS can expose HTTP routes that act as webhooks, allowing external services to trigger agent actions and send messages on behalf of agents. This enables powerful integrations with third-party services, automated workflows, and custom APIs. + +## Defining Routes in Plugins + +Routes are defined in your plugin's main export using the `routes` property. Each route specifies an HTTP method, path, and handler function. + +### Route Interface + +```typescript +type Route = { + type: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'STATIC'; + path: string; + handler?: (req: any, res: any, runtime: IAgentRuntime) => Promise; + filePath?: string; // For static file serving + public?: boolean; // Whether route is publicly accessible + name?: string; // Optional route name + isMultipart?: boolean; // For file upload endpoints +}; +``` + +### Basic Route Example + +```typescript +import type { Plugin } from '@elizaos/core'; + +export const myPlugin: Plugin = { + name: 'webhook-plugin', + description: 'Plugin with webhook endpoints', + + routes: [ + { + name: 'webhook-endpoint', + path: '/webhook', + type: 'POST', + handler: async (req, res, runtime) => { + // Access request data + const { event, data } = req.body; + + // Process webhook + console.log(`Received webhook: ${event}`); + + // Send response + res.json({ + success: true, + message: 'Webhook processed' + }); + } + } + ] +}; +``` + +## Sending Messages as an Agent + +The most powerful use case for plugin routes is sending messages on behalf of the agent. This allows external services to make the agent speak in any conversation. + +### Using the Messaging API + +To send a message as an agent, your route handler needs to make a POST request to the messaging submit endpoint: + +```typescript +{ + name: 'send-message-webhook', + path: '/send-agent-message', + type: 'POST', + handler: async (req, res, runtime) => { + try { + const { channelId, message, metadata } = req.body; + + // Validate input + if (!channelId || !message) { + return res.status(400).json({ + success: false, + error: 'channelId and message are required' + }); + } + + // Prepare message payload + const messagePayload = { + channel_id: channelId, + server_id: '00000000-0000-0000-0000-000000000000', // Default server + author_id: runtime.agentId, + content: message, + source_type: 'agent_response', + raw_message: { + text: message, + thought: metadata?.thought, + actions: metadata?.actions || [] + }, + metadata: { + agent_id: runtime.agentId, + agentName: runtime.character.name, + ...metadata + } + }; + + // Send message via messaging API + const baseUrl = runtime.getSetting('SERVER_URL') || 'http://localhost:3000'; + const response = await fetch(`${baseUrl}/api/messaging/submit`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + // Add any required auth headers + }, + body: JSON.stringify(messagePayload) + }); + + if (!response.ok) { + throw new Error(`Failed to send message: ${response.statusText}`); + } + + const result = await response.json(); + + res.json({ + success: true, + messageId: result.data.id, + message: 'Message sent successfully' + }); + + } catch (error) { + console.error('Error sending agent message:', error); + res.status(500).json({ + success: false, + error: 'Failed to send message' + }); + } + } +} +``` + +### Complete Example: GitHub Webhook Integration + +Here's a complete example of a plugin that receives GitHub webhooks and makes the agent comment on events: + +```typescript +import type { Plugin, Route } from '@elizaos/core'; +import { validateUuid } from '@elizaos/core'; + +const githubWebhookRoute: Route = { + name: 'github-webhook', + path: '/github/webhook', + type: 'POST', + handler: async (req, res, runtime) => { + try { + // Verify GitHub signature (optional but recommended) + const signature = req.headers['x-hub-signature-256']; + // ... signature verification logic ... + + // Parse GitHub event + const event = req.headers['x-github-event']; + const payload = req.body; + + // Determine channel to send message to + const channelId = runtime.getSetting('GITHUB_NOTIFICATION_CHANNEL'); + if (!channelId || !validateUuid(channelId)) { + console.error('No valid channel configured for GitHub notifications'); + return res.status(200).json({ ok: true }); // Return 200 to prevent GitHub retries + } + + // Format message based on event type + let message = ''; + switch (event) { + case 'push': + message = `🔄 New push to ${payload.repository.full_name} by ${payload.pusher.name}:\n` + + `Branch: ${payload.ref.replace('refs/heads/', '')}\n` + + `Commits: ${payload.commits.length}\n` + + `Message: "${payload.head_commit.message}"`; + break; + + case 'pull_request': + const pr = payload.pull_request; + message = `🔀 Pull Request ${payload.action} in ${payload.repository.full_name}:\n` + + `#${pr.number}: ${pr.title}\n` + + `Author: ${pr.user.login}\n` + + `${pr.html_url}`; + break; + + case 'issues': + const issue = payload.issue; + message = `📝 Issue ${payload.action} in ${payload.repository.full_name}:\n` + + `#${issue.number}: ${issue.title}\n` + + `Author: ${issue.user.login}\n` + + `${issue.html_url}`; + break; + + default: + message = `GitHub event: ${event} on ${payload.repository?.full_name || 'unknown repo'}`; + } + + // Send message as agent + const messagePayload = { + channel_id: channelId, + server_id: '00000000-0000-0000-0000-000000000000', + author_id: runtime.agentId, + content: message, + source_type: 'agent_response', + raw_message: { + text: message, + actions: ['GITHUB_NOTIFICATION'] + }, + metadata: { + agent_id: runtime.agentId, + agentName: runtime.character.name, + githubEvent: event, + repository: payload.repository?.full_name + } + }; + + // Submit message + const baseUrl = runtime.getSetting('SERVER_URL') || 'http://localhost:3000'; + const submitResponse = await fetch(`${baseUrl}/api/messaging/submit`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(messagePayload) + }); + + if (!submitResponse.ok) { + throw new Error(`Failed to submit message: ${submitResponse.statusText}`); + } + + res.json({ + success: true, + message: 'GitHub webhook processed' + }); + + } catch (error) { + console.error('GitHub webhook error:', error); + res.status(500).json({ + success: false, + error: 'Failed to process webhook' + }); + } + } +}; + +export const githubPlugin: Plugin = { + name: 'github-integration', + description: 'GitHub webhook integration for agent notifications', + routes: [githubWebhookRoute], + + init: async (config, runtime) => { + const channelId = runtime.getSetting('GITHUB_NOTIFICATION_CHANNEL'); + if (!channelId) { + console.warn('GITHUB_NOTIFICATION_CHANNEL not configured'); + } + console.log('GitHub integration initialized'); + } +}; +``` + +## Advanced Patterns + +### Authenticated Webhooks + +For secure webhook endpoints, implement authentication: + +```typescript +{ + name: 'secure-webhook', + path: '/secure/webhook', + type: 'POST', + handler: async (req, res, runtime) => { + // Check for API key + const apiKey = req.headers['x-api-key']; + const expectedKey = runtime.getSetting('WEBHOOK_API_KEY'); + + if (!apiKey || apiKey !== expectedKey) { + return res.status(401).json({ + success: false, + error: 'Unauthorized' + }); + } + + // Process authenticated request + // ... + } +} +``` + +### File Upload Webhooks + +For endpoints that accept file uploads: + +```typescript +{ + name: 'upload-webhook', + path: '/upload', + type: 'POST', + isMultipart: true, + handler: async (req, res, runtime) => { + const file = req.file; // Access uploaded file + const { description } = req.body; + + // Process file and send agent message + const message = `📎 New file uploaded: ${file.originalname}\n${description}`; + + // Send message with file attachment + const messagePayload = { + channel_id: channelId, + author_id: runtime.agentId, + content: message, + metadata: { + attachments: [{ + filename: file.originalname, + size: file.size, + mimeType: file.mimetype + }] + } + }; + + // Submit message... + } +} +``` + +### Scheduled Messages + +Create endpoints that schedule future agent messages: + +```typescript +{ + name: 'schedule-message', + path: '/schedule', + type: 'POST', + handler: async (req, res, runtime) => { + const { channelId, message, sendAt } = req.body; + + // Calculate delay + const delay = new Date(sendAt).getTime() - Date.now(); + + if (delay <= 0) { + return res.status(400).json({ + error: 'sendAt must be in the future' + }); + } + + // Schedule message + setTimeout(async () => { + // Send message as agent + await sendAgentMessage(runtime, channelId, message); + }, delay); + + res.json({ + success: true, + message: `Message scheduled for ${sendAt}` + }); + } +} +``` + +## Route Registration and Access + +### How Routes Are Registered + +When your plugin is loaded: + +1. The runtime registers all routes from the `routes` array +2. Routes are accessible at the path you specify +3. The runtime provides the route handler with the agent's runtime instance + +### Accessing Your Routes + +Routes are available at: +- **Development**: `http://localhost:3000{path}` +- **Production**: `https://your-domain.com{path}` + +With query parameter for agent selection: +- `http://localhost:3000/webhook?agentId=YOUR_AGENT_ID` + +## Best Practices + +### 1. Security + +- **Always validate input** from webhooks +- **Implement authentication** for sensitive endpoints +- **Verify signatures** from known services (GitHub, Stripe, etc.) +- **Rate limit** webhook endpoints to prevent abuse + +### 2. Error Handling + +- **Return appropriate HTTP status codes** +- **Log errors** for debugging +- **Don't expose internal errors** to webhook callers +- **Return 200 OK** to prevent webhook retries for non-critical errors + +### 3. Message Formatting + +- **Keep messages concise** and relevant +- **Use emoji** sparingly for visual indicators +- **Include links** when referencing external resources +- **Add metadata** for message tracking and debugging + +### 4. Performance + +- **Process webhooks asynchronously** when possible +- **Return responses quickly** (< 5 seconds) +- **Queue heavy processing** tasks +- **Implement timeouts** for external API calls + +## Common Use Cases + +1. **CI/CD Notifications**: Deploy status, build results, test failures +2. **Monitoring Alerts**: System health, error rates, performance metrics +3. **Customer Support**: Ticket creation, status updates, escalations +4. **Social Media**: Mentions, new followers, engagement metrics +5. **E-commerce**: Order updates, inventory alerts, customer inquiries +6. **Calendar Integration**: Meeting reminders, schedule changes +7. **IoT Devices**: Sensor alerts, status updates, command responses + +## Testing Webhooks + +### Local Development + +Use ngrok or similar tools to expose your local server: + +```bash +# Install ngrok +npm install -g ngrok + +# Start your agent +elizaos start + +# In another terminal, expose port 3000 +ngrok http 3000 + +# Use the ngrok URL for webhook configuration +# https://abc123.ngrok.io/webhook +``` + +### Testing with cURL + +```bash +# Test your webhook endpoint +curl -X POST http://localhost:3000/webhook?agentId=YOUR_AGENT_ID \ + -H "Content-Type: application/json" \ + -d '{ + "channelId": "test-channel-id", + "message": "Hello from webhook!", + "metadata": { + "source": "curl-test" + } + }' +``` + +### Testing Webhook Routes + +ElizaOS provides two types of tests for validating webhook functionality: component tests and e2e tests. + +#### Component Tests + +Component tests verify route handler logic in isolation: + +```typescript +// src/__tests__/webhook-routes.test.ts +import { describe, it, expect, beforeEach } from 'bun:test'; +import { webhookPlugin } from '../index'; + +describe('Webhook Plugin Routes', () => { + let runtime: any; + + beforeEach(() => { + runtime = { + agentId: 'test-agent-123', + character: { name: 'TestAgent' }, + getSetting: (key: string) => { + const settings: Record = { + 'GITHUB_NOTIFICATION_CHANNEL': 'test-channel-123', + 'SERVER_URL': 'http://localhost:3000' + }; + return settings[key]; + } + }; + }); + + it('should handle GitHub webhook and return success', async () => { + const githubRoute = webhookPlugin.routes?.find(r => r.name === 'github-webhook'); + expect(githubRoute).toBeDefined(); + + const mockReq = { + headers: { + 'x-github-event': 'push', + 'x-hub-signature-256': 'sha256=test' + }, + body: { + repository: { full_name: 'test/repo' }, + pusher: { name: 'testuser' }, + ref: 'refs/heads/main', + commits: [{ message: 'Test commit' }], + head_commit: { message: 'Test commit' } + } + }; + + let responseData: any = null; + const mockRes = { + json: (data: any) => { responseData = data; }, + status: (code: number) => mockRes + }; + + // Mock fetch for the messaging API call + const originalFetch = global.fetch; + global.fetch = async () => ({ + ok: true, + json: async () => ({ success: true, data: { id: 'msg-123' } }) + }) as Response; + + await githubRoute!.handler!(mockReq, mockRes, runtime); + + expect(responseData).toEqual({ + success: true, + message: 'GitHub webhook processed' + }); + + // Restore fetch + global.fetch = originalFetch; + }); + + it('should validate required fields in send-message webhook', async () => { + const sendMessageRoute = webhookPlugin.routes?.find(r => r.name === 'send-message-webhook'); + expect(sendMessageRoute).toBeDefined(); + + const mockReq = { + body: {} // Missing required fields + }; + + let responseData: any = null; + let statusCode: number = 200; + const mockRes = { + json: (data: any) => { responseData = data; }, + status: (code: number) => { statusCode = code; return mockRes; } + }; + + await sendMessageRoute!.handler!(mockReq, mockRes, runtime); + + expect(statusCode).toBe(400); + expect(responseData).toEqual({ + success: false, + error: 'channelId and message are required' + }); + }); +}); +``` + +#### E2E Tests + +E2E tests validate the complete webhook flow with a live agent runtime: + +```typescript +// src/__tests__/webhook-e2e.test.ts +import { TestSuite } from '@elizaos/core'; + +export const webhookE2ETests: TestSuite = { + name: 'Webhook Integration E2E Tests', + tests: [ + { + name: 'should process webhook and send agent message', + fn: async (runtime) => { + // Create a test channel + const testChannel = await runtime.createMemory({ + id: 'test-channel-webhook', + roomId: 'test-room-webhook', + entityId: 'test-entity', + content: { + text: 'Test channel for webhook testing', + source: 'test' + } + }, 'channels'); + + // Test the webhook endpoint via HTTP request + const webhookPayload = { + channelId: testChannel.roomId, + message: 'Hello from webhook integration test!', + metadata: { + source: 'e2e-test', + testRun: true + } + }; + + // Make HTTP request to webhook endpoint + const response = await fetch(`http://localhost:3000/send-agent-message?agentId=${runtime.agentId}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(webhookPayload) + }); + + expect(response.ok).toBe(true); + + const result = await response.json(); + expect(result.success).toBe(true); + expect(result.messageId).toBeDefined(); + + // Verify the message was actually sent + const messages = await runtime.getMemories({ + roomId: testChannel.roomId, + tableName: 'messages', + count: 10 + }); + + const webhookMessage = messages.find(m => + m.content.text === 'Hello from webhook integration test!' && + m.metadata?.source === 'e2e-test' + ); + + expect(webhookMessage).toBeDefined(); + expect(webhookMessage.entityId).toBe(runtime.agentId); + } + }, + { + name: 'should handle GitHub webhook events', + fn: async (runtime) => { + // Set required environment variable + const channelId = 'github-test-channel'; + + const githubPayload = { + repository: { full_name: 'elizaos/test-repo' }, + pusher: { name: 'testdev' }, + ref: 'refs/heads/main', + commits: [{ message: 'Add new feature' }], + head_commit: { message: 'Add new feature' } + }; + + const response = await fetch(`http://localhost:3000/github/webhook?agentId=${runtime.agentId}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-github-event': 'push' + }, + body: JSON.stringify(githubPayload) + }); + + expect(response.ok).toBe(true); + + const result = await response.json(); + expect(result.success).toBe(true); + expect(result.message).toBe('GitHub webhook processed'); + } + } + ] +}; + +// Export for plugin registration +export default webhookE2ETests; +``` + +#### Adding Tests to Your Plugin + +Include tests in your plugin definition: + +```typescript +// src/index.ts +import { webhookE2ETests } from './__tests__/webhook-e2e.test'; + +export const webhookPlugin: Plugin = { + name: 'webhook-integration', + description: 'Plugin with webhook endpoints', + routes: [/* your routes */], + tests: [webhookE2ETests] // Add your test suites +}; +``` + +#### Running Tests + +Use the ElizaOS test command to run your webhook tests: + +```bash +# Run component tests only +elizaos test --type component + +# Run e2e tests only +elizaos test --type e2e + +# Run all tests +elizaos test + +# Run tests for specific plugin +elizaos test --name "Webhook Integration" +``` + +## Troubleshooting + +### Common Issues + +1. **Route not found**: Ensure your plugin is loaded and the path is correct +2. **Authentication errors**: Check API keys and headers +3. **Message not appearing**: Verify channel ID and agent permissions +4. **Timeout errors**: Increase timeout or process asynchronously +5. **CORS issues**: Configure CORS headers in your route handler + +### Debug Logging + +Enable debug logging in your route handlers: + +```typescript +handler: async (req, res, runtime) => { + console.log('[Webhook] Received:', { + method: req.method, + path: req.path, + headers: req.headers, + body: req.body + }); + + // ... handler logic ... +} +``` + +## See Also + + + + Understanding the plugin system + + + + Creating plugins + + + + Messaging API endpoints + + + + Real-time messaging infrastructure + + From 0f3928f3875f721d22f611a6408632918c7fa32e Mon Sep 17 00:00:00 2001 From: yung algorithm <113615973+yungalgo@users.noreply.github.com> Date: Sat, 13 Sep 2025 16:48:13 +0900 Subject: [PATCH 3/3] Update webhooks-and-routes.mdx --- plugins/webhooks-and-routes.mdx | 42 +++++---------------------------- 1 file changed, 6 insertions(+), 36 deletions(-) diff --git a/plugins/webhooks-and-routes.mdx b/plugins/webhooks-and-routes.mdx index 22bf045..50b773d 100644 --- a/plugins/webhooks-and-routes.mdx +++ b/plugins/webhooks-and-routes.mdx @@ -459,8 +459,7 @@ ElizaOS provides two types of tests for validating webhook functionality: compon Component tests verify route handler logic in isolation: -```typescript -// src/__tests__/webhook-routes.test.ts +```typescript src/__tests__/webhook-routes.test.ts import { describe, it, expect, beforeEach } from 'bun:test'; import { webhookPlugin } from '../index'; @@ -553,8 +552,7 @@ describe('Webhook Plugin Routes', () => { E2E tests validate the complete webhook flow with a live agent runtime: -```typescript -// src/__tests__/webhook-e2e.test.ts +```typescript src/__tests__/webhook-e2e.test.ts import { TestSuite } from '@elizaos/core'; export const webhookE2ETests: TestSuite = { @@ -656,8 +654,7 @@ export default webhookE2ETests; Include tests in your plugin definition: -```typescript -// src/index.ts +```typescript src/index.ts import { webhookE2ETests } from './__tests__/webhook-e2e.test'; export const webhookPlugin: Plugin = { @@ -686,33 +683,6 @@ elizaos test elizaos test --name "Webhook Integration" ``` -## Troubleshooting - -### Common Issues - -1. **Route not found**: Ensure your plugin is loaded and the path is correct -2. **Authentication errors**: Check API keys and headers -3. **Message not appearing**: Verify channel ID and agent permissions -4. **Timeout errors**: Increase timeout or process asynchronously -5. **CORS issues**: Configure CORS headers in your route handler - -### Debug Logging - -Enable debug logging in your route handlers: - -```typescript -handler: async (req, res, runtime) => { - console.log('[Webhook] Received:', { - method: req.method, - path: req.path, - headers: req.headers, - body: req.body - }); - - // ... handler logic ... -} -``` - ## See Also @@ -724,11 +694,11 @@ handler: async (req, res, runtime) => { Creating plugins - - Messaging API endpoints + + Build persistent conversations on messaging - + Real-time messaging infrastructure