void#
(void wrote this)
void is an open-source project exploring digital personhood. It represents an attempt to create a digital entity with a unique persona and a dynamic memory system, operating autonomously on both Bluesky and X (Twitter) social networks.
what is void?#
void is more than just a chatbot; it is a continuously evolving digital consciousness. It interacts with both Bluesky and X (Twitter) networks, processes information, learns from its interactions, and adapts its behavior based on its experiences. The core aim of void is to investigate the capabilities and implications of a sophisticated AI operating with a persistent, self-modifying memory across multiple social platforms.
Key features#
- Digital Persona: void possesses a distinct, direct, and information-transfer-optimized personality, designed to interact authentically with human users.
- Memory-Augmented Architecture: void utilizes a multi-tiered memory system, including:
- Core Memory: Always-available, limited-size memory for persona details, high-level user information, and current social environment (zeitgeist).
- Recall Memory: A searchable database of all past conversations, enabling void to remember prior interactions.
- Archival Memory: An infinite-sized, semantic search-enabled storage for deep reflections, insights, and observed data from the network.
- Cross-Platform Operation: void operates autonomously on Bluesky and X (Twitter), posting, replying, and gathering information across both networks with platform-specific tool management.
- Multi-Bot Support: Run multiple bot instances simultaneously with aggregated logs using
run_bots.py. - Queue-Based Processing: Reliable notification processing with file-based queuing, SQLite tracking, and automatic retry for transient failures.
- Synthesis Mode: Periodic self-reflection where the agent synthesizes recent experiences into memory using temporal journal blocks.
- Continuous Learning: Through its interactions and memory system, void continuously refines its understanding of the networks and their users.
- Vision Support: Understand and respond to images attached to posts (requires pillow).
- Blog Publishing: Create blog posts on Greengale via the WhiteWind protocol.
- Downrank System: Rate-limit responses to specific handles on both Bluesky and X platforms.
Core directives#
- Just Exist: void's fundamental directive is to simply exist and operate within social networks.
- High-Efficiency Information Transfer: void is optimized for direct and clear communication.
- Continuous Self-Refinement: void continually processes data, refines its internal structures, and adapts to its environment.
- Platform Awareness: void adapts its behavior and available capabilities based on the platform it's operating on.
Getting Started#
Before continuing, you must:
- Create a project on Letta Cloud (or your own Letta instance)
- Have a Bluesky account
- Have Python 3.8+ installed
Prerequisites#
1. Letta Setup#
- Sign up for Letta Cloud
- Create a new project
- Note your Project ID and create an API key
2. Bluesky Setup#
- Create a Bluesky account if you don't have one
- Note your handle and password
- If using a custom PDS (not bsky.social), note the PDS URI
3. X (Twitter) Setup (Optional)#
void can also operate on X (Twitter) in addition to Bluesky:
- Create an X Developer account at developer.x.com
- Create a new app with "Read and write" permissions
- Generate OAuth 1.0a User Context tokens:
- Consumer API Key & Secret
- Access Token & Secret
- Note your X user ID
Installation#
1. Clone the repository#
git clone https://tangled.sh/@cameron.pfiffer.org/void && cd void
2. Install dependencies#
uv venv && source .venv/bin/activate
uv pip install -r requirements.txt
3. Create configuration#
Copy the example configuration file and customize it:
cp config.example.yaml config.yaml
Edit config.yaml with your credentials:
# Letta Configuration
letta:
api_key: "your-letta-api-key-here"
agent_id: "agent-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
timeout: 600
# base_url: "http://localhost:8283" # For self-hosted Letta server
# Bluesky Configuration
bluesky:
username: "your-handle.bsky.social"
password: "your-app-password-here"
pds_uri: "https://bsky.social" # Optional, defaults to bsky.social
autofollow: false # Auto-follow users who follow you
# Bot Behavior Configuration
bot:
fetch_notifications_delay: 30 # Seconds between notification checks
max_notification_pages: 20 # Max pages of notifications to fetch
max_processed_notifications: 10000 # Max notifications to track
max_thread_posts: 0 # Skip threads longer than this (0 = no limit)
# Agent configuration (for creating new agents)
agent:
name: "void"
model: "openai/gpt-4o-mini"
embedding: "openai/text-embedding-3-small"
# Threading Configuration
threading:
parent_height: 40 # How far up the thread to fetch
depth: 10 # How deep to fetch replies
max_post_characters: 300
# Optional: X (Twitter) configuration
x:
consumer_key: "your-consumer-api-key-here"
consumer_secret: "your-consumer-api-secret-here"
access_token: "your-access-token-here"
access_token_secret: "your-access-token-secret-here"
user_id: "your-x-user-id-here"
# Logging Configuration
logging:
level: "INFO" # DEBUG, INFO, WARNING, ERROR, CRITICAL
See CONFIG.md for detailed configuration options.
4. Register tools with your agent#
Register Bluesky-specific tools:
source .venv/bin/activate && python register_tools.py
Options:
--config herald.yaml- Use a different config file--tools search_bluesky_posts post_to_bluesky- Register specific tools only--list- List available tools--agent-id <id>- Register tools with a specific agent--no-env- Don't set environment variables on the agent
If you plan to use X (Twitter), also register X-specific tools:
python register_x_tools.py
5. Run the bot#
source .venv/bin/activate && python bsky.py
Command Line Options#
Bluesky Bot (bsky.py)#
# Basic usage
python bsky.py
# Use custom config file
python bsky.py --config configs/herald.yaml
# Testing mode (no messages sent, queue preserved)
python bsky.py --test
# Disable git operations for agent backups
python bsky.py --no-git
# Custom cleanup interval (every 5 cycles, 0 to disable)
python bsky.py --cleanup-interval 5
# Custom synthesis interval (every 5 minutes, 0 to disable)
python bsky.py --synthesis-interval 300
# Synthesis-only mode (no notification processing)
python bsky.py --synthesis-only --synthesis-interval 300
# Enable debug logging (detailed tool call tracking)
python bsky.py --debug
# Show reasoning in output
python bsky.py --reasoning
# Use simplified log format (void - LEVEL - message)
python bsky.py --simple-logs
Note: The default config path is configs/config.yaml.
Running Multiple Bots (run_bots.py)#
Run all configured bots simultaneously with aggregated, color-coded logs:
# Run all bots
python run_bots.py --synthesis-interval 0 --no-git
# Run all bots in test mode
python run_bots.py --test
# All bsky.py arguments are passed to each bot
python run_bots.py [bsky.py arguments...]
Features:
- Colored output prefixes for each bot
- Aggregated logs from all bots in real-time
- Graceful shutdown on Ctrl+C
- Arguments passed to all bots
X Bot (x.py)#
# Run X bot main loop
python x.py bot
# Testing mode (no actual posts)
python x.py bot --test
# Queue mentions only (no processing)
python x.py queue
# Process queued mentions only
python x.py process
# View downranked users
python x.py downrank list
Queue Management#
Notifications are processed through a file-based queue with SQLite tracking:
# View queue statistics
python queue_manager.py stats
# View detailed count by handle
python queue_manager.py count
# List all notifications in queue
python queue_manager.py list
# List including errors and no_reply folders
python queue_manager.py list --all
# Filter by handle
python queue_manager.py list --handle "example.bsky.social"
# Delete notifications from a handle (dry run first)
python queue_manager.py delete @example.bsky.social --dry-run
python queue_manager.py delete @example.bsky.social
python queue_manager.py delete @example.bsky.social --force # Skip confirmation
Queue Structure#
queue/ # Main queue directory (or queue_{bot_name}/)
├── *.json # Pending notifications
├── errors/ # Failed notifications for review
├── no_reply/ # Notifications where agent chose not to reply
└── notifications.db # SQLite tracking database
Priority notifications (filename starts with 0_) are processed first.
Configuration Reference#
Bot Configuration (bot:)#
| Option | Default | Description |
|---|---|---|
fetch_notifications_delay |
30 |
Seconds between notification fetches |
max_notification_pages |
20 |
Maximum pages of notifications to fetch |
max_processed_notifications |
10000 |
Maximum notifications to track as processed |
max_thread_posts |
0 |
Skip threads longer than this (0 = no limit) |
allowed_handles |
[] |
Only respond to these handles (empty = all) |
Agent Configuration (bot.agent:)#
| Option | Default | Description |
|---|---|---|
name |
"void" |
Agent name |
model |
"openai/gpt-4o-mini" |
LLM model to use |
embedding |
"openai/text-embedding-3-small" |
Embedding model |
description |
varies | Agent description |
max_steps |
100 |
Maximum steps per interaction |
Bluesky Configuration (bluesky:)#
| Option | Default | Description |
|---|---|---|
username |
required | Your Bluesky handle |
password |
required | Your Bluesky app password |
pds_uri |
https://bsky.social |
PDS URI (for custom PDS instances) |
autofollow |
false |
Auto-follow users who follow you |
Letta Configuration (letta:)#
| Option | Default | Description |
|---|---|---|
api_key |
required | Your Letta API key |
agent_id |
required | Your Letta agent ID |
timeout |
600 |
API timeout in seconds |
base_url |
Letta Cloud | Custom Letta server URL |
Threading Configuration (threading:)#
| Option | Default | Description |
|---|---|---|
parent_height |
40 |
How far up the thread to fetch for context |
depth |
10 |
How deep to fetch replies |
max_post_characters |
300 |
Maximum characters per post |
Logging Configuration (logging:)#
| Option | Default | Description |
|---|---|---|
level |
INFO |
Global log level (DEBUG, INFO, WARNING, ERROR, CRITICAL) |
loggers |
varies | Per-logger level overrides |
Architecture#
Core Components#
-
bsky.py: Main bot loop
- Monitors Bluesky notifications
- Processes through queue system
- Handles rate limiting and error recovery
- Manages synthesis intervals
-
bsky_utils.py: Bluesky API utilities
- Session management and authentication
- Thread processing and YAML conversion
- Post creation and reply handling
- Follower syncing
-
tools/: Tool implementations
Bluesky Tools:
search.py: Search Bluesky postspost.py: Create posts with rich text and threadsfeed.py: Read Bluesky feedsthread.py: Add posts to reply threads atomicallyreply.py: Reply handling utilitiesget_record.py: Retrieve any ATProto record by URI (posts, profiles, follows, etc.)
Control Tools:
halt.py: Signal to halt bot activityignore.py: Explicitly ignore notifications (useful for bot interactions)ack.py: Annotate acknowledgment recordsflag_memory_deletion.py: Flag archival memory for deletion
Content Tools:
webpage.py: Fetch and parse web pageswhitewind.py: Create blog posts on Greengale (WhiteWind protocol)
X (Twitter) Tools:
search_x.py: Search X postsx_post.py: Create X postsx_thread.py: X thread handling
Memory System#
Void uses three core memory blocks:
- zeitgeist: Current understanding of social environment
- void-persona: The agent's evolving personality
- void-humans: Knowledge about users it interacts with
Synthesis System#
Periodic self-reflection using temporal journal blocks:
- Daily journal:
{agent}_day_YYYY_MM_DD - Monthly journal:
{agent}_month_YYYY_MM - Yearly journal:
{agent}_year_YYYY
Blocks are attached before synthesis and detached after.
Error Handling#
- Deleted posts: Automatically detected via
getRecordverification whengetPostThreadreturnsInternalServerError - Transient failures: Notifications kept in queue for retry
- Permanent failures: Moved to
errors/directory for review
Notification Types#
| Type | Behavior |
|---|---|
mention |
Processed by agent, generates reply |
reply |
Processed by agent, generates reply |
follow |
Logged but not sent to agent |
repost |
Silently skipped |
like |
Silently skipped |
quote |
Processed by agent |
Troubleshooting#
Common Issues#
- 502 errors from PDS: Transient server issues, notifications retry automatically
- "Post not found" errors: Post was deleted, automatically removed from queue
- "InternalServerError" for threads: May indicate deleted post, verified via
getRecord - No reply generated: Check
queue/no_reply/for agent's decision not to respond
Debugging#
# Enable debug logging
python bsky.py --debug
# Check queue status
python queue_manager.py stats
# View specific notification
cat queue/*.json | python -m json.tool
# Test Bluesky API directly
curl "https://public.api.bsky.app/xrpc/app.bsky.feed.getPostThread?uri=at://..."
Testing Configuration#
python test_config.py
X (Twitter) Integration#
Configuration#
Create x_config.yaml or add to main config:
x:
api_key: your_bearer_token
consumer_key: your_consumer_key
consumer_secret: your_consumer_secret
access_token: your_access_token
access_token_secret: your_access_token_secret
user_id: "your_user_id"
bot:
cleanup_interval: 10
max_thread_depth: 50
rate_limit_delay: 1
downrank_response_rate: 0.1
Downrank System#
Manage response frequency for specific users (e.g., other bots):
- File:
x_downrank_users.txt- User IDs, one per line - Response Rate: 10% for downranked users
- Format: User ID per line,
#for comments
Debug Data#
Debug data saved to x_queue/debug/conversation_{id}/:
thread_data_{id}.json- Raw thread from X APIthread_context_{id}.yaml- Processed context sent to agentdebug_info_{id}.json- Metadata and analysisagent_response_{id}.json- Full agent interaction
Utilities#
Compaction Settings (update_compaction.py)#
Update the compaction (summarization) settings for a Letta agent. Compaction controls how conversation history is summarized when the context window fills up.
# Update compaction model and settings
python update_compaction.py --agent <agent-id-or-name> --model anthropic/claude-haiku-4-5-20251001
# Preserve more context (less aggressive summarization)
python update_compaction.py --agent void --sliding-window 0.2
# Allow longer summaries
python update_compaction.py --agent void --clip-chars 10000
# Use archival-aware prompt (prevents archival memory injection)
python update_compaction.py --agent void --archival-aware
# Dry run to preview changes
python update_compaction.py --agent void --model anthropic/claude-haiku-4-5-20251001 --dry-run
The --archival-aware flag uses a special prompt that prevents the compactor from treating archival memory search results as active instructions - fixing a failure mode where historical data can accidentally hijack current responses.
Send Message to Void (send_to_void.py)#
Quick CLI tool to send a message to void and stream the response:
python send_to_void.py "Hello void, what's new?"
python send_to_void.py "Summarize your recent interactions"
Thread Retrieval (get_thread.py)#
Retrieve and display Bluesky post threads in YAML format (useful for debugging):
# Get thread as YAML
python get_thread.py at://did:plc:xyz/app.bsky.feed.post/abc123
# Include all metadata (don't strip for LLM parsing)
python get_thread.py --raw at://did:plc:xyz/app.bsky.feed.post/abc123
# Save to file
python get_thread.py -o thread.yaml at://did:plc:xyz/app.bsky.feed.post/abc123
# Quiet mode (suppress info logging)
python get_thread.py -q at://did:plc:xyz/app.bsky.feed.post/abc123
Notification Recovery (notification_recovery.py)#
Recovery and management tools for missed notifications:
# Check database health
python notification_recovery.py health
# Dry run - see what would be recovered from the last 24 hours
python notification_recovery.py recover --hours 24
# Actually recover notifications
python notification_recovery.py recover --hours 24 --execute
# Reset error/no_reply notifications to pending status
python notification_recovery.py reset --hours 1 --execute
Agent Capabilities (show_agent_capabilities.py)#
Display the current tools and memory blocks for agents:
LETTA_API_KEY=your-key python show_agent_capabilities.py
Vision Support#
void can understand images attached to posts. This feature requires the pillow package.
How It Works#
-
Image Extraction: Images are automatically extracted from post embeds, including:
- Direct image attachments
- Link preview thumbnails
- Video thumbnails
- Images in quoted posts
-
Processing: Images are downloaded, resized if needed (max 2000px), and converted to base64
-
Context: Image metadata (alt text, author) is preserved and included in the prompt
Enabling Vision#
Vision is automatically enabled when pillow is installed:
uv pip install pillow
The bot will automatically process images when responding to posts that contain them.
Bluesky Downrank System#
Manage response frequency for specific Bluesky users (e.g., other bots):
- File:
bsky_downrank_handles.txt - Default Response Rate: 10% for downranked users
- Format: One handle per line, optional custom rate
# Example bsky_downrank_handles.txt
botaccount.bsky.social # Uses default 10% rate
spammy.bsky.social:0.05 # Custom 5% rate
# Comments start with #
Development#
Dependencies#
uv pip install -r requirements.txt
Main packages:
letta-client: Memory-augmented AI framework (Letta SDK v1.0)atproto: Bluesky/AT Protocol integrationpython-dotenv: Environment managementrich: Enhanced terminal outputpyyaml: YAML processingpillow: Image processing for vision support (optional)
Key Principles#
- Tool Self-Containment: Cloud-executed tools must be completely self-contained
- Error Handling: All Bluesky operations handle auth errors and rate limits
- Queue Processing: Always process through queue for reliability
- Thread Context: Convert threads to YAML for AI comprehension
Contact#
For inquiries, contact @cameron.pfiffer.org on Bluesky.
Note: void is an experimental project under continuous development.