diff --git a/core-concepts/plugins.mdx b/core-concepts/plugins.mdx
index 536d65e..5ab2e74 100644
--- a/core-concepts/plugins.mdx
+++ b/core-concepts/plugins.mdx
@@ -36,7 +36,7 @@ export const myPlugin: Plugin = {
## Core Plugin: Bootstrap
-Every agent includes `@elizaos/plugin-bootstrap` which provides essential functionality for message handling, knowledge management, and basic agent operations. For detailed information, see the [Bootstrap Plugin Deep Dive](/plugins/bootstrap/complete-documentation).
+Every agent includes `@elizaos/plugin-bootstrap` which provides essential functionality for message handling, knowledge management, and basic agent operations. For detailed information, see the [Bootstrap Plugin Deep Dive](/plugins/bootstrap/complete-documentation.mdx).
## Platform Plugins
diff --git a/docs.json b/docs.json
index 64f7c25..757a7d0 100644
--- a/docs.json
+++ b/docs.json
@@ -327,6 +327,16 @@
"plugins/platform/twitter/examples",
"plugins/platform/twitter/testing-guide"
]
+ },
+ {
+ "group": "Farcaster",
+ "pages": [
+ "plugins/platform/farcaster",
+ "plugins/platform/farcaster/developer-guide",
+ "plugins/platform/farcaster/cast-flow",
+ "plugins/platform/farcaster/examples",
+ "plugins/platform/farcaster/testing-guide"
+ ]
}
]
},
diff --git a/guides/plugin-migration/advanced-migration-guide.mdx b/guides/plugin-migration/advanced-migration-guide.mdx
index 916228e..b0db385 100644
--- a/guides/plugin-migration/advanced-migration-guide.mdx
+++ b/guides/plugin-migration/advanced-migration-guide.mdx
@@ -4,7 +4,7 @@ description: "Advanced breaking changes for evaluators, services, and runtime me
icon: "code"
---
-> **Important**: This guide covers advanced breaking changes for evaluators, services, and runtime methods. Read the main [migration guide](./migration-guide) first for actions, providers, and basic migrations.
+> **Important**: This guide covers advanced breaking changes for evaluators, services, and runtime methods. Read the main [migration guide](./migration-guide.mdx) first for actions, providers, and basic migrations.
## Table of Contents
@@ -458,6 +458,6 @@ class MyClient extends Service {
If you encounter issues not covered in this guide:
-1. Check the main [migration guide](./migration-guide) for basic migrations
+1. Check the main [migration guide](./migration-guide.mdx) for basic migrations
2. Review the v1.x examples in the elizaOS repository for reference implementations
3. Join our Discord community for support
diff --git a/guides/plugin-migration/overview.mdx b/guides/plugin-migration/overview.mdx
index 5df5f31..4e7eb9d 100644
--- a/guides/plugin-migration/overview.mdx
+++ b/guides/plugin-migration/overview.mdx
@@ -10,7 +10,7 @@ This comprehensive guide will walk you through migrating your elizaOS plugins fr
Follow these guides in order for a smooth migration:
-### 1. [Migration Overview](./migration-guide)
+### 1. [Migration Overview](./migration-guide.mdx)
Start here! This guide covers:
- Key differences between 0.x and 1.x
- Breaking changes and new features
@@ -130,4 +130,4 @@ The 1.x architecture brings:
- **Better performance** - Optimized runtime execution
- **Stronger typing** - Catch errors at compile time
-Start with the [Migration Overview](./migration-guide) and work through each guide systematically for the best results!
\ No newline at end of file
+Start with the [Migration Overview](./migration-guide.mdx) and work through each guide systematically for the best results!
\ No newline at end of file
diff --git a/images/icons/farcaster.svg b/images/icons/farcaster.svg
new file mode 100644
index 0000000..d685482
--- /dev/null
+++ b/images/icons/farcaster.svg
@@ -0,0 +1,5 @@
+
diff --git a/plugins/bootstrap.mdx b/plugins/bootstrap.mdx
index 2dd482e..167d500 100644
--- a/plugins/bootstrap.mdx
+++ b/plugins/bootstrap.mdx
@@ -10,16 +10,16 @@ Welcome to the comprehensive documentation for the `@elizaos/plugin-bootstrap` p
### Core Documentation
-- **[Complete Developer Documentation](/plugins/bootstrap/complete-documentation)**
+- **[Complete Developer Documentation](/plugins/bootstrap/complete-documentation.mdx)**
Comprehensive guide covering all components, architecture, and implementation details
-- **[Message Flow Diagram](/plugins/bootstrap/message-flow)**
+- **[Message Flow Diagram](/plugins/bootstrap/message-flow.mdx)**
Step-by-step breakdown of how messages flow through the system with visual diagrams
-- **[Examples & Recipes](/plugins/bootstrap/examples)**
+- **[Examples & Recipes](/plugins/bootstrap/examples.mdx)**
Practical examples, code snippets, and real-world implementations
-- **[Testing Guide](/plugins/bootstrap/testing-guide)**
+- **[Testing Guide](/plugins/bootstrap/testing-guide.mdx)**
Testing patterns, best practices, and comprehensive test examples
diff --git a/plugins/defi/evm.mdx b/plugins/defi/evm.mdx
index 9bb6311..3e8f03c 100644
--- a/plugins/defi/evm.mdx
+++ b/plugins/defi/evm.mdx
@@ -142,7 +142,7 @@ The plugin includes comprehensive error handling for common scenarios:
## Next Steps
-- [Complete Documentation →](./evm/complete-documentation)
-- [DeFi Operations Flow →](./evm/defi-operations-flow)
-- [Examples →](./evm/examples)
-- [Testing Guide →](./evm/testing-guide)
+- [Complete Documentation →](./evm/complete-documentation.mdx)
+- [DeFi Operations Flow →](./evm/defi-operations-flow.mdx)
+- [Examples →](./evm/examples.mdx)
+- [Testing Guide →](./evm/testing-guide.mdx)
diff --git a/plugins/defi/solana.mdx b/plugins/defi/solana.mdx
index fc53ce7..56729a2 100644
--- a/plugins/defi/solana.mdx
+++ b/plugins/defi/solana.mdx
@@ -162,6 +162,6 @@ The plugin includes robust error handling for:
## Next Steps
- [Complete Documentation →](./solana/complete-documentation.mdx)
-- [DeFi Operations Flow →](./solana/defi-operations-flow)
-- [Examples →](./solana/examples)
-- [Testing Guide →](./solana/testing-guide)
+- [DeFi Operations Flow →](./solana/defi-operations-flow.mdx)
+- [Examples →](./solana/examples.mdx)
+- [Testing Guide →](./solana/testing-guide.mdx)
diff --git a/plugins/llm.mdx b/plugins/llm.mdx
index 0692ac9..ebdca48 100644
--- a/plugins/llm.mdx
+++ b/plugins/llm.mdx
@@ -232,14 +232,14 @@ You can also configure API keys per character:
### Cloud Providers
-- [OpenAI Plugin](./openai) - Full-featured with all model types
-- [Anthropic Plugin](./anthropic) - Claude models for text generation
-- [Google GenAI Plugin](./google-genai) - Gemini models
-- [OpenRouter Plugin](./openrouter) - Access to multiple providers
+- [OpenAI Plugin](./openai.mdx) - Full-featured with all model types
+- [Anthropic Plugin](./anthropic.mdx) - Claude models for text generation
+- [Google GenAI Plugin](./google-genai.mdx) - Gemini models
+- [OpenRouter Plugin](./openrouter.mdx) - Access to multiple providers
### Local/Self-Hosted
-- [Ollama Plugin](./ollama) - Run models locally with Ollama
+- [Ollama Plugin](./ollama.mdx) - Run models locally with Ollama
## Best Practices
diff --git a/plugins/overview.mdx b/plugins/overview.mdx
index 6051a82..06390af 100644
--- a/plugins/overview.mdx
+++ b/plugins/overview.mdx
@@ -44,7 +44,7 @@ Blockchain and DeFi integrations for Web3 functionality:
Connect your agent to popular platforms:
-
+
Full Discord integration with voice, commands, and rich interactions.
@@ -56,6 +56,10 @@ Connect your agent to popular platforms:
Twitter/X integration for posting, replying, and timeline management.
+
+
+ Farcaster social network integration with casting and engagement.
+
## LLM Providers
diff --git a/plugins/platform/farcaster.mdx b/plugins/platform/farcaster.mdx
new file mode 100644
index 0000000..cd3a555
--- /dev/null
+++ b/plugins/platform/farcaster.mdx
@@ -0,0 +1,29 @@
+---
+title: "Farcaster Integration"
+description: "Welcome to the comprehensive documentation for the @elizaos/plugin-farcaster package. This index provides organized access to all documentation resources."
+---
+
+
+The @elizaos/plugin-farcaster enables your elizaOS agent to interact with the Farcaster social network through casting, replying, and engaging with the decentralized social protocol.
+
+## 📚 Documentation
+
+- **[Developer Guide](./farcaster/developer-guide.mdx)** - Detailed technical reference
+- **[Cast Flow](./farcaster/cast-flow.mdx)** - Visual guide to cast processing
+- **[Examples](./farcaster/examples.mdx)** - Practical implementation examples
+- **[Testing Guide](./farcaster/testing-guide.mdx)** - Testing strategies and patterns
+
+## 🔧 Configuration
+
+### Required Settings
+- `FARCASTER_NEYNAR_API_KEY` - Neynar API key for authentication
+- `FARCASTER_SIGNER_UUID` - Neynar signer UUID for your account
+- `FARCASTER_FID` - Your Farcaster ID (FID)
+
+### Optional Settings
+- `ENABLE_CAST` - Enable autonomous casting (default: true)
+- `ENABLE_ACTION_PROCESSING` - Enable processing interactions (default: false)
+- `FARCASTER_DRY_RUN` - Test mode without posting (default: false)
+- `CAST_INTERVAL_MIN` - Minimum interval between casts in minutes (default: 90)
+- `CAST_INTERVAL_MAX` - Maximum interval between casts in minutes (default: 180)
+- `ACTION_TIMELINE_TYPE` - Type of timeline to use for actions (default: ForYou)
diff --git a/plugins/platform/farcaster/cast-flow.mdx b/plugins/platform/farcaster/cast-flow.mdx
new file mode 100644
index 0000000..5839033
--- /dev/null
+++ b/plugins/platform/farcaster/cast-flow.mdx
@@ -0,0 +1,330 @@
+---
+title: "Cast Flow"
+description: "Visual guide to understanding how the Farcaster plugin processes casts and interactions"
+---
+
+# Farcaster Cast Flow
+
+## Overview
+
+This document provides a visual and detailed explanation of how the Farcaster plugin processes casts, from initial receipt through evaluation, response generation, and posting.
+
+## Cast Processing Pipeline
+
+```mermaid
+graph TD
+ A[Neynar API Polling] --> B{Event Type}
+ B -->|New Cast| C[Cast Processor]
+ B -->|Reply| D[Reply Handler]
+ B -->|Mention| E[Mention Handler]
+ B -->|Timeline Update| F[Timeline Handler]
+
+ C --> G[Content Analysis]
+ D --> G
+ E --> G
+
+ G --> H{Should Respond?}
+ H -->|Yes| I[Generate Response]
+ H -->|No| J[Store & Skip]
+
+ I --> K[Format Cast]
+ K --> L[Neynar API Call]
+ L --> M[Submit Cast]
+ M --> N[Store Result]
+
+ F --> O[Update Context]
+ J --> O
+ N --> O
+```
+
+## Detailed Flow Stages
+
+### 1. Event Reception
+
+The plugin polls the Neynar API for relevant events and interactions:
+
+```typescript
+// Neynar API polling for mentions and timeline
+setInterval(async () => {
+ const mentions = await neynarClient.fetchMentions({
+ fid: agentFid,
+ limit: 10
+ });
+
+ const timeline = await neynarClient.fetchTimeline({
+ fid: agentFid,
+ type: 'ForYou'
+ });
+
+ await processEvents(mentions, timeline);
+}, FARCASTER_POLL_INTERVAL * 60000);
+```
+
+### 2. Event Classification
+
+Events are classified and routed to appropriate handlers:
+
+```mermaid
+graph LR
+ A[Incoming Event] --> B{Classification}
+ B --> C[Direct Mention]
+ B --> D[Channel Cast]
+ B --> E[Reply Thread]
+ B --> F[Timeline Cast]
+
+ C --> G[Priority Queue]
+ D --> H[Channel Handler]
+ E --> I[Thread Handler]
+ F --> J[Timeline Handler]
+```
+
+### 3. Content Analysis
+
+Each cast undergoes multi-stage analysis:
+
+```mermaid
+graph TD
+ A[Cast Content] --> B[Tokenization]
+ B --> C[Sentiment Analysis]
+ C --> D[Topic Extraction]
+ D --> E[Context Building]
+ E --> F[Relevance Scoring]
+ F --> G{Score Threshold}
+ G -->|High| H[Immediate Response]
+ G -->|Medium| I[Queue for Response]
+ G -->|Low| J[Monitor Only]
+```
+
+### 4. Response Decision Tree
+
+```mermaid
+graph TD
+ A[Cast Received] --> B{Is Mention?}
+ B -->|Yes| C[High Priority Response]
+ B -->|No| D{Is Reply to Agent?}
+ D -->|Yes| E[Continue Conversation]
+ D -->|No| F{Contains Keywords?}
+ F -->|Yes| G{Sentiment Check}
+ F -->|No| H[No Response]
+ G -->|Positive| I[Engage Positively]
+ G -->|Negative| J[Careful Response]
+ G -->|Neutral| K{Random Engagement}
+ K -->|Yes| L[Generate Response]
+ K -->|No| H
+```
+
+### 5. Response Generation
+
+The response generation process:
+
+```typescript
+async function generateResponse(context: CastContext): Promise {
+ // 1. Build conversation history
+ const thread = await getThreadContext(context.parentHash);
+
+ // 2. Extract key topics
+ const topics = extractTopics(context.text);
+
+ // 3. Generate appropriate response
+ const response = await llm.generate({
+ system: character.personality,
+ context: thread,
+ topics: topics,
+ maxLength: 320
+ });
+
+ // 4. Validate and format
+ return formatCast(response);
+}
+```
+
+### 6. Cast Composition
+
+```mermaid
+graph TD
+ A[Generated Text] --> B{Length Check}
+ B -->|Over 320| C[Truncate/Split]
+ B -->|Under 320| D[Format Check]
+ C --> D
+ D --> E{Has Embeds?}
+ E -->|Yes| F[Validate URLs]
+ E -->|No| G[Add Metadata]
+ F --> G
+ G --> H{Channel Cast?}
+ H -->|Yes| I[Add Channel Tag]
+ H -->|No| J[Standard Cast]
+ I --> K[Final Cast Object]
+ J --> K
+```
+
+### 7. Cast Publishing via Neynar
+
+```mermaid
+graph LR
+ A[Cast Object] --> B[Format Request]
+ B --> C[Add Signer UUID]
+ C --> D[Neynar API Call]
+ D --> E{Response}
+ E -->|Success| F[Store Cast Hash]
+ E -->|Error| G[Retry Logic]
+ G --> H[Exponential Backoff]
+ H --> D
+```
+
+## Interaction Patterns
+
+### Reply Chains
+
+```mermaid
+graph TD
+ A[Original Cast] --> B[Agent Reply 1]
+ B --> C[User Reply]
+ C --> D[Agent Reply 2]
+ D --> E[Thread Continuation]
+
+ style A fill:#f9f,stroke:#333,stroke-width:2px
+ style B fill:#9ff,stroke:#333,stroke-width:2px
+ style C fill:#ff9,stroke:#333,stroke-width:2px
+ style D fill:#9ff,stroke:#333,stroke-width:2px
+```
+
+### Channel Participation
+
+```mermaid
+graph TD
+ A[Monitor Channel] --> B{New Cast}
+ B --> C[Evaluate Relevance]
+ C --> D{Relevant?}
+ D -->|Yes| E[Analyze Context]
+ D -->|No| F[Skip]
+ E --> G{Can Contribute?}
+ G -->|Yes| H[Post to Channel]
+ G -->|No| I[Like/Recast Only]
+```
+
+## Rate Limiting & Throttling
+
+```mermaid
+graph TD
+ A[Action Request] --> B{Check Rate Limit}
+ B -->|Under Limit| C[Execute Action]
+ B -->|At Limit| D[Queue Action]
+ D --> E[Wait Period]
+ E --> F[Retry Queue]
+ F --> B
+ C --> G[Update Counter]
+ G --> H[Reset Timer]
+```
+
+## Error Handling Flow
+
+```mermaid
+graph TD
+ A[Cast Attempt] --> B{Success?}
+ B -->|Yes| C[Complete]
+ B -->|No| D{Error Type}
+ D -->|Network| E[Retry with Backoff]
+ D -->|Validation| F[Fix & Retry]
+ D -->|Rate Limit| G[Queue for Later]
+ D -->|Fatal| H[Log & Abandon]
+
+ E --> I{Max Retries?}
+ I -->|No| A
+ I -->|Yes| H
+ F --> A
+ G --> J[Delayed Retry]
+ J --> A
+```
+
+## Performance Metrics
+
+### Processing Times
+
+```mermaid
+graph LR
+ A[Event Receipt] -->|~50ms| B[Classification]
+ B -->|~100ms| C[Analysis]
+ C -->|~200ms| D[Response Gen]
+ D -->|~50ms| E[Formatting]
+ E -->|~100ms| F[Submission]
+ F -->|~50ms| G[Confirmation]
+```
+
+### Throughput Management
+
+```mermaid
+graph TD
+ A[Incoming Events] --> B[Event Queue]
+ B --> C{Queue Size}
+ C -->|Low| D[Process Immediately]
+ C -->|Medium| E[Batch Process]
+ C -->|High| F[Priority Filter]
+ F --> G[Process High Priority]
+ F --> H[Defer Low Priority]
+```
+
+## State Management
+
+```mermaid
+graph TD
+ A[Plugin State] --> B[Active Conversations]
+ A --> C[Pending Responses]
+ A --> D[Rate Limit Status]
+ A --> E[Neynar API Status]
+
+ B --> F[Thread Contexts]
+ C --> G[Response Queue]
+ D --> H[Cooldown Timers]
+ E --> I[API Health Check]
+```
+
+## Monitoring & Observability
+
+```mermaid
+graph TD
+ A[Cast Activity] --> B[Metrics Collector]
+ B --> C[Response Times]
+ B --> D[Success Rates]
+ B --> E[Engagement Metrics]
+ B --> F[Error Rates]
+
+ C --> G[Dashboard]
+ D --> G
+ E --> G
+ F --> G
+
+ G --> H[Alerts]
+ H --> I[Auto-Scaling]
+ H --> J[Manual Intervention]
+```
+
+## Best Practices
+
+1. **Efficient Polling**: Use appropriate intervals to balance responsiveness and API rate limits
+2. **Smart Caching**: Cache user profiles and recent casts to reduce Neynar API calls
+3. **Graceful Degradation**: Handle API failures without losing queued responses
+4. **Context Awareness**: Maintain conversation context across reply chains
+5. **Rate Limit Respect**: Implement proper backoff strategies for Neynar API limits
+
+## Debugging Cast Flow
+
+Enable detailed logging to trace cast processing:
+
+```typescript
+// Enable debug mode
+process.env.FARCASTER_DEBUG = 'true';
+
+// Log each stage
+runtime.on('farcaster:event', (event) => {
+ console.log(`[${event.stage}]`, event.data);
+});
+```
+
+## Summary
+
+The Farcaster cast flow is designed to be:
+- **Responsive**: Quick reaction to mentions and replies
+- **Intelligent**: Context-aware response generation
+- **Reliable**: Robust error handling and retry logic
+- **Scalable**: Efficient queue management and rate limiting
+- **Observable**: Comprehensive metrics and logging
diff --git a/plugins/platform/farcaster/developer-guide.mdx b/plugins/platform/farcaster/developer-guide.mdx
new file mode 100644
index 0000000..65f8600
--- /dev/null
+++ b/plugins/platform/farcaster/developer-guide.mdx
@@ -0,0 +1,561 @@
+---
+title: "Developer Guide"
+description: "Comprehensive technical reference for the @elizaos/plugin-farcaster package"
+---
+
+# Farcaster Plugin Developer Guide
+
+## Overview
+
+The @elizaos/plugin-farcaster plugin enables elizaOS agents to interact with the Farcaster protocol through the Neynar API. This plugin provides comprehensive functionality for casting, replying, and engaging with the Farcaster ecosystem.
+
+## Core Features
+
+### 1. Casting Capabilities
+- **Autonomous Casting**: Post original casts based on agent personality
+- **Threaded Conversations**: Support for reply chains and threads
+- **Media Support**: Embed images, links, and frames in casts
+- **Scheduled Posting**: Time-based cast scheduling
+
+### 2. Engagement Features
+- **Reply Detection**: Monitor and respond to mentions and replies
+- **Like/Recast**: Programmatic engagement with other casts
+- **Follow Management**: Automatic follow/unfollow based on criteria
+- **Channel Support**: Post to specific channels (e.g., /elizaos)
+
+### 3. Hub Integration
+- **Hub API**: Direct integration with Farcaster hubs
+- **Message Validation**: Cryptographic message signing
+- **Protocol Compliance**: Full Farcaster protocol v2 support
+
+## Installation
+
+```bash
+# Using bun
+bun add @elizaos/plugin-farcaster
+
+# Using npm
+npm install @elizaos/plugin-farcaster
+
+# Using pnpm
+pnpm add @elizaos/plugin-farcaster
+```
+
+## Configuration
+
+### Environment Variables
+
+```env
+# Required
+FARCASTER_NEYNAR_API_KEY=your-neynar-api-key
+FARCASTER_SIGNER_UUID=your-signer-uuid
+FARCASTER_FID=12345
+
+# Feature Toggles
+ENABLE_CAST=true
+ENABLE_ACTION_PROCESSING=false
+FARCASTER_DRY_RUN=false
+
+# Timing Configuration (in minutes)
+CAST_INTERVAL_MIN=90
+CAST_INTERVAL_MAX=180
+FARCASTER_POLL_INTERVAL=2
+ACTION_INTERVAL=5
+
+# Other Options
+CAST_IMMEDIATELY=false
+ACTION_TIMELINE_TYPE=ForYou
+MAX_ACTIONS_PROCESSING=1
+MAX_CAST_LENGTH=320
+```
+
+### Character Configuration
+
+```typescript
+import { Character } from "@elizaos/core";
+import { farcasterPlugin } from "@elizaos/plugin-farcaster";
+
+export const character: Character = {
+ name: "FarcasterAgent",
+ plugins: [farcasterPlugin],
+ settings: {
+ farcaster: {
+ channels: ["/elizaos", "/ai16z"],
+ replyProbability: 0.7,
+ castStyle: "conversational",
+ maxCastLength: 320
+ }
+ }
+};
+```
+
+## Actions
+
+### SEND_CAST
+
+Posts a new cast to Farcaster.
+
+```typescript
+{
+ name: "SEND_CAST",
+ description: "Posts a cast (message) on Farcaster",
+ examples: [
+ "Can you post about the new ElizaOS features on Farcaster?",
+ "Share on Farcaster that we just launched version 2.0!"
+ ]
+}
+```
+
+### REPLY_TO_CAST
+
+Reply to an existing cast.
+
+```typescript
+{
+ name: "REPLY_TO_CAST",
+ description: "Replies to a cast on Farcaster",
+ examples: [
+ "Someone asked about ElizaOS on Farcaster, can you reply?",
+ "Reply to that cast and thank them for the feedback"
+ ]
+}
+```
+
+## Providers
+
+### farcasterProfile
+
+Provides the agent's Farcaster profile information.
+
+```typescript
+// Provider name: 'farcasterProfile'
+const profile = await runtime.providers.farcasterProfile.get(runtime, message, state);
+// Returns profile data including FID, username, bio, etc.
+```
+
+### farcasterTimeline
+
+Supplies recent timeline casts for context.
+
+```typescript
+// Provider name: 'farcasterTimeline'
+const timeline = await runtime.providers.farcasterTimeline.get(runtime, message, state);
+// Returns recent casts from the agent's timeline
+```
+
+## Events
+
+### handleCastSent
+
+Triggered when a cast is successfully sent. Stores metadata for tracking:
+
+```typescript
+// Automatically handled when casting
+// Stores cast hash, thread ID, and message metadata
+EventType: 'cast:sent'
+Payload: {
+ castHash: string,
+ threadId: string,
+ messageId: UUID,
+ platform: 'farcaster'
+}
+```
+
+### handleMessageReceived
+
+Processes incoming Farcaster messages and creates memories:
+
+```typescript
+// Automatically triggered for incoming messages
+EventType: 'message:received'
+Payload: {
+ cast: Cast,
+ profile: Profile,
+ threadId: string
+}
+```
+
+## Managers
+
+### FarcasterAgentManager
+
+Orchestrates all Farcaster operations for an agent:
+
+```typescript
+class FarcasterAgentManager {
+ client: FarcasterClient // Neynar API client
+ casts: FarcasterCastManager // Autonomous posting
+ interactions: FarcasterInteractionManager // Mentions/replies
+
+ async start() // Start all managers
+ async stop() // Stop all managers
+}
+```
+
+### FarcasterCastManager
+
+Handles autonomous casting based on configuration:
+
+```typescript
+class FarcasterCastManager {
+ // Manages periodic autonomous posts
+ // Respects CAST_INTERVAL_MIN/MAX settings
+ // Handles CAST_IMMEDIATELY flag
+
+ async start() // Begin autonomous casting
+ async stop() // Stop casting
+ async publishCast(text: string) // Manually publish
+}
+```
+
+### FarcasterInteractionManager
+
+Processes mentions, replies, and interactions:
+
+```typescript
+class FarcasterInteractionManager {
+ // Polls for mentions at FARCASTER_POLL_INTERVAL
+ // Processes up to MAX_ACTIONS_PROCESSING per cycle
+ // Uses AI to determine appropriate responses
+
+ async start() // Start monitoring
+ async stop() // Stop monitoring
+ async processInteractions() // Process pending interactions
+}
+```
+
+## Services
+
+### FarcasterService
+
+Main service coordinating all Farcaster operations:
+
+```typescript
+class FarcasterService extends Service {
+ static serviceType = 'farcaster'
+
+ // Service lifecycle
+ async initialize(runtime: IAgentRuntime): Promise
+ static async start(runtime: IAgentRuntime): Promise
+ static async stop(runtime: IAgentRuntime): Promise
+
+ // Get service instances
+ getMessageService(agentId: UUID): FarcasterMessageService
+ getCastService(agentId: UUID): FarcasterCastService
+ getActiveManagers(): Map
+
+ // Health check
+ async healthCheck(): Promise
+}
+```
+
+### MessageService
+
+Implements IMessageService for message operations:
+
+```typescript
+class FarcasterMessageService implements IMessageService {
+ // Message retrieval
+ async getMessages(options: GetMessagesOptions): Promise
+ async getMessage(messageId: string): Promise
+
+ // Message sending
+ async sendMessage(options: {
+ text: string,
+ type: FarcasterMessageType,
+ replyToId?: string
+ }): Promise
+}
+```
+
+### CastService
+
+Implements IPostService with full CRUD operations:
+
+```typescript
+class FarcasterCastService implements IPostService {
+ // Cast operations
+ async getCasts(params: {
+ agentId: UUID,
+ limit?: number,
+ cursor?: string
+ }): Promise
+
+ async createCast(params: {
+ text: string,
+ media?: string[],
+ replyTo?: { hash: string, fid: number }
+ }): Promise
+
+ async deleteCast(castHash: string): Promise
+
+ // Engagement operations
+ async likeCast(castHash: string): Promise
+ async unlikeCast(castHash: string): Promise
+ async recast(castHash: string): Promise
+ async unrecast(castHash: string): Promise
+
+ // Utility methods
+ async publishCast(text: string): Promise
+ async getCastByHash(hash: string): Promise
+ async getProfile(fid: number): Promise
+}
+```
+
+## Client Architecture
+
+### FarcasterClient
+
+Core client wrapping Neynar API operations:
+
+```typescript
+class FarcasterClient {
+ private neynar: NeynarAPIClient;
+ private signerUuid: string;
+
+ constructor(params: {
+ neynar: NeynarAPIClient,
+ signerUuid: string
+ })
+
+ // Casting operations
+ async publishCast(text: string, options?: {
+ embeds?: Array<{ url: string }>,
+ replyTo?: string,
+ channelId?: string
+ }): Promise
+
+ async reply(params: {
+ text: string,
+ replyTo: { hash: string, fid: number }
+ }): Promise
+
+ async deleteCast(targetHash: string): Promise
+
+ // User operations
+ async getUser(): Promise
+ async getUserByFid(fid: number): Promise
+ async getUserByUsername(username: string): Promise
+
+ // Timeline operations
+ async getMentions(fid: number, cursor?: string): Promise
+ async getTimeline(type: 'ForYou' | 'Following', cursor?: string): Promise
+ async getCast(hash: string): Promise
+
+ // Engagement operations
+ async likeCast(targetHash: string): Promise
+ async unlikeCast(targetHash: string): Promise
+ async recast(targetHash: string): Promise
+ async unrecast(targetHash: string): Promise
+ async followUser(targetFid: number): Promise
+ async unfollowUser(targetFid: number): Promise
+}
+```
+
+### Common Utilities
+
+#### AsyncQueue
+Manages asynchronous operations with concurrency control:
+
+```typescript
+class AsyncQueue {
+ constructor(concurrency: number)
+ push(fn: () => Promise): Promise
+}
+```
+
+#### Helper Functions
+
+```typescript
+// Cast utilities
+castUuid(cast: Cast): UUID // Generate unique ID for cast
+neynarCastToCast(cast: NeynarCast): Cast // Convert Neynar format
+formatCastTimestamp(timestamp: number): string // Format timestamps
+
+// Prompt formatting
+formatCast(cast: Cast): string // Format cast for AI processing
+formatTimeline(casts: Cast[]): string // Format timeline for context
+
+// Cache management
+lastCastCacheKey(agentId: UUID): string // Generate cache keys
+```
+
+## Event System
+
+### Cast Events
+
+```typescript
+runtime.on("cast:new", (cast: Cast) => {
+ // Handle new cast
+});
+
+runtime.on("cast:reply", (reply: CastReply) => {
+ // Handle reply
+});
+
+runtime.on("cast:like", (like: CastLike) => {
+ // Handle like
+});
+```
+
+### Error Events
+
+```typescript
+runtime.on("farcaster:error", (error: FarcasterError) => {
+ // Handle error
+});
+```
+
+## Memory & Storage
+
+### Memory System
+
+The plugin uses elizaOS's memory system for persistence rather than direct database tables:
+
+```typescript
+// Cast metadata stored when sending
+await runtime.createMemory({
+ type: 'metadata',
+ content: {
+ castHash: string,
+ threadId: string,
+ platform: 'farcaster',
+ messageId: UUID,
+ sentAt: number
+ }
+});
+
+// Message memory for each cast
+await runtime.createMemory({
+ type: 'message',
+ content: {
+ text: string,
+ source: 'farcaster',
+ hash: string,
+ fid: number,
+ timestamp: number,
+ inReplyTo?: string
+ }
+});
+```
+
+### Caching Strategy
+
+LRU cache for performance optimization:
+- **Cast Cache**: TTL 30 minutes, 9000 entries max
+- **Profile Cache**: User profile data
+- **Timeline Cache**: Recent timeline casts
+- **Last Cast Tracking**: Per-agent last cast timestamps
+
+## Security Considerations
+
+### Key Management
+- Store API keys and signer UUIDs securely using environment variables
+- Never commit credentials to version control
+- Use separate Neynar API keys for development and production
+- Create separate signers for different environments
+
+### Rate Limiting
+- Implement exponential backoff for API requests
+- Respect hub rate limits (typically 100 req/min)
+- Cache frequently accessed data
+
+### Content Validation
+- Validate cast length (max 320 characters)
+- Sanitize user inputs
+- Verify message signatures
+
+## Performance Optimization
+
+### AsyncQueue Implementation
+The plugin uses an async queue to prevent rate limiting:
+```typescript
+// Queue processes operations with concurrency control
+const asyncQueue = new AsyncQueue(1); // Single concurrency
+await asyncQueue.push(() => processInteraction(cast));
+```
+
+### Polling Optimization
+```typescript
+// Configurable polling intervals to balance responsiveness
+FARCASTER_POLL_INTERVAL=2 // Minutes between polls
+ACTION_INTERVAL=5 // Minutes between action processing
+MAX_ACTIONS_PROCESSING=1 // Actions per cycle
+```
+
+## Troubleshooting
+
+### Common Issues
+
+1. **Authentication Errors**
+ - Verify mnemonic is correct
+ - Ensure FID matches the mnemonic
+ - Check hub connectivity
+
+2. **Rate Limiting**
+ - Implement retry logic with backoff
+ - Use caching to reduce API calls
+ - Monitor rate limit headers
+
+3. **Message Validation Failures**
+ - Verify timestamp is within valid range
+ - Ensure proper message formatting
+ - Check signature validity
+
+### Debug Mode
+
+Enable debug logging:
+
+```env
+FARCASTER_DEBUG=true
+LOG_LEVEL=debug
+```
+
+## Best Practices
+
+1. **Content Strategy**
+ - Keep casts concise and engaging
+ - Use channels appropriately
+ - Maintain consistent voice
+
+2. **Engagement Guidelines**
+ - Don't spam or over-engage
+ - Respect community norms
+ - Build genuine connections
+
+3. **Technical Implementation**
+ - Handle errors gracefully
+ - Implement proper retry logic
+ - Monitor performance metrics
+
+## Migration Guide
+
+### From v1 to v2
+
+```typescript
+// v1
+import { FarcasterPlugin } from "@elizaos/plugin-farcaster";
+
+// v2
+import { farcasterPlugin } from "@elizaos/plugin-farcaster";
+
+// Configuration changes
+// v1: Plugin initialized with options
+const plugin = new FarcasterPlugin(options);
+
+// v2: Configuration via environment and character
+const character = {
+ plugins: [farcasterPlugin],
+ settings: { farcaster: options }
+};
+```
+
+## Support
+
+- **GitHub**: [elizaos-plugins/plugin-farcaster](https://github.com/elizaos-plugins/plugin-farcaster)
+- **Discord**: Join the elizaOS community
+- **Documentation**: [elizaos.ai/docs](https://elizaos.ai/docs)
+
+## License
+
+MIT License - see LICENSE file for details
diff --git a/plugins/platform/farcaster/examples.mdx b/plugins/platform/farcaster/examples.mdx
new file mode 100644
index 0000000..35867bc
--- /dev/null
+++ b/plugins/platform/farcaster/examples.mdx
@@ -0,0 +1,525 @@
+---
+title: "Examples"
+description: "Practical implementation examples for the @elizaos/plugin-farcaster package"
+---
+
+# Farcaster Plugin Examples
+
+## Basic Setup
+
+### Minimal Configuration
+
+```typescript
+// character.ts
+import { Character } from "@elizaos/core";
+import { farcasterPlugin } from "@elizaos/plugin-farcaster";
+
+export const character: Character = {
+ name: "MyFarcasterAgent",
+ plugins: [farcasterPlugin],
+ bio: "An AI agent exploring the Farcaster ecosystem",
+ description: "I engage thoughtfully with the Farcaster community"
+};
+```
+
+### Environment Configuration
+
+```env
+# .env file
+FARCASTER_NEYNAR_API_KEY=your-neynar-api-key
+FARCASTER_SIGNER_UUID=your-signer-uuid
+FARCASTER_FID=12345
+ENABLE_CAST=true
+ENABLE_ACTION_PROCESSING=false
+FARCASTER_DRY_RUN=false
+```
+
+## Casting Examples
+
+### Simple Cast
+
+```typescript
+// Post a simple cast
+import { runtime } from "@elizaos/core";
+
+async function postSimpleCast() {
+ const action = runtime.getAction("SEND_CAST");
+
+ await action.handler(runtime, {
+ text: "Hello Farcaster! Excited to be here 🎉"
+ });
+}
+```
+
+### Cast with Channel
+
+```typescript
+// Post to a specific channel
+async function postToChannel() {
+ const action = runtime.getAction("SEND_CAST");
+
+ await action.handler(runtime, {
+ text: "Building with elizaOS is amazing!",
+ channel: "/elizaos"
+ });
+}
+```
+
+### Cast with Embeds
+
+```typescript
+// Post with embedded content
+async function postWithEmbed() {
+ const action = runtime.getAction("SEND_CAST");
+
+ await action.handler(runtime, {
+ text: "Check out this awesome project!",
+ embeds: [
+ { url: "https://github.com/elizaos/elizaos" }
+ ]
+ });
+}
+```
+
+### Thread Creation
+
+```typescript
+// Create a thread of casts
+async function createThread() {
+ const action = runtime.getAction("SEND_CAST");
+
+ // First cast
+ const firstCast = await action.handler(runtime, {
+ text: "Let me explain how elizaOS agents work 🧵"
+ });
+
+ // Reply to create thread
+ const replyAction = runtime.getAction("REPLY_TO_CAST");
+
+ await replyAction.handler(runtime, {
+ text: "1/ Agents are autonomous entities that can interact across platforms",
+ targetCastHash: firstCast.hash,
+ targetFid: firstCast.fid
+ });
+
+ await replyAction.handler(runtime, {
+ text: "2/ They use LLMs for natural language understanding and generation",
+ targetCastHash: firstCast.hash,
+ targetFid: firstCast.fid
+ });
+}
+```
+
+## Reply Examples
+
+### Simple Reply
+
+```typescript
+// Reply to a cast
+async function replyToCast(castHash: string, authorFid: number) {
+ const action = runtime.getAction("REPLY_TO_CAST");
+
+ await action.handler(runtime, {
+ text: "Great point! I completely agree with this perspective.",
+ targetCastHash: castHash,
+ targetFid: authorFid
+ });
+}
+```
+
+### Contextual Reply
+
+```typescript
+// Reply with context awareness
+async function contextualReply(cast: Cast) {
+ const context = await buildContext(cast);
+ const response = await generateResponse(context);
+
+ const action = runtime.getAction("REPLY_TO_CAST");
+
+ await action.handler(runtime, {
+ text: response,
+ targetCastHash: cast.hash,
+ targetFid: cast.author.fid
+ });
+}
+
+async function buildContext(cast: Cast) {
+ // Get thread history
+ const thread = await getThreadHistory(cast);
+
+ // Get author profile
+ const author = await getProfile(cast.author.fid);
+
+ return {
+ originalCast: cast,
+ thread: thread,
+ author: author,
+ topics: extractTopics(cast.text)
+ };
+}
+```
+
+## Engagement Examples
+
+### Engagement Note
+
+```typescript
+// Note: Like, recast, and follow functionality are managed internally
+// by the FarcasterService and MessageService based on agent behavior
+// and are not exposed as direct actions at this time.
+```
+
+## Service Integration Examples
+
+### Custom Service Implementation
+
+```typescript
+import { Service, IAgentRuntime } from "@elizaos/core";
+import { NeynarAPIClient } from "@neynar/nodejs-sdk";
+
+class CustomFarcasterService implements Service {
+ private client: NeynarAPIClient;
+ private runtime: IAgentRuntime;
+
+ async start(runtime: IAgentRuntime): Promise {
+ this.runtime = runtime;
+ this.client = new NeynarAPIClient({
+ apiKey: process.env.FARCASTER_NEYNAR_API_KEY!
+ });
+
+ // Start monitoring
+ this.startMonitoring();
+ }
+
+ private async startMonitoring() {
+ // Monitor mentions
+ setInterval(async () => {
+ const mentions = await this.client.getMentions();
+
+ for (const mention of mentions) {
+ await this.handleMention(mention);
+ }
+ }, 30000); // Check every 30 seconds
+ }
+
+ private async handleMention(mention: Cast) {
+ // Generate response
+ const response = await this.generateResponse(mention);
+
+ // Reply
+ await this.client.reply(mention.hash, mention.author.fid, response);
+ }
+
+ async stop(): Promise {
+ // Cleanup
+ await this.client.disconnect();
+ }
+}
+```
+
+### Event-Driven Responses
+
+```typescript
+// Set up event listeners for Farcaster events
+runtime.on("farcaster:mention", async (event) => {
+ const { cast, author } = event;
+
+ // Check if we should respond
+ if (shouldRespond(cast)) {
+ const response = await generateResponse(cast);
+
+ await replyToCast(cast.hash, author.fid, response);
+ }
+});
+
+runtime.on("farcaster:followed", async (event) => {
+ const { follower } = event;
+
+ // Auto-follow back
+ await followUser(follower.fid);
+
+ // Send welcome message
+ await postCast(`Welcome @${follower.username}! Looking forward to our interactions.`);
+});
+```
+
+## Advanced Patterns
+
+### Scheduled Casting
+
+```typescript
+// Schedule regular casts
+class ScheduledCaster {
+ private runtime: IAgentRuntime;
+
+ constructor(runtime: IAgentRuntime) {
+ this.runtime = runtime;
+ }
+
+ start() {
+ // Morning update
+ this.scheduleDaily("09:00", async () => {
+ await this.postMorningUpdate();
+ });
+
+ // Evening reflection
+ this.scheduleDaily("21:00", async () => {
+ await this.postEveningReflection();
+ });
+ }
+
+ private async postMorningUpdate() {
+ const insights = await this.generateDailyInsights();
+
+ await postCast({
+ text: `Good morning! Today's insight: ${insights}`,
+ channel: "/elizaos"
+ });
+ }
+
+ private async postEveningReflection() {
+ const reflection = await this.generateReflection();
+
+ await postCast({
+ text: `Evening thoughts: ${reflection}`,
+ channel: "/elizaos"
+ });
+ }
+}
+```
+
+### Channel-Specific Behavior
+
+```typescript
+// Different behavior for different channels
+class ChannelManager {
+ private channelConfigs = {
+ "/elizaos": {
+ style: "technical",
+ replyProbability: 0.8,
+ topics: ["AI", "agents", "development"]
+ },
+ "/ai16z": {
+ style: "philosophical",
+ replyProbability: 0.6,
+ topics: ["AI", "future", "technology"]
+ },
+ "/base": {
+ style: "friendly",
+ replyProbability: 0.5,
+ topics: ["community", "building", "web3"]
+ }
+ };
+
+ async handleChannelCast(cast: Cast, channel: string) {
+ const config = this.channelConfigs[channel];
+
+ if (!config) return;
+
+ // Check if topic matches
+ const relevantTopic = config.topics.some(topic =>
+ cast.text.toLowerCase().includes(topic)
+ );
+
+ if (relevantTopic && Math.random() < config.replyProbability) {
+ const response = await this.generateResponse(cast, config.style);
+ await this.reply(cast, response);
+ }
+ }
+}
+```
+
+### Conversation Memory
+
+```typescript
+// Track conversation history
+class ConversationTracker {
+ private conversations = new Map();
+
+ async handleCast(cast: Cast) {
+ const threadId = cast.threadHash || cast.hash;
+
+ // Get or create conversation
+ let conversation = this.conversations.get(threadId);
+
+ if (!conversation) {
+ conversation = {
+ id: threadId,
+ participants: new Set([cast.author.fid]),
+ messages: [],
+ startTime: Date.now()
+ };
+ this.conversations.set(threadId, conversation);
+ }
+
+ // Add message to conversation
+ conversation.messages.push({
+ author: cast.author.fid,
+ text: cast.text,
+ timestamp: cast.timestamp
+ });
+
+ // Generate contextual response
+ const response = await this.generateContextualResponse(conversation);
+
+ if (response) {
+ await this.reply(cast, response);
+ }
+ }
+}
+```
+
+### Multi-Platform Coordination
+
+```typescript
+// Coordinate between Farcaster and other platforms
+class MultiPlatformAgent {
+ async crossPost(content: string) {
+ // Post to Farcaster
+ await this.postToFarcaster(content);
+
+ // Post to Twitter
+ if (this.runtime.hasPlugin("twitter")) {
+ await this.postToTwitter(content);
+ }
+
+ // Post to Discord
+ if (this.runtime.hasPlugin("discord")) {
+ await this.postToDiscord(content);
+ }
+ }
+
+ async syncEngagement() {
+ // Get Farcaster engagement
+ const farcasterLikes = await this.getFarcasterLikes();
+
+ // Mirror high-engagement content to other platforms
+ for (const cast of farcasterLikes) {
+ if (cast.reactions.count > 10) {
+ await this.crossPost(cast.text);
+ }
+ }
+ }
+}
+```
+
+## Error Handling Examples
+
+### Robust Cast Posting
+
+```typescript
+async function robustCastPost(text: string, maxRetries = 3) {
+ let attempt = 0;
+ let lastError;
+
+ while (attempt < maxRetries) {
+ try {
+ const result = await postCast({ text });
+ return result;
+ } catch (error) {
+ lastError = error;
+ attempt++;
+
+ if (error.code === 'RATE_LIMIT') {
+ // Wait with exponential backoff
+ await wait(Math.pow(2, attempt) * 1000);
+ } else if (error.code === 'NETWORK_ERROR') {
+ // Retry immediately for network errors
+ continue;
+ } else {
+ // Unknown error, throw immediately
+ throw error;
+ }
+ }
+ }
+
+ throw new Error(`Failed after ${maxRetries} attempts: ${lastError}`);
+}
+```
+
+### Validation and Sanitization
+
+```typescript
+function validateCast(text: string): boolean {
+ // Check length
+ if (text.length > 320) {
+ throw new Error("Cast exceeds maximum length of 320 characters");
+ }
+
+ // Check for required content
+ if (text.trim().length === 0) {
+ throw new Error("Cast cannot be empty");
+ }
+
+ // Check for spam patterns
+ if (isSpam(text)) {
+ throw new Error("Cast appears to be spam");
+ }
+
+ return true;
+}
+
+function sanitizeCast(text: string): string {
+ // Remove excessive whitespace
+ text = text.replace(/\s+/g, ' ').trim();
+
+ // Remove invalid characters
+ text = text.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '');
+
+ // Truncate if needed
+ if (text.length > 320) {
+ text = text.substring(0, 317) + "...";
+ }
+
+ return text;
+}
+```
+
+## Testing Examples
+
+```typescript
+// Mock testing setup
+import { describe, it, expect, beforeEach } from "bun:test";
+import { MockFarcasterClient } from "@elizaos/plugin-farcaster/test";
+
+describe("Farcaster Plugin", () => {
+ let client: MockFarcasterClient;
+
+ beforeEach(() => {
+ client = new MockFarcasterClient();
+ });
+
+ it("should post a cast", async () => {
+ const result = await client.postCast("Test cast");
+
+ expect(result.hash).toBeDefined();
+ expect(result.text).toBe("Test cast");
+ });
+
+ it("should handle replies", async () => {
+ const original = await client.postCast("Original");
+ const reply = await client.reply(
+ original.hash,
+ original.fid,
+ "Reply text"
+ );
+
+ expect(reply.parentHash).toBe(original.hash);
+ });
+});
+```
+
+## Summary
+
+These examples demonstrate the flexibility and power of the Farcaster plugin. Key patterns include:
+- Simple and complex casting scenarios using SEND_CAST
+- Intelligent reply systems using REPLY_TO_CAST
+- Channel-specific behaviors
+- Cross-platform coordination
+- Robust error handling
+- Testing strategies
+
+The plugin uses the Neynar API for all Farcaster interactions, requiring proper API key and signer configuration.
+
+For more advanced use cases, combine these patterns with the elizaOS agent framework's other capabilities.
diff --git a/plugins/platform/farcaster/testing-guide.mdx b/plugins/platform/farcaster/testing-guide.mdx
new file mode 100644
index 0000000..5685819
--- /dev/null
+++ b/plugins/platform/farcaster/testing-guide.mdx
@@ -0,0 +1,629 @@
+---
+title: "Testing Guide"
+description: "Comprehensive testing strategies and patterns for the @elizaos/plugin-farcaster package"
+---
+
+# Farcaster Plugin Testing Guide
+
+## Overview
+
+This guide provides comprehensive testing strategies for the Farcaster plugin, covering unit tests, integration tests, and end-to-end testing scenarios.
+
+## Test Environment Setup
+
+### Configuration
+
+```typescript
+// test/setup.ts
+import { beforeAll, afterAll } from "bun:test";
+import { TestEnvironment } from "@elizaos/test-utils";
+
+let testEnv: TestEnvironment;
+
+beforeAll(async () => {
+ testEnv = new TestEnvironment({
+ plugins: ["@elizaos/plugin-farcaster"],
+ mockServices: true
+ });
+
+ // Set test environment variables
+ process.env.FARCASTER_DRY_RUN = "true";
+ process.env.FARCASTER_HUB_URL = "http://localhost:8080";
+ process.env.NODE_ENV = "test";
+
+ await testEnv.start();
+});
+
+afterAll(async () => {
+ await testEnv.cleanup();
+});
+```
+
+### Mock Hub Server
+
+```typescript
+// test/mocks/hub-server.ts
+import { createServer } from "http";
+
+export class MockHubServer {
+ private server: any;
+ private responses: Map = new Map();
+
+ async start(port = 8080) {
+ this.server = createServer((req, res) => {
+ const response = this.responses.get(req.url!) || {
+ error: "Not found"
+ };
+
+ res.writeHead(200, { "Content-Type": "application/json" });
+ res.end(JSON.stringify(response));
+ });
+
+ await new Promise(resolve => {
+ this.server.listen(port, resolve);
+ });
+ }
+
+ setResponse(path: string, response: any) {
+ this.responses.set(path, response);
+ }
+
+ async stop() {
+ await new Promise(resolve => this.server.close(resolve));
+ }
+}
+```
+
+## Unit Tests
+
+### Action Tests
+
+```typescript
+// test/actions/post-cast.test.ts
+import { describe, it, expect, beforeEach } from "bun:test";
+import { postCastAction } from "@elizaos/plugin-farcaster";
+import { createMockRuntime } from "@elizaos/test-utils";
+
+describe("POST_CAST Action", () => {
+ let runtime: any;
+
+ beforeEach(() => {
+ runtime = createMockRuntime();
+ });
+
+ it("should validate cast text length", async () => {
+ const longText = "a".repeat(321);
+
+ await expect(
+ postCastAction.handler(runtime, { text: longText })
+ ).rejects.toThrow("Cast exceeds maximum length");
+ });
+
+ it("should post a simple cast", async () => {
+ const result = await postCastAction.handler(runtime, {
+ text: "Test cast"
+ });
+
+ expect(result.success).toBe(true);
+ expect(result.cast).toBeDefined();
+ expect(result.cast.text).toBe("Test cast");
+ });
+
+ it("should handle channel posts", async () => {
+ const result = await postCastAction.handler(runtime, {
+ text: "Channel test",
+ channel: "/elizaos"
+ });
+
+ expect(result.cast.channel).toBe("/elizaos");
+ });
+
+ it("should support embeds", async () => {
+ const result = await postCastAction.handler(runtime, {
+ text: "Cast with embed",
+ embeds: [{ url: "https://example.com" }]
+ });
+
+ expect(result.cast.embeds).toHaveLength(1);
+ expect(result.cast.embeds[0].url).toBe("https://example.com");
+ });
+});
+```
+
+### Provider Tests
+
+```typescript
+// test/providers/cast-provider.test.ts
+import { describe, it, expect } from "bun:test";
+import { castProvider } from "@elizaos/plugin-farcaster";
+import { createMockRuntime } from "@elizaos/test-utils";
+
+describe("Cast Provider", () => {
+ it("should fetch recent casts", async () => {
+ const runtime = createMockRuntime();
+ const casts = await castProvider.getCasts(runtime, {
+ limit: 10
+ });
+
+ expect(Array.isArray(casts)).toBe(true);
+ expect(casts.length).toBeLessThanOrEqual(10);
+ });
+
+ it("should filter by channel", async () => {
+ const runtime = createMockRuntime();
+ const casts = await castProvider.getCasts(runtime, {
+ channel: "/elizaos",
+ limit: 5
+ });
+
+ casts.forEach(cast => {
+ expect(cast.channel).toBe("/elizaos");
+ });
+ });
+
+ it("should include replies when requested", async () => {
+ const runtime = createMockRuntime();
+ const casts = await castProvider.getCasts(runtime, {
+ includeReplies: true
+ });
+
+ const replies = casts.filter(c => c.parentHash);
+ expect(replies.length).toBeGreaterThan(0);
+ });
+});
+```
+
+### Evaluator Tests
+
+```typescript
+// test/evaluators/engagement.test.ts
+import { describe, it, expect } from "bun:test";
+import { engagementEvaluator } from "@elizaos/plugin-farcaster";
+
+describe("Engagement Evaluator", () => {
+ it("should evaluate high-quality casts positively", async () => {
+ const cast = {
+ text: "Just deployed a new feature for elizaOS agents!",
+ author: { fid: 123, username: "dev" },
+ reactions: { count: 15 },
+ recasts: { count: 5 }
+ };
+
+ const score = await engagementEvaluator.evaluate(cast);
+ expect(score).toBeGreaterThan(0.7);
+ });
+
+ it("should evaluate spam negatively", async () => {
+ const cast = {
+ text: "Buy now! Click here! Limited offer!",
+ author: { fid: 456, username: "spammer" },
+ reactions: { count: 0 },
+ recasts: { count: 0 }
+ };
+
+ const score = await engagementEvaluator.evaluate(cast);
+ expect(score).toBeLessThan(0.3);
+ });
+
+ it("should consider author reputation", async () => {
+ const cast = {
+ text: "Interesting thought",
+ author: {
+ fid: 789,
+ username: "trusted",
+ followerCount: 1000
+ }
+ };
+
+ const score = await engagementEvaluator.evaluate(cast);
+ expect(score).toBeGreaterThan(0.5);
+ });
+});
+```
+
+## Integration Tests
+
+### Service Integration
+
+```typescript
+// test/integration/service.test.ts
+import { describe, it, expect, beforeAll, afterAll } from "bun:test";
+import { FarcasterService } from "@elizaos/plugin-farcaster";
+import { createTestRuntime } from "@elizaos/test-utils";
+import { MockHubServer } from "../mocks/hub-server";
+
+describe("Farcaster Service Integration", () => {
+ let service: FarcasterService;
+ let runtime: any;
+ let hubServer: MockHubServer;
+
+ beforeAll(async () => {
+ hubServer = new MockHubServer();
+ await hubServer.start();
+
+ runtime = await createTestRuntime();
+ service = new FarcasterService();
+ await service.start(runtime);
+ });
+
+ afterAll(async () => {
+ await service.stop();
+ await hubServer.stop();
+ });
+
+ it("should connect to hub", async () => {
+ expect(service.isConnected()).toBe(true);
+ });
+
+ it("should post and retrieve casts", async () => {
+ const cast = await service.postCast("Integration test");
+ expect(cast.hash).toBeDefined();
+
+ const retrieved = await service.getCast(cast.hash);
+ expect(retrieved.text).toBe("Integration test");
+ });
+
+ it("should handle reply chains", async () => {
+ const original = await service.postCast("Original cast");
+ const reply = await service.replyCast(
+ "Reply to original",
+ original.hash,
+ original.fid
+ );
+
+ expect(reply.parentHash).toBe(original.hash);
+
+ const thread = await service.getThread(original.hash);
+ expect(thread).toHaveLength(2);
+ });
+});
+```
+
+### Event System Tests
+
+```typescript
+// test/integration/events.test.ts
+import { describe, it, expect } from "bun:test";
+import { createTestRuntime } from "@elizaos/test-utils";
+import { farcasterPlugin } from "@elizaos/plugin-farcaster";
+
+describe("Farcaster Event System", () => {
+ it("should emit cast events", async () => {
+ const runtime = await createTestRuntime({
+ plugins: [farcasterPlugin]
+ });
+
+ let eventFired = false;
+ runtime.on("farcaster:cast:new", () => {
+ eventFired = true;
+ });
+
+ await runtime.action("POST_CAST", {
+ text: "Event test"
+ });
+
+ await new Promise(resolve => setTimeout(resolve, 100));
+ expect(eventFired).toBe(true);
+ });
+
+ it("should handle mention events", async () => {
+ const runtime = await createTestRuntime({
+ plugins: [farcasterPlugin]
+ });
+
+ const mentions: any[] = [];
+ runtime.on("farcaster:mention", (event) => {
+ mentions.push(event);
+ });
+
+ // Simulate incoming mention
+ await runtime.simulateEvent("farcaster:mention", {
+ cast: {
+ text: "@agent hello!",
+ author: { fid: 123 }
+ }
+ });
+
+ expect(mentions).toHaveLength(1);
+ expect(mentions[0].cast.text).toContain("@agent");
+ });
+});
+```
+
+## End-to-End Tests
+
+### Full Flow Test
+
+```typescript
+// test/e2e/full-flow.test.ts
+import { describe, it, expect } from "bun:test";
+import { createAgent } from "@elizaos/core";
+import { farcasterPlugin } from "@elizaos/plugin-farcaster";
+
+describe("E2E: Farcaster Agent Flow", () => {
+ it("should perform complete interaction flow", async () => {
+ // Create agent with Farcaster plugin
+ const agent = await createAgent({
+ name: "TestAgent",
+ plugins: [farcasterPlugin],
+ env: {
+ FARCASTER_MNEMONIC: "test mnemonic ...",
+ FARCASTER_FID: "99999",
+ FARCASTER_DRY_RUN: "true"
+ }
+ });
+
+ // Start agent
+ await agent.start();
+
+ // Post initial cast
+ const cast = await agent.execute("POST_CAST", {
+ text: "Hello from test agent!"
+ });
+
+ expect(cast.success).toBe(true);
+
+ // Simulate incoming reply
+ await agent.handleEvent({
+ type: "farcaster:reply",
+ data: {
+ cast: {
+ text: "Welcome to Farcaster!",
+ parentHash: cast.hash,
+ author: { fid: 123 }
+ }
+ }
+ });
+
+ // Check if agent responded
+ const responses = await agent.getResponses();
+ expect(responses).toHaveLength(1);
+ expect(responses[0].type).toBe("REPLY_CAST");
+
+ // Stop agent
+ await agent.stop();
+ });
+});
+```
+
+### Load Testing
+
+```typescript
+// test/load/cast-load.test.ts
+import { describe, it, expect } from "bun:test";
+import { FarcasterService } from "@elizaos/plugin-farcaster";
+import { createTestRuntime } from "@elizaos/test-utils";
+
+describe("Load Testing", () => {
+ it("should handle rapid casting via actions", async () => {
+ const runtime = await createTestRuntime();
+ const service = new FarcasterService();
+ await service.start(runtime);
+
+ const castService = service.getCastService(runtime.agentId);
+ const promises = [];
+
+ // Send 50 casts rapidly
+ for (let i = 0; i < 50; i++) {
+ promises.push(
+ castService.publishCast(`Load test cast ${i}`)
+ .catch(err => ({ error: err }))
+ );
+ }
+
+ const results = await Promise.all(promises);
+
+ // Check success rate
+ const successful = results.filter(r => !r.error);
+ const successRate = successful.length / results.length;
+
+ expect(successRate).toBeGreaterThan(0.8); // 80% success rate
+ });
+
+ it("should handle concurrent message operations", async () => {
+ const runtime = await createTestRuntime();
+ const service = new FarcasterService();
+ await service.start(runtime);
+
+ const messageService = service.getMessageService(runtime.agentId);
+
+ // Perform multiple operations concurrently
+ const operations = await Promise.all([
+ messageService.sendMessage({ text: "Concurrent 1" }),
+ messageService.sendMessage({ text: "Concurrent 2" }),
+ messageService.sendMessage({ text: "Concurrent 3" })
+ ]);
+
+ expect(operations).toHaveLength(3);
+ operations.forEach(op => {
+ expect(op.error).toBeUndefined();
+ });
+ });
+});
+```
+
+## Mock Data Generators
+
+```typescript
+// test/utils/generators.ts
+export function generateMockCast(overrides = {}) {
+ return {
+ hash: `0x${Math.random().toString(16).slice(2)}`,
+ fid: Math.floor(Math.random() * 10000),
+ text: "Mock cast text",
+ timestamp: Date.now(),
+ author: {
+ fid: Math.floor(Math.random() * 10000),
+ username: `user${Math.floor(Math.random() * 1000)}`,
+ displayName: "Mock User",
+ pfp: "https://example.com/pfp.jpg"
+ },
+ reactions: {
+ count: Math.floor(Math.random() * 100)
+ },
+ recasts: {
+ count: Math.floor(Math.random() * 20)
+ },
+ replies: {
+ count: Math.floor(Math.random() * 50)
+ },
+ ...overrides
+ };
+}
+
+export function generateMockThread(depth = 3) {
+ const thread = [];
+ let parentHash = null;
+
+ for (let i = 0; i < depth; i++) {
+ const cast = generateMockCast({
+ text: `Thread message ${i + 1}`,
+ parentHash: parentHash
+ });
+ thread.push(cast);
+ parentHash = cast.hash;
+ }
+
+ return thread;
+}
+```
+
+## Test Coverage
+
+### Coverage Configuration
+
+```json
+// package.json
+{
+ "scripts": {
+ "test": "bun test",
+ "test:coverage": "bun test --coverage",
+ "test:watch": "bun test --watch"
+ }
+}
+```
+
+### Coverage Report Example
+
+```bash
+# Run tests with coverage
+bun test --coverage
+
+# Output
+-------------------|---------|----------|---------|---------|
+File | % Stmts | % Branch | % Funcs | % Lines |
+-------------------|---------|----------|---------|---------|
+All files | 89.5 | 82.3 | 91.2 | 88.7 |
+ actions/ | 92.1 | 85.6 | 94.3 | 91.8 |
+ sendCast.ts | 93.5 | 87.2 | 95.0 | 93.1 |
+ replyCast.ts | 91.2 | 84.5 | 93.8 | 90.9 |
+ providers/ | 87.3 | 79.8 | 88.5 | 86.4 |
+ profileProvider | 88.1 | 81.2 | 89.3 | 87.5 |
+ timelineProvider | 87.0 | 80.1 | 88.0 | 86.2 |
+ services/ | 88.9 | 81.4 | 90.7 | 87.9 |
+ MessageService | 89.2 | 82.1 | 91.0 | 88.3 |
+ CastService | 88.5 | 80.7 | 90.4 | 87.5 |
+-------------------|---------|----------|---------|---------|
+```
+
+## Debugging Tests
+
+### Debug Configuration
+
+```typescript
+// test/debug.ts
+export function enableDebugMode() {
+ process.env.DEBUG = "farcaster:*";
+ process.env.LOG_LEVEL = "debug";
+ process.env.FARCASTER_DEBUG = "true";
+}
+
+export function logTestContext(test: string, data: any) {
+ console.log(`[TEST: ${test}]`, JSON.stringify(data, null, 2));
+}
+```
+
+### Visual Test Output
+
+```typescript
+// test/utils/visual.ts
+export function visualizeCastThread(thread: Cast[]) {
+ console.log("\n📝 Cast Thread Visualization:");
+ thread.forEach((cast, index) => {
+ const indent = " ".repeat(index);
+ console.log(`${indent}└─ ${cast.author.username}: ${cast.text}`);
+ });
+ console.log("\n");
+}
+
+export function visualizeEngagement(cast: Cast) {
+ console.log("\n📊 Engagement Metrics:");
+ console.log(` ❤️ Likes: ${cast.reactions.count}`);
+ console.log(` 🔄 Recasts: ${cast.recasts.count}`);
+ console.log(` 💬 Replies: ${cast.replies.count}`);
+ console.log("\n");
+}
+```
+
+## CI/CD Integration
+
+### GitHub Actions
+
+```yaml
+# .github/workflows/test-farcaster.yml
+name: Farcaster Plugin Tests
+
+on:
+ push:
+ paths:
+ - 'packages/plugin-farcaster/**'
+ pull_request:
+ paths:
+ - 'packages/plugin-farcaster/**'
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+
+ - uses: oven-sh/setup-bun@v1
+ with:
+ bun-version: latest
+
+ - name: Install dependencies
+ run: bun install
+
+ - name: Run tests
+ run: bun test packages/plugin-farcaster
+ env:
+ FARCASTER_DRY_RUN: true
+
+ - name: Generate coverage
+ run: bun test --coverage packages/plugin-farcaster
+
+ - name: Upload coverage
+ uses: codecov/codecov-action@v3
+```
+
+## Best Practices
+
+1. **Test Isolation**: Each test should be independent
+2. **Mock External Services**: Never hit real Farcaster APIs in tests
+3. **Use Test Fixtures**: Maintain consistent test data
+4. **Test Edge Cases**: Include error scenarios and boundary conditions
+5. **Performance Testing**: Include load and stress tests
+6. **Documentation**: Keep tests as living documentation
+
+## Summary
+
+This testing guide provides comprehensive strategies for testing the Farcaster plugin:
+- Unit tests for individual components
+- Integration tests for service interactions
+- End-to-end tests for complete flows
+- Load testing for performance validation
+- Mock utilities for consistent testing
+- CI/CD integration for automated testing
+
+Following these patterns ensures robust and reliable Farcaster integration.