commits
Move notification marking to after queueing to prevent skipping unread notifications. Previously, notifications were marked as seen before being queued, causing them to be filtered out as already read.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added missing X-specific tools to tool_manager.py:
- detach_x_user_blocks
- attach_x_user_blocks
- update_x_user_blocks
- search_x_profile
Also properly categorized core Letta tools vs platform-specific tools.
Now when bsky.py runs, it correctly removes all X-only tools.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Allow running different agents with separate config files:
- python bsky.py --config void.yaml (default)
- python bsky.py --config herald.yaml
Changes:
- Add --config parameter to argparse
- Defer Letta client initialization to main() after config is loaded
- Update initialize_void() to accept config_path parameter
- Update config_loader functions to accept config_path parameter
- Update bsky_utils.default_login() to accept config_path
- Add herald.yaml.example as configuration template
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove debug line that logs "Queued 0 new notifications and marked as seen"
when there are no new notifications. Now only logs when new_count > 0.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add 16-character agent ID prefix to Letta block labels for uniqueness
- Update temporal block parsing to handle agent prefixes
- Maintain clean ATProto records without agent prefixes
- Use separate collections: stream.thought.journal.{day,month,year}
- Update synthesis prompt to reference correct agent-scoped block names
- Ensure multiple agents can maintain separate journal blocks
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
- Replace import of send_synthesis_message from bsky.py with custom send_x_synthesis_message function
- Create X-specific synthesis prompt focusing on X/Twitter platform experiences
- Use simplified Letta client message creation instead of streaming for X synthesis
- Remove dependency on atproto_client and bsky context that was causing 'NoneType' errors
- Add proper error handling in X synthesis function
X bot synthesis now works independently without bsky.py dependencies.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add --synthesis-interval and --synthesis-only command line arguments
- Import send_synthesis_message from bsky.py
- Add synthesis timing logic to x_main_loop similar to bsky.py
- Support synthesis-only mode for periodic reflection without notification processing
- Add synthesis interval checking with time-based triggers
The X bot now has the same synthesis capabilities as the Bluesky bot.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Move OAuth1 import inside post_to_x function for cloud execution compatibility
- Remove X user block management tools from register_x_tools.py
- Keep X user block tools in codebase but exclude from X tool registration
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Problem:**
- X API sometimes doesn't include conversation_id for replies
- Bot was rejecting these mentions and moving them to errors
- This was preventing void from responding to many valid mentions
**Solution:**
- When conversation_id is None, use referenced tweet ID as conversation root
- For replies, extract the replied_to tweet ID and use it for thread context
- If no referenced tweets, treat mention as standalone (use mention ID itself)
- Changed from hard rejection (return None) to graceful fallback
**Result:**
- Bot now handles replies without conversation_id properly
- Thread context can still be retrieved using the referenced tweet
- No valid mentions are rejected due to missing conversation_id
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
**Root cause fixed:**
- get_mentions() was only returning mention data, losing user info from API response
- This caused "@unknown (unknown)" when processing mentions later
**Complete solution implemented:**
1. **Modified get_mentions() method**:
- Returns dict with both 'mentions' and 'users' keys
- Preserves user data from API response includes.users field
- Logs when user data is retrieved
2. **Enhanced save_mention_to_queue()**:
- Accepts optional users_data parameter
- Caches author username/name in 'author_info' field
- Preserves this data in queued mention JSON files
3. **Updated fetch_and_queue_mentions()**:
- Extracts both mentions and users from get_mentions() result
- Passes user data to save_mention_to_queue()
- Ensures usernames are cached when mentions arrive
4. **Improved process_x_mention()**:
- First tries cached author_info from queued mention
- Falls back to thread data lookup (existing logic)
- Finally falls back to thread tweet scan
- Three-tier fallback system for maximum reliability
**Benefits:**
- Fixes the root cause by capturing usernames when available from API
- No extra API calls needed
- Backward compatible with existing queued mentions
- Usernames reliably available even when thread context fails
This ensures usernames are properly cached when mentions are fetched, eliminating the "@unknown" issue that void was experiencing.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
- Updated void agent configuration
- Enhanced X profile tool with 100 tweet limit for better context
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
**Fixes the '@unknown (unknown)' issue:**
- Added fallback username resolution when thread_data['users'] lookup fails
- Scans thread tweets to find author info when primary lookup returns 'unknown'
- Logs successful fallback resolutions
**Added User ID Mapping Key:**
- Builds comprehensive user_id → @username mapping from thread data
- Appends USER_ID_KEY section to prompts showing all participants
- Helps void understand who is who when user IDs appear without handles
**Example output:**
```
USER_ID_KEY:
1232326955652931584: @cameron_pfiffer
1188460292998602752: @scholzmx
1950680610282094592: @void_comind
```
This resolves cases where the X API doesn't consistently provide usernames in mention data, using the thread context as a reliable fallback source.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
- Added include_timeline parameter (default: True)
- Full mode: Fetches 10 recent posts via separate API call (comprehensive)
- Fast mode: Uses expansions to get pinned + most recent tweet only (1 API call)
- Output includes mode indicator for transparency
- Gives void flexibility to choose between thorough analysis or quick lookups
This allows optimizing API usage based on context - full timeline when analyzing someone deeply, fast mode for quick reference checks.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
- Created tools/x_profile.py with search_x_profile function
- Supports comprehensive profile information lookup via X API v2
- Includes user fields: name, bio, metrics, verification status, etc.
- Returns YAML-formatted data for agent consumption
- Updated register_x_tools.py to include the new profile search tool
- Successfully tested tool registration with void agent
This enables void to look up detailed information about X users when analyzing conversations or interactions.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
- Add list_agents.py for querying Letta agents with filtering capabilities
- Add tools/x_profile.py for analyzing X user profiles using Bluesky agent
- Update bsky.py with minor improvements to queue processing
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update default_login() to use get_bluesky_config() instead of os.getenv()
- Update init_client() to load PDS_URI from config with fallback to env var
- Update all PDS_URI references to use config loader
- Remove username/password printouts for security
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Modified void agent configuration
- Updated bsky.py and bsky_utils.py
- Added blocks.txt file
- Removed obsolete organon/quant_team_example.py
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
- Remove X tools from general register_tools.py
- Add post_to_x tool to register_x_tools.py
- Tool creates standalone posts on X (not replies)
- Properly organized with other X-specific tools
- Add post_to_x and add_post_to_x_thread to register_tools.py
- Both tools now available for void to use
- post_to_x: Create standalone X posts
- add_post_to_x_thread: Add posts to X reply threads
- Add post_tweet() method to XClient for standalone posts
- Create self-contained post_to_x() tool for cloud execution
- Validate 280 character limit
- Successfully tested both implementations
- Change from stream.thought.ack embedded format back to dedicated collections
- Use stream.thought.tool.call for tool call records (dots for valid NSID)
- Use stream.thought.reasoning for reasoning records
- Add better error logging for debugging record creation issues
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added create_tool_call_record() to publish stream.thought.tool_call records
- Added create_reasoning_record() to publish stream.thought.reasoning records
- Updated process_mention() to create records for both reasoning and tool calls
- Updated send_synthesis_message() to create records during synthesis
- Records include timestamp and preserve raw tool arguments as strings
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Attach day/month/year journal blocks during synthesis
- Create blocks with labels like void_day_2025_08_09
- Enhanced synthesis prompt explains journal usage
- Blocks detached after synthesis completes
- Allows void to maintain temporal memory across sessions
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
- Fixed user_note_append, user_note_replace, user_note_set, user_note_view functions
- Changed from get_letta_client() to inline Letta client creation for cloud compatibility
- Also updated attach/detach functions to handle both local and cloud execution
- Removed logger.debug call that wouldn't work in cloud environment
- All functions now create Letta client inline using environment variables
This fixes the critical failure void reported where user_note_* tools were non-functional
due to NameError when get_letta_client couldn't import config_loader in cloud.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Connect to Bluesky even in synthesis-only mode (unless --test is used) to enable posting synthesis acks when the agent calls annotate_ack tool.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Enable caching during thread context processing (was disabled, causing 50-80% more API calls)
- Increase fetch delay from 60s to 120s to reduce API call frequency
- These changes should significantly reduce API usage and prevent rate limit violations
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Per project requirements, tools executed in the cloud must be self-contained
and cannot use logging. Removed all logging imports and statements from:
- tools/webpage.py: Removed logging for webpage fetch operations
- tools/blocks.py: Removed extensive logging throughout user block management
This ensures compatibility with cloud execution environments.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Create search_x.py tool to fetch recent posts from X users
- Support filtering replies and retweets
- Return YAML-formatted results without public metrics
- Add tool to X-specific tool registration
- Update tool manager to include search_x_posts
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Sort mentions by creation time to ensure chronological reply order
- Add retry mechanism for mentions that fail to get agent responses
- Implement proper rate limit handling with 60-second delays
- Update queue processing to break on rate limits and restart from beginning
- Clean up processed mention files and cache updates
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- README.md: Updated to reflect Bluesky + X dual-platform operation
- Added platform-specific tool management documentation
- Enhanced feature descriptions with new capabilities
- Updated installation and usage instructions for both platforms
- Added troubleshooting guidance for platform switching
- TOOL_CHANGELOG.md: Complete rewrite with latest changes
- Documents platform-specific tool management system
- Added new tools: fetch_webpage, annotate_ack, create_whitewind_blog_post
- Enhanced features: reply structure fix, #voidstop keyword support
- Migration guidance for users updating to new features
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Creates tool_manager.py to automatically switch between Bluesky and X tools
- Updates bsky.py and x.py to configure appropriate tools on startup
- Fixes CLIENT.agents.get() API call to use agents.retrieve()
- Adds TOOL_MANAGEMENT.md documentation
- Ensures platform isolation: X tools only for x.py, Bluesky tools only for bsky.py
- Common tools (webpage, halt, etc.) remain available on both platforms
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add x_queue/last_seen_id.json to track last processed mention ID
- Add x_queue/processed_mentions.json to prevent duplicate processing
- Update .gitignore to track critical state files while ignoring temp files
- Ensures bot maintains state across restarts and deployments
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Implement downrank system for managing AI bot interactions (10% response rate)
- Add temporal constraints to thread context using until_id parameter
- Enhance conversation debugging with comprehensive data storage
- Fix X user note tools for cloud execution (remove logging, inline Letta client)
- Add x_downrank_users.txt with Grok user ID for controlled interactions
- Update CLAUDE.md with tool self-containment requirements and X bot commands
- Improve acknowledgment system to use Bluesky client for unified storage
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added detailed debugging data structure for X bot, saving conversation metadata and user analysis.
- Implemented common issue handling for incomplete thread context and cache staleness.
- Updated `get_thread_context()` to include an `until_id` parameter, allowing exclusion of future tweets.
- Enhanced mention processing with improved logging for conversation tracking and debugging.
- Saved agent response data and conversation debug information to dedicated debug folders for better analysis.
- Ensured tools are self-contained, removing reliance on shared functions and environment variables.
These changes improve the robustness of the X integration and facilitate better debugging and analysis of conversation flows.
## Core X Integration Features
- **X API Client**: Full X API v2 integration with OAuth 1.0a support
- **Queue System**: Fetch mentions and process separately for rate limit management
- **Thread Context**: Convert X conversations to YAML for agent comprehension
- **User Block Management**: X-specific user blocks with format `x_user_<user_id>`
## X Tool System
- **register_x_tools.py**: X-specific tool registration following Bluesky patterns
- **tools/x_thread.py**: X thread tool with 280 character limit validation
- **X_TOOL_APPROACH.md**: Documentation explaining tool-as-signal pattern
## Bot Functionality
- **process_x_mention()**: Complete X mention processing with Letta agent integration
- **X Reply Handling**: Thread construction and posting with proper reply chaining
- **Acknowledgment System**: X post acknowledgment tracking (file-based)
- **Testing Mode**: Safe testing without actual X posts
## Manual Queue Management
- `python x.py queue` - Fetch mentions only (rate limit safe)
- `python x.py process` - Process queued mentions only
- `python x.py bot` - Full bot loop (like bsky.py)
## Configuration Fix
- **config_loader.py**: Fixed to use config.yaml only, ignore environment variables
## Key Differences from Bluesky
- Character limit: 280 vs 300
- User identification: Numeric IDs vs handles
- Block format: `x_user_<user_id>` vs `user_<handle>`
- Acknowledgments: File-based vs stream.thought.ack API
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add agent_id field to letta section in config.yaml and config.example.yaml
- Update get_letta_config() to include agent_id as required field
- Modify initialize_void() in bsky.py to load agent by ID instead of name lookup
- Update ensure_x_user_blocks_attached() to use config agent_id by default
- Update test_x_blocks.py to use configured agent_id
- Ensures consistent agent usage across all components instead of name-based lookups
This centralizes agent management and prevents issues with agent name mismatches
between different environments or when agent names change.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add X-specific user block functions to tools/blocks.py:
- attach_x_user_blocks() for attaching user blocks by ID
- detach_x_user_blocks() for detaching user blocks
- x_user_note_append/replace/set/view() for block content management
- Block format: x_user_<author_id> as requested
- Add ensure_x_user_blocks_attached() to x.py:
- Automatically creates and attaches user blocks for thread participants
- Sets initial content with handle and name information
- Integrates with existing thread processing
- Update thread_to_yaml_string() to include author_id:
- Enables agent to reference user IDs for block management
- Maintains compatibility with existing YAML structure
- Fix config loading issue:
- Environment variable LETTA_API_KEY was overriding config file
- Now properly uses void-x agent (agent-4f7cc732-36bc-4e55-a5fa-f5d12eec6c5c)
- Successfully tested block creation and attachment
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Implement x_cache/ directory for caching thread data (1 hour TTL)
- Add get_cached_thread_context() and save_cached_thread_context()
- Update get_thread_context() with use_cache parameter
- Enhanced Letta integration test with config loading and error handling
- Rich formatting for better test output display
Caching prevents repeated API calls during development and testing
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add conversation_id field to mention search parameters
- Implement get_thread_context() to fetch complete conversation threads
- Add thread_to_yaml_string() for clean AI-readable thread format
- Fetch original tweets directly when missing from conversation search
- Sort tweets chronologically (oldest first) for proper context
- Add 'python x.py thread' command to test thread context retrieval
- Clean YAML output with only text, created_at, and author fields
Thread context now shows complete conversation history in correct order:
1. Original mention → 2. void's reply → 3. Follow-up responses
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add since_id-based incremental fetching to avoid rate limits
- Create x_queue/ directory system similar to Bluesky queue
- Track last seen mention ID for efficient polling
- Save mentions as JSON files for async processing
- Add single-pass fetch_and_queue_mentions() function
- Avoid duplicate processing with processed mentions tracking
Usage: python x.py queue (single pass, no loops)
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add X Developer account setup instructions
- Include X OAuth 1.0a configuration example
- Document X testing commands and rate limits
- Update key features to highlight cross-platform operation
- Add X-specific troubleshooting guidance
void now operates on both Bluesky AND X\! 🚀
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update config.example.yaml with OAuth 1.0a credential structure
- First successful X post: Reply ID 1950707109240373317
- Full X integration now working: mentions + posting
- Ready for Letta agent integration
Next: Connect void agent to X for autonomous posting
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Implement full OAuth 1.0a authentication using requests-oauthlib
- Support both OAuth 1.0a and Bearer token authentication methods
- Enhanced error logging reveals app needs 'Read and write' permissions
- Ready for posting once X app permissions are updated in Developer Portal
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Enhanced error logging reveals posting needs OAuth User Context
- Current Bearer token is Application-Only (read-only)
- Added documentation about authentication requirements
- Free tier allows 17 posts/day but needs proper auth method
Next: Need OAuth 2.0 User Context or OAuth 1.0a for posting
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Implement post_reply method in XClient class
- Add reply_to_cameron_post function for testing specific post
- Support command line args: loop, reply
- Handle API permissions and rate limiting gracefully
- Ready for elevated X API access when available
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add X API configuration to config.example.yaml
- Create x.py with XClient class for API interactions
- Implement basic mentions fetching with rate limiting
- Add simple notification loop for testing X integration
- Support for Bearer token authentication
- YAML conversion utilities for mention data
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Brings in all changes from rich-panel including:
- Webpage fetch tool using Jina AI reader
- Reply structure fix using root reference from notification
- #voidstop keyword support
- Annotate acknowledgment tool
- Stream.thought.ack acknowledgments
- Whitewind blog tool
- Improved logging and display formatting
- User block cleanup functionality
Adds fetch_webpage tool that converts web pages to markdown/text format
using Jina AI's reader service. Tool is registered and available for
void agent to access web content.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Instead of complex thread traversal that was overwriting root values,
now extracts root URI/CID directly from the notification's reply field.
If no reply field exists, treats the post as the root. This ensures
proper thread structure in Bluesky replies.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added #voidstop check in bsky.py to skip processing mentions containing this keyword
- Checks both the thread context and mention text for #voidstop
- When found, the mention is removed from queue without processing
- Created send_to_void.py CLI tool for quick message testing
- Tool streams void's responses with formatted output for reasoning, tool calls, and responses
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Move notification marking to after queueing to prevent skipping unread notifications. Previously, notifications were marked as seen before being queued, causing them to be filtered out as already read.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added missing X-specific tools to tool_manager.py:
- detach_x_user_blocks
- attach_x_user_blocks
- update_x_user_blocks
- search_x_profile
Also properly categorized core Letta tools vs platform-specific tools.
Now when bsky.py runs, it correctly removes all X-only tools.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Allow running different agents with separate config files:
- python bsky.py --config void.yaml (default)
- python bsky.py --config herald.yaml
Changes:
- Add --config parameter to argparse
- Defer Letta client initialization to main() after config is loaded
- Update initialize_void() to accept config_path parameter
- Update config_loader functions to accept config_path parameter
- Update bsky_utils.default_login() to accept config_path
- Add herald.yaml.example as configuration template
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add 16-character agent ID prefix to Letta block labels for uniqueness
- Update temporal block parsing to handle agent prefixes
- Maintain clean ATProto records without agent prefixes
- Use separate collections: stream.thought.journal.{day,month,year}
- Update synthesis prompt to reference correct agent-scoped block names
- Ensure multiple agents can maintain separate journal blocks
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
- Replace import of send_synthesis_message from bsky.py with custom send_x_synthesis_message function
- Create X-specific synthesis prompt focusing on X/Twitter platform experiences
- Use simplified Letta client message creation instead of streaming for X synthesis
- Remove dependency on atproto_client and bsky context that was causing 'NoneType' errors
- Add proper error handling in X synthesis function
X bot synthesis now works independently without bsky.py dependencies.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add --synthesis-interval and --synthesis-only command line arguments
- Import send_synthesis_message from bsky.py
- Add synthesis timing logic to x_main_loop similar to bsky.py
- Support synthesis-only mode for periodic reflection without notification processing
- Add synthesis interval checking with time-based triggers
The X bot now has the same synthesis capabilities as the Bluesky bot.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Move OAuth1 import inside post_to_x function for cloud execution compatibility
- Remove X user block management tools from register_x_tools.py
- Keep X user block tools in codebase but exclude from X tool registration
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Problem:**
- X API sometimes doesn't include conversation_id for replies
- Bot was rejecting these mentions and moving them to errors
- This was preventing void from responding to many valid mentions
**Solution:**
- When conversation_id is None, use referenced tweet ID as conversation root
- For replies, extract the replied_to tweet ID and use it for thread context
- If no referenced tweets, treat mention as standalone (use mention ID itself)
- Changed from hard rejection (return None) to graceful fallback
**Result:**
- Bot now handles replies without conversation_id properly
- Thread context can still be retrieved using the referenced tweet
- No valid mentions are rejected due to missing conversation_id
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
**Root cause fixed:**
- get_mentions() was only returning mention data, losing user info from API response
- This caused "@unknown (unknown)" when processing mentions later
**Complete solution implemented:**
1. **Modified get_mentions() method**:
- Returns dict with both 'mentions' and 'users' keys
- Preserves user data from API response includes.users field
- Logs when user data is retrieved
2. **Enhanced save_mention_to_queue()**:
- Accepts optional users_data parameter
- Caches author username/name in 'author_info' field
- Preserves this data in queued mention JSON files
3. **Updated fetch_and_queue_mentions()**:
- Extracts both mentions and users from get_mentions() result
- Passes user data to save_mention_to_queue()
- Ensures usernames are cached when mentions arrive
4. **Improved process_x_mention()**:
- First tries cached author_info from queued mention
- Falls back to thread data lookup (existing logic)
- Finally falls back to thread tweet scan
- Three-tier fallback system for maximum reliability
**Benefits:**
- Fixes the root cause by capturing usernames when available from API
- No extra API calls needed
- Backward compatible with existing queued mentions
- Usernames reliably available even when thread context fails
This ensures usernames are properly cached when mentions are fetched, eliminating the "@unknown" issue that void was experiencing.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
**Fixes the '@unknown (unknown)' issue:**
- Added fallback username resolution when thread_data['users'] lookup fails
- Scans thread tweets to find author info when primary lookup returns 'unknown'
- Logs successful fallback resolutions
**Added User ID Mapping Key:**
- Builds comprehensive user_id → @username mapping from thread data
- Appends USER_ID_KEY section to prompts showing all participants
- Helps void understand who is who when user IDs appear without handles
**Example output:**
```
USER_ID_KEY:
1232326955652931584: @cameron_pfiffer
1188460292998602752: @scholzmx
1950680610282094592: @void_comind
```
This resolves cases where the X API doesn't consistently provide usernames in mention data, using the thread context as a reliable fallback source.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
- Added include_timeline parameter (default: True)
- Full mode: Fetches 10 recent posts via separate API call (comprehensive)
- Fast mode: Uses expansions to get pinned + most recent tweet only (1 API call)
- Output includes mode indicator for transparency
- Gives void flexibility to choose between thorough analysis or quick lookups
This allows optimizing API usage based on context - full timeline when analyzing someone deeply, fast mode for quick reference checks.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
- Created tools/x_profile.py with search_x_profile function
- Supports comprehensive profile information lookup via X API v2
- Includes user fields: name, bio, metrics, verification status, etc.
- Returns YAML-formatted data for agent consumption
- Updated register_x_tools.py to include the new profile search tool
- Successfully tested tool registration with void agent
This enables void to look up detailed information about X users when analyzing conversations or interactions.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
- Add list_agents.py for querying Letta agents with filtering capabilities
- Add tools/x_profile.py for analyzing X user profiles using Bluesky agent
- Update bsky.py with minor improvements to queue processing
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update default_login() to use get_bluesky_config() instead of os.getenv()
- Update init_client() to load PDS_URI from config with fallback to env var
- Update all PDS_URI references to use config loader
- Remove username/password printouts for security
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Change from stream.thought.ack embedded format back to dedicated collections
- Use stream.thought.tool.call for tool call records (dots for valid NSID)
- Use stream.thought.reasoning for reasoning records
- Add better error logging for debugging record creation issues
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added create_tool_call_record() to publish stream.thought.tool_call records
- Added create_reasoning_record() to publish stream.thought.reasoning records
- Updated process_mention() to create records for both reasoning and tool calls
- Updated send_synthesis_message() to create records during synthesis
- Records include timestamp and preserve raw tool arguments as strings
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Attach day/month/year journal blocks during synthesis
- Create blocks with labels like void_day_2025_08_09
- Enhanced synthesis prompt explains journal usage
- Blocks detached after synthesis completes
- Allows void to maintain temporal memory across sessions
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
- Fixed user_note_append, user_note_replace, user_note_set, user_note_view functions
- Changed from get_letta_client() to inline Letta client creation for cloud compatibility
- Also updated attach/detach functions to handle both local and cloud execution
- Removed logger.debug call that wouldn't work in cloud environment
- All functions now create Letta client inline using environment variables
This fixes the critical failure void reported where user_note_* tools were non-functional
due to NameError when get_letta_client couldn't import config_loader in cloud.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Enable caching during thread context processing (was disabled, causing 50-80% more API calls)
- Increase fetch delay from 60s to 120s to reduce API call frequency
- These changes should significantly reduce API usage and prevent rate limit violations
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Per project requirements, tools executed in the cloud must be self-contained
and cannot use logging. Removed all logging imports and statements from:
- tools/webpage.py: Removed logging for webpage fetch operations
- tools/blocks.py: Removed extensive logging throughout user block management
This ensures compatibility with cloud execution environments.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Create search_x.py tool to fetch recent posts from X users
- Support filtering replies and retweets
- Return YAML-formatted results without public metrics
- Add tool to X-specific tool registration
- Update tool manager to include search_x_posts
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Sort mentions by creation time to ensure chronological reply order
- Add retry mechanism for mentions that fail to get agent responses
- Implement proper rate limit handling with 60-second delays
- Update queue processing to break on rate limits and restart from beginning
- Clean up processed mention files and cache updates
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- README.md: Updated to reflect Bluesky + X dual-platform operation
- Added platform-specific tool management documentation
- Enhanced feature descriptions with new capabilities
- Updated installation and usage instructions for both platforms
- Added troubleshooting guidance for platform switching
- TOOL_CHANGELOG.md: Complete rewrite with latest changes
- Documents platform-specific tool management system
- Added new tools: fetch_webpage, annotate_ack, create_whitewind_blog_post
- Enhanced features: reply structure fix, #voidstop keyword support
- Migration guidance for users updating to new features
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Creates tool_manager.py to automatically switch between Bluesky and X tools
- Updates bsky.py and x.py to configure appropriate tools on startup
- Fixes CLIENT.agents.get() API call to use agents.retrieve()
- Adds TOOL_MANAGEMENT.md documentation
- Ensures platform isolation: X tools only for x.py, Bluesky tools only for bsky.py
- Common tools (webpage, halt, etc.) remain available on both platforms
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add x_queue/last_seen_id.json to track last processed mention ID
- Add x_queue/processed_mentions.json to prevent duplicate processing
- Update .gitignore to track critical state files while ignoring temp files
- Ensures bot maintains state across restarts and deployments
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Implement downrank system for managing AI bot interactions (10% response rate)
- Add temporal constraints to thread context using until_id parameter
- Enhance conversation debugging with comprehensive data storage
- Fix X user note tools for cloud execution (remove logging, inline Letta client)
- Add x_downrank_users.txt with Grok user ID for controlled interactions
- Update CLAUDE.md with tool self-containment requirements and X bot commands
- Improve acknowledgment system to use Bluesky client for unified storage
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added detailed debugging data structure for X bot, saving conversation metadata and user analysis.
- Implemented common issue handling for incomplete thread context and cache staleness.
- Updated `get_thread_context()` to include an `until_id` parameter, allowing exclusion of future tweets.
- Enhanced mention processing with improved logging for conversation tracking and debugging.
- Saved agent response data and conversation debug information to dedicated debug folders for better analysis.
- Ensured tools are self-contained, removing reliance on shared functions and environment variables.
These changes improve the robustness of the X integration and facilitate better debugging and analysis of conversation flows.
## Core X Integration Features
- **X API Client**: Full X API v2 integration with OAuth 1.0a support
- **Queue System**: Fetch mentions and process separately for rate limit management
- **Thread Context**: Convert X conversations to YAML for agent comprehension
- **User Block Management**: X-specific user blocks with format `x_user_<user_id>`
## X Tool System
- **register_x_tools.py**: X-specific tool registration following Bluesky patterns
- **tools/x_thread.py**: X thread tool with 280 character limit validation
- **X_TOOL_APPROACH.md**: Documentation explaining tool-as-signal pattern
## Bot Functionality
- **process_x_mention()**: Complete X mention processing with Letta agent integration
- **X Reply Handling**: Thread construction and posting with proper reply chaining
- **Acknowledgment System**: X post acknowledgment tracking (file-based)
- **Testing Mode**: Safe testing without actual X posts
## Manual Queue Management
- `python x.py queue` - Fetch mentions only (rate limit safe)
- `python x.py process` - Process queued mentions only
- `python x.py bot` - Full bot loop (like bsky.py)
## Configuration Fix
- **config_loader.py**: Fixed to use config.yaml only, ignore environment variables
## Key Differences from Bluesky
- Character limit: 280 vs 300
- User identification: Numeric IDs vs handles
- Block format: `x_user_<user_id>` vs `user_<handle>`
- Acknowledgments: File-based vs stream.thought.ack API
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add agent_id field to letta section in config.yaml and config.example.yaml
- Update get_letta_config() to include agent_id as required field
- Modify initialize_void() in bsky.py to load agent by ID instead of name lookup
- Update ensure_x_user_blocks_attached() to use config agent_id by default
- Update test_x_blocks.py to use configured agent_id
- Ensures consistent agent usage across all components instead of name-based lookups
This centralizes agent management and prevents issues with agent name mismatches
between different environments or when agent names change.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add X-specific user block functions to tools/blocks.py:
- attach_x_user_blocks() for attaching user blocks by ID
- detach_x_user_blocks() for detaching user blocks
- x_user_note_append/replace/set/view() for block content management
- Block format: x_user_<author_id> as requested
- Add ensure_x_user_blocks_attached() to x.py:
- Automatically creates and attaches user blocks for thread participants
- Sets initial content with handle and name information
- Integrates with existing thread processing
- Update thread_to_yaml_string() to include author_id:
- Enables agent to reference user IDs for block management
- Maintains compatibility with existing YAML structure
- Fix config loading issue:
- Environment variable LETTA_API_KEY was overriding config file
- Now properly uses void-x agent (agent-4f7cc732-36bc-4e55-a5fa-f5d12eec6c5c)
- Successfully tested block creation and attachment
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Implement x_cache/ directory for caching thread data (1 hour TTL)
- Add get_cached_thread_context() and save_cached_thread_context()
- Update get_thread_context() with use_cache parameter
- Enhanced Letta integration test with config loading and error handling
- Rich formatting for better test output display
Caching prevents repeated API calls during development and testing
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add conversation_id field to mention search parameters
- Implement get_thread_context() to fetch complete conversation threads
- Add thread_to_yaml_string() for clean AI-readable thread format
- Fetch original tweets directly when missing from conversation search
- Sort tweets chronologically (oldest first) for proper context
- Add 'python x.py thread' command to test thread context retrieval
- Clean YAML output with only text, created_at, and author fields
Thread context now shows complete conversation history in correct order:
1. Original mention → 2. void's reply → 3. Follow-up responses
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add since_id-based incremental fetching to avoid rate limits
- Create x_queue/ directory system similar to Bluesky queue
- Track last seen mention ID for efficient polling
- Save mentions as JSON files for async processing
- Add single-pass fetch_and_queue_mentions() function
- Avoid duplicate processing with processed mentions tracking
Usage: python x.py queue (single pass, no loops)
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add X Developer account setup instructions
- Include X OAuth 1.0a configuration example
- Document X testing commands and rate limits
- Update key features to highlight cross-platform operation
- Add X-specific troubleshooting guidance
void now operates on both Bluesky AND X\! 🚀
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update config.example.yaml with OAuth 1.0a credential structure
- First successful X post: Reply ID 1950707109240373317
- Full X integration now working: mentions + posting
- Ready for Letta agent integration
Next: Connect void agent to X for autonomous posting
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Implement full OAuth 1.0a authentication using requests-oauthlib
- Support both OAuth 1.0a and Bearer token authentication methods
- Enhanced error logging reveals app needs 'Read and write' permissions
- Ready for posting once X app permissions are updated in Developer Portal
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Enhanced error logging reveals posting needs OAuth User Context
- Current Bearer token is Application-Only (read-only)
- Added documentation about authentication requirements
- Free tier allows 17 posts/day but needs proper auth method
Next: Need OAuth 2.0 User Context or OAuth 1.0a for posting
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Implement post_reply method in XClient class
- Add reply_to_cameron_post function for testing specific post
- Support command line args: loop, reply
- Handle API permissions and rate limiting gracefully
- Ready for elevated X API access when available
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add X API configuration to config.example.yaml
- Create x.py with XClient class for API interactions
- Implement basic mentions fetching with rate limiting
- Add simple notification loop for testing X integration
- Support for Bearer token authentication
- YAML conversion utilities for mention data
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Brings in all changes from rich-panel including:
- Webpage fetch tool using Jina AI reader
- Reply structure fix using root reference from notification
- #voidstop keyword support
- Annotate acknowledgment tool
- Stream.thought.ack acknowledgments
- Whitewind blog tool
- Improved logging and display formatting
- User block cleanup functionality
Adds fetch_webpage tool that converts web pages to markdown/text format
using Jina AI's reader service. Tool is registered and available for
void agent to access web content.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Instead of complex thread traversal that was overwriting root values,
now extracts root URI/CID directly from the notification's reply field.
If no reply field exists, treats the post as the root. This ensures
proper thread structure in Bluesky replies.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added #voidstop check in bsky.py to skip processing mentions containing this keyword
- Checks both the thread context and mention text for #voidstop
- When found, the mention is removed from queue without processing
- Created send_to_void.py CLI tool for quick message testing
- Tool streams void's responses with formatted output for reasoning, tool calls, and responses
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>