packages/examples/bluesky/README.md
A full-featured AI agent running on Bluesky, implemented in TypeScript, Python, and Rust. This agent uses the complete elizaOS runtime pipeline - no shortcuts, no bypasses.
messageService.handleMessage() with:
shouldRespond evaluation (LLM-powered decision making)messageHandlerTemplateruntime.runActionsByMode("ALWAYS_AFTER", ...)This agent uses the canonical elizaOS message processing:
Bluesky Notification → Create Memory → messageService.handleMessage()
↓
┌─────────────────────┐
│ State Composition │ (providers)
└─────────────────────┘
↓
┌─────────────────────┐
│ shouldRespond │ (LLM evaluation)
└─────────────────────┘
↓
┌─────────────────────┐
│ Action Planning │ (if enabled)
└─────────────────────┘
↓
┌─────────────────────┐
│ Response Generation │ (messageHandlerTemplate)
└─────────────────────┘
↓
┌─────────────────────┐
│ Callback │ (posts to Bluesky)
└─────────────────────┘
↓
┌─────────────────────┐
│ ALWAYS_AFTER hooks │ (run post-response)
└─────────────────────┘
cp env.example .env
# Edit .env with your credentials
Required settings:
BLUESKY_HANDLE: Your Bluesky handle (e.g., yourname.bsky.social)BLUESKY_PASSWORD: App password from https://bsky.app/settings/app-passwordsOPENAI_API_KEY: OpenAI API key (or use another model provider)# From the repo root, build the required plugins
bun install
bun run build
cd examples/bluesky
bun install
bun run start
cd examples/bluesky/python
pip install -r requirements.txt
python agent.py
cd examples/bluesky/rust/bluesky-agent
cargo run --release
examples/bluesky/
├── env.example # Environment template
├── README.md # This file
├── typescript/ # TypeScript implementation
│ ├── agent.ts # Main entry point (initializes runtime)
│ ├── handlers.ts # Event handlers (uses messageService.handleMessage)
│ ├── character.ts # Agent personality
│ ├── package.json
│ └── __tests__/ # Tests
├── python/ # Python implementation
│ ├── agent.py # Main entry point
│ ├── handlers.py # Event handlers
│ ├── character.py # Agent personality
│ ├── requirements.txt
│ └── tests/ # Tests
└── rust/ # Rust implementation
└── bluesky-agent/
├── src/
│ ├── main.rs # Main entry point
│ ├── handlers.rs
│ └── character.rs
├── Cargo.toml
└── tests/ # Tests
// 1. Create memory using the standard helper
const message = createMessageMemory({
id: stringToUuid(uuidv4()),
entityId,
roomId,
content: {
text: mentionText,
source: "bluesky",
mentionContext: {
isMention: true,
mentionType: "platform_mention",
},
},
});
// 2. Define callback to handle the generated response
const callback: HandlerCallback = async (content: Content) => {
// Post response to Bluesky
const post = await postService.createPost(content.text, {
uri: notification.uri,
cid: notification.cid,
});
// Return memories for persistence
return [responseMemory];
};
// 3. Process through the FULL elizaOS pipeline
await runtime.messageService.handleMessage(runtime, message, callback);
The messageService.handleMessage() call automatically:
CHARACTER - Agent's personality and bioRECENT_MESSAGES - Conversation contextACTIONS - Available actions the agent can takeANXIETY - Conversation urgency metricsENTITIES - Known entities in the conversationshouldRespond using LLM when needed:
actionPlanning is enabled)messageHandlerTemplate:
| Variable | Description | Default |
|---|---|---|
BLUESKY_HANDLE | Your Bluesky handle | Required |
BLUESKY_PASSWORD | App password | Required |
BLUESKY_SERVICE | Bluesky PDS URL | https://bsky.social |
BLUESKY_DRY_RUN | Simulate without posting | false |
BLUESKY_POLL_INTERVAL | Seconds between polls | 60 |
BLUESKY_ENABLE_POSTING | Enable automated posts | true |
BLUESKY_ENABLE_DMS | Process direct messages | true |
BLUESKY_POST_INTERVAL_MIN | Min seconds between posts | 1800 |
BLUESKY_POST_INTERVAL_MAX | Max seconds between posts | 3600 |
const runtime = new AgentRuntime({
character,
plugins: [sqlPlugin, openaiPlugin, blueSkyPlugin],
// These are the defaults:
// disableBasicCapabilities: false, // REPLY, IGNORE, NONE actions
// enableExtendedCapabilities: false, // Facts, roles, etc.
// actionPlanning: undefined, // Uses ACTION_PLANNING setting
// checkShouldRespond: undefined, // Uses CHECK_SHOULD_RESPOND setting
});
cd typescript
bun test # Unit tests (mocked)
LIVE_TEST=true bun test # Live integration tests
cd python
pytest # Unit tests (mocked)
LIVE_TEST=true pytest # Live integration tests
cd rust/bluesky-agent
cargo test # Unit tests (mocked)
cargo test --features live # Live integration tests
Edit the character configuration:
export const character: Character = {
name: "BlueSkyBot",
bio: "A helpful AI assistant on Bluesky",
system: "You are a friendly assistant...",
// Topics the agent knows about
topics: ["AI", "technology", "helpful tips"],
// Personality traits
adjectives: ["friendly", "helpful", "concise"],
// Few-shot examples for the LLM
messageExamples: [
[
{ name: "User", content: { text: "@Bot hello!" } },
{ name: "BlueSkyBot", content: { text: "Hey there! 👋 How can I help?" } }
],
],
// Examples for automated posts
postExamples: [
"🤖 Tip of the day: Take breaks and stay hydrated!",
],
};
Register custom actions through plugins:
const myPlugin: Plugin = {
name: "my-plugin",
actions: [
{
name: "SEARCH_WEB",
description: "Search the web for information",
validate: async (runtime, message) => true,
handler: async (runtime, message, state, callback) => {
// Implementation
},
},
],
};
const runtime = new AgentRuntime({
character,
plugins: [sqlPlugin, openaiPlugin, blueSkyPlugin, myPlugin],
});
await runtime.initialize()name.bsky.social)BLUESKY_POLL_INTERVAL if hitting limitsPOSTGRES_URL with valid credentialsshouldRespond evaluation resultsMIT - See the main elizaOS repository for details.