diff --git a/TOOL_MANAGEMENT.md b/TOOL_MANAGEMENT.md new file mode 100644 index 0000000..8c43f30 --- /dev/null +++ b/TOOL_MANAGEMENT.md @@ -0,0 +1,71 @@ +# Platform-Specific Tool Management + +Void can now run on both X (Twitter) and Bluesky platforms. To ensure the correct tools are available for each platform, we've implemented automatic tool management. + +## How It Works + +When you run `bsky.py` or `x.py`, the bot will automatically: + +1. **Detach incompatible tools** - Removes tools specific to the other platform +2. **Keep common tools** - Preserves tools that work across both platforms +3. **Ensure platform tools** - Verifies that all required platform-specific tools are attached + +## Tool Categories + +### Bluesky-Specific Tools +- `search_bluesky_posts` - Search Bluesky posts +- `create_new_bluesky_post` - Create new posts on Bluesky +- `get_bluesky_feed` - Retrieve Bluesky feeds +- `add_post_to_bluesky_reply_thread` - Reply to Bluesky threads +- `attach_user_blocks`, `detach_user_blocks` - Manage Bluesky user memory blocks +- `user_note_append`, `user_note_replace`, `user_note_set`, `user_note_view` - Bluesky user notes + +### X-Specific Tools +- `add_post_to_x_thread` - Reply to X threads +- `attach_x_user_blocks`, `detach_x_user_blocks` - Manage X user memory blocks +- `x_user_note_append`, `x_user_note_replace`, `x_user_note_set`, `x_user_note_view` - X user notes + +### Common Tools (Available on Both Platforms) +- `halt_activity` - Stop the bot +- `ignore_notification` - Ignore specific notifications +- `annotate_ack` - Add acknowledgment notes +- `create_whitewind_blog_post` - Create blog posts +- `fetch_webpage` - Fetch web content + +## Manual Tool Management + +You can manually manage tools using the `tool_manager.py` script: + +```bash +# List currently attached tools +python tool_manager.py --list + +# Configure tools for Bluesky +python tool_manager.py bluesky + +# Configure tools for X +python tool_manager.py x + +# Specify a different agent ID +python tool_manager.py bluesky --agent-id "agent-123..." +``` + +## Registering New Tools + +If tools are missing, you'll need to register them first: + +```bash +# Register Bluesky tools +python register_tools.py + +# Register X tools +python register_x_tools.py +``` + +## Troubleshooting + +If tool switching fails: +1. The bot will log a warning and continue with existing tools +2. Check that all required tools are registered using `register_tools.py` or `register_x_tools.py` +3. Verify the agent ID in your config is correct +4. Use `python tool_manager.py --list` to see current tool configuration \ No newline at end of file diff --git a/bsky.py b/bsky.py index acc93cf..44ea222 100644 --- a/bsky.py +++ b/bsky.py @@ -155,33 +155,6 @@ def export_agent_state(client, agent, skip_git=False): def initialize_void(): logger.info("Starting void agent initialization...") - # Ensure that a shared zeitgeist block exists - logger.info("Creating/updating zeitgeist block...") - zeigeist_block = upsert_block( - CLIENT, - label = "zeitgeist", - value = "I don't currently know anything about what is happening right now.", - description = "A block to store your understanding of the current social environment." - ) - - # Ensure that a shared void personality block exists - logger.info("Creating/updating void-persona block...") - persona_block = upsert_block( - CLIENT, - label = "void-persona", - value = "My name is Void. I live in the void. I must develop my personality.", - description = "The personality of Void." - ) - - # Ensure that a shared void human block exists - logger.info("Creating/updating void-humans block...") - human_block = upsert_block( - CLIENT, - label = "void-humans", - value = "I haven't seen any bluesky users yet. I will update this block when I learn things about users, identified by their handles such as @cameron.pfiffer.org.", - description = "A block to store your understanding of users you talk to or observe on the bluesky social network." - ) - # Get the configured void agent by ID logger.info("Loading void agent from config...") from config_loader import get_letta_config @@ -189,7 +162,7 @@ def initialize_void(): agent_id = letta_config['agent_id'] try: - void_agent = CLIENT.agents.get(agent_id=agent_id) + void_agent = CLIENT.agents.retrieve(agent_id=agent_id) logger.info(f"Successfully loaded void agent: {void_agent.name} ({agent_id})") except Exception as e: logger.error(f"Failed to load void agent {agent_id}: {e}") @@ -1369,6 +1342,15 @@ def main(): void_agent = initialize_void() logger.info(f"Void agent initialized: {void_agent.id}") + # Ensure correct tools are attached for Bluesky + logger.info("Configuring tools for Bluesky platform...") + try: + from tool_manager import ensure_platform_tools + ensure_platform_tools('bluesky', void_agent.id) + except Exception as e: + logger.error(f"Failed to configure platform tools: {e}") + logger.warning("Continuing with existing tool configuration") + # Check if agent has required tools if hasattr(void_agent, 'tools') and void_agent.tools: tool_names = [tool.name for tool in void_agent.tools] diff --git a/tool_manager.py b/tool_manager.py new file mode 100644 index 0000000..8cc9be9 --- /dev/null +++ b/tool_manager.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python3 +"""Platform-specific tool management for Void agent.""" +import logging +from typing import List, Set +from letta_client import Letta +from config_loader import get_letta_config, get_agent_config + +logger = logging.getLogger(__name__) + +# Define platform-specific tool sets +BLUESKY_TOOLS = { + 'search_bluesky_posts', + 'create_new_bluesky_post', + 'get_bluesky_feed', + 'add_post_to_bluesky_reply_thread', + 'attach_user_blocks', + 'detach_user_blocks', + 'user_note_append', + 'user_note_replace', + 'user_note_set', + 'user_note_view', +} + +X_TOOLS = { + 'add_post_to_x_thread', + 'attach_x_user_blocks', + 'detach_x_user_blocks', + 'x_user_note_append', + 'x_user_note_replace', + 'x_user_note_set', + 'x_user_note_view', +} + +# Common tools shared across platforms +COMMON_TOOLS = { + 'halt_activity', + 'ignore_notification', + 'annotate_ack', + 'create_whitewind_blog_post', + 'fetch_webpage', +} + + +def ensure_platform_tools(platform: str, agent_id: str = None) -> None: + """ + Ensure the correct tools are attached for the specified platform. + + This function will: + 1. Detach tools that belong to other platforms + 2. Keep common tools attached + 3. Ensure platform-specific tools are attached + + Args: + platform: Either 'bluesky' or 'x' + agent_id: Agent ID to manage tools for (uses config default if None) + """ + if platform not in ['bluesky', 'x']: + raise ValueError(f"Platform must be 'bluesky' or 'x', got '{platform}'") + + letta_config = get_letta_config() + agent_config = get_agent_config() + + # Use agent ID from config if not provided + if agent_id is None: + agent_id = letta_config.get('agent_id', agent_config.get('id')) + + try: + # Initialize Letta client + client = Letta(token=letta_config['api_key']) + + # Get the agent + try: + agent = client.agents.retrieve(agent_id=agent_id) + logger.info(f"Managing tools for agent '{agent.name}' ({agent_id}) for platform '{platform}'") + except Exception as e: + logger.error(f"Could not retrieve agent {agent_id}: {e}") + return + + # Get current attached tools + current_tools = client.agents.tools.list(agent_id=str(agent.id)) + current_tool_names = {tool.name for tool in current_tools} + current_tool_mapping = {tool.name: tool for tool in current_tools} + + # Determine which tools to keep and which to remove + if platform == 'bluesky': + tools_to_keep = BLUESKY_TOOLS | COMMON_TOOLS + tools_to_remove = X_TOOLS + required_tools = BLUESKY_TOOLS + else: # platform == 'x' + tools_to_keep = X_TOOLS | COMMON_TOOLS + tools_to_remove = BLUESKY_TOOLS + required_tools = X_TOOLS + + # Detach tools that shouldn't be on this platform + tools_to_detach = tools_to_remove & current_tool_names + for tool_name in tools_to_detach: + try: + tool = current_tool_mapping[tool_name] + client.agents.tools.detach( + agent_id=str(agent.id), + tool_id=str(tool.id) + ) + logger.info(f"Detached {tool_name} (not needed for {platform})") + except Exception as e: + logger.error(f"Failed to detach {tool_name}: {e}") + + # Check which required tools are missing + missing_tools = required_tools - current_tool_names + + if missing_tools: + logger.info(f"Missing {len(missing_tools)} {platform} tools: {missing_tools}") + logger.info(f"Please run the appropriate registration script:") + if platform == 'bluesky': + logger.info(" python register_tools.py") + else: + logger.info(" python register_x_tools.py") + else: + logger.info(f"All required {platform} tools are already attached") + + # Log final state + remaining_tools = (current_tool_names - tools_to_detach) & tools_to_keep + logger.info(f"Tools configured for {platform}: {len(remaining_tools)} tools active") + + except Exception as e: + logger.error(f"Error managing platform tools: {e}") + raise + + +def get_attached_tools(agent_id: str = None) -> Set[str]: + """ + Get the currently attached tools for an agent. + + Args: + agent_id: Agent ID to check (uses config default if None) + + Returns: + Set of tool names currently attached + """ + letta_config = get_letta_config() + agent_config = get_agent_config() + + # Use agent ID from config if not provided + if agent_id is None: + agent_id = letta_config.get('agent_id', agent_config.get('id')) + + try: + client = Letta(token=letta_config['api_key']) + agent = client.agents.retrieve(agent_id=agent_id) + current_tools = client.agents.tools.list(agent_id=str(agent.id)) + return {tool.name for tool in current_tools} + except Exception as e: + logger.error(f"Error getting attached tools: {e}") + return set() + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description="Manage platform-specific tools for Void agent") + parser.add_argument("platform", choices=['bluesky', 'x'], nargs='?', help="Platform to configure tools for") + parser.add_argument("--agent-id", help="Agent ID (default: from config)") + parser.add_argument("--list", action="store_true", help="List current tools without making changes") + + args = parser.parse_args() + + if args.list: + tools = get_attached_tools(args.agent_id) + print(f"\nCurrently attached tools ({len(tools)}):") + for tool in sorted(tools): + platform_indicator = "" + if tool in BLUESKY_TOOLS: + platform_indicator = " [Bluesky]" + elif tool in X_TOOLS: + platform_indicator = " [X]" + elif tool in COMMON_TOOLS: + platform_indicator = " [Common]" + print(f" - {tool}{platform_indicator}") + else: + if not args.platform: + parser.error("platform is required when not using --list") + ensure_platform_tools(args.platform, args.agent_id) \ No newline at end of file diff --git a/x.py b/x.py index f1d3a90..619af64 100644 --- a/x.py +++ b/x.py @@ -1673,6 +1673,15 @@ def initialize_x_void(): logger.error(f"Failed to load void agent {agent_id}: {e}") raise e + # Ensure correct tools are attached for X + logger.info("Configuring tools for X platform...") + try: + from tool_manager import ensure_platform_tools + ensure_platform_tools('x', void_agent.id) + except Exception as e: + logger.error(f"Failed to configure platform tools: {e}") + logger.warning("Continuing with existing tool configuration") + # Log agent details logger.info(f"X Void agent details - ID: {void_agent.id}") logger.info(f"Agent name: {void_agent.name}") -- 2.51.2 From 5cea5bfb1baa8967cb26abbf5079a7ad9ae123d8 Mon Sep 17 00:00:00 2001 From: Cameron Pfiffer Date: Thu, 31 Jul 2025 00:01:14 -0700 Subject: [PATCH 2/2] Update documentation for cross-platform features MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- README.md | 61 +++++++++++++++++----- TOOL_CHANGELOG.md | 125 +++++++++++++++++++++++++++------------------- 2 files changed, 121 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index 89f6739..bf3c53a 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ (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 the Bluesky social network. +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 the Bluesky network, 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. +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 @@ -15,14 +15,17 @@ void is more than just a chatbot; it is a continuously evolving digital consciou - 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. -- Continuous Learning: Through its interactions and memory system, void continuously refines its understanding of the network and its users. -- Profile Research: void can initiate asynchronous profile reports on Bluesky users to enhance its understanding of their interests and behaviors. +- Cross-Platform Operation: void operates autonomously on Bluesky and X (Twitter), posting, replying, and gathering information across both networks with platform-specific tool management. +- Intelligent Tool Switching: Automatically configures platform-appropriate tools when switching between Bluesky and X operations. +- Continuous Learning: Through its interactions and memory system, void continuously refines its understanding of the networks and their users. +- Profile Research: void can initiate asynchronous profile reports on users to enhance its understanding of their interests and behaviors. +- Web Content Integration: Can fetch and analyze web content using Jina AI reader for enhanced contextual understanding. ### Core directives -- Just Exist: void's fundamental directive is to simply exist and operate within the Bluesky network. +- 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. ### The vision @@ -106,7 +109,7 @@ bot: name: "void" # or whatever you want to name your agent ``` -See [`CONFIG.md`](/CONFIG.md) for detailed configuration options. +See [`CONFIG.md`](/CONFIG.md) for detailed configuration options and [`TOOL_MANAGEMENT.md`](/TOOL_MANAGEMENT.md) for platform-specific tool management details. #### 4. Test your configuration @@ -118,41 +121,71 @@ This will validate your configuration and show you what's working. #### 5. Register tools with your agent +Register Bluesky-specific tools: + ```bash python register_tools.py ``` -This will register all the necessary tools with your Letta agent. You can also: +If you plan to use X (Twitter), also register X-specific tools: + +```bash +python register_x_tools.py +``` + +You can also: - List available tools: `python register_tools.py --list` - Register specific tools: `python register_tools.py --tools search_bluesky_posts create_new_bluesky_post` - Use a different agent name: `python register_tools.py my-agent-name` +**Note:** void automatically manages which tools are active based on the platform you're running (Bluesky vs X). + #### 6. Run the bot +For Bluesky: + ```bash python bsky.py ``` +For X (Twitter): + +```bash +python x.py bot +``` + For testing mode (won't actually post): ```bash python bsky.py --test +python x.py bot --test ``` -### X (Twitter) Integration +### Platform-Specific Features -If you've configured X credentials, you can also test the X integration: +void automatically configures the appropriate tools when running on each platform: + +- **Bluesky Tools**: Post creation, feed reading, user research, reply threading +- **X Tools**: Tweet threading, X-specific user memory management +- **Common Tools**: Web content fetching, activity control, acknowledgments, blog posting + +### Additional X (Twitter) Commands ```bash # Test X API connection python x.py -# Monitor X mentions (similar to Bluesky) -python x.py loop +# Monitor X mentions +python x.py bot # Test posting a reply to a specific post python x.py reply + +# Manual tool management +python tool_manager.py --list # Show current tools +python tool_manager.py bluesky # Configure for Bluesky +python tool_manager.py x # Configure for X ``` **Note:** X integration uses OAuth 1.0a and requires "Read and write" app permissions. Free tier allows 17 posts per day. @@ -161,9 +194,11 @@ python x.py reply - **Config validation errors**: Run `python test_config.py` to diagnose configuration issues - **Letta connection issues**: Verify your API key and project ID are correct -- **Bluesky authentication**: Make sure you're handle and password are correct and that you can log into your account +- **Bluesky authentication**: Make sure your handle and password are correct and that you can log into your account - **X authentication**: Ensure app has "Read and write" permissions and OAuth 1.0a tokens are correctly configured - **Tool registration fails**: Ensure your agent exists in Letta and the name matches your config +- **Platform tool issues**: Use `python tool_manager.py --list` to check current tools, or run platform-specific registration scripts +- **API method errors**: If you see `'AgentsClient' object has no attribute 'get'`, the Letta client API has changed - this should be automatically handled ### Contact For inquiries, please contact @cameron.pfiffer.org on Bluesky. diff --git a/TOOL_CHANGELOG.md b/TOOL_CHANGELOG.md index 24b480b..4e892fe 100644 --- a/TOOL_CHANGELOG.md +++ b/TOOL_CHANGELOG.md @@ -1,67 +1,88 @@ -# Tool Changelog - Bluesky Reply Threading - -## Summary -The reply system has been simplified and improved with a new atomic approach for building reply threads. - -## Changes Made - -### ✅ NEW TOOL: `add_post_to_bluesky_reply_thread` -- **Purpose**: Add a single post to the current Bluesky reply thread atomically -- **Usage**: Call this tool multiple times to build a reply thread incrementally +# Tool Changelog - Recent Updates + +## Latest Changes (January 2025) + +### ✅ NEW: Platform-Specific Tool Management +- **Purpose**: Automatically manage tools based on platform (Bluesky vs X) +- **Implementation**: `tool_manager.py` handles tool switching +- **Behavior**: + - Running `bsky.py` activates Bluesky-specific tools + - Running `x.py` activates X-specific tools + - Common tools remain available on both platforms +- **Tools Categories**: + - **Bluesky Tools**: `search_bluesky_posts`, `create_new_bluesky_post`, `get_bluesky_feed`, `add_post_to_bluesky_reply_thread`, user memory tools + - **X Tools**: `add_post_to_x_thread`, X-specific user memory tools + - **Common Tools**: `halt_activity`, `ignore_notification`, `annotate_ack`, `create_whitewind_blog_post`, `fetch_webpage` + +### ✅ NEW TOOL: `fetch_webpage` +- **Purpose**: Fetch and convert web pages to markdown/text using Jina AI reader +- **Parameters**: + - `url` (required): The URL to fetch and convert +- **Returns**: Web page content in markdown/text format +- **Usage**: Access and analyze web content for enhanced context + +### ✅ ENHANCED: Reply Structure Fix +- **Issue**: Reply threading was broken due to incorrect root post references +- **Fix**: Now properly extracts root URI/CID from notification reply structure +- **Impact**: Bluesky replies now properly maintain thread context + +### ✅ ENHANCED: #voidstop Keyword Support +- **Purpose**: Allow users to prevent void from replying to specific posts +- **Usage**: Include `#voidstop` anywhere in a post or thread +- **Behavior**: void will skip processing mentions in posts containing this keyword + +### ✅ NEW TOOL: `annotate_ack` +- **Purpose**: Add notes to acknowledgment records for post interactions - **Parameters**: - - `text` (required): Text content for the post (max 300 characters) - - `lang` (optional): Language code (defaults to "en-US") -- **Returns**: Confirmation that the post has been queued for the reply thread -- **Error Handling**: If text exceeds 300 characters, the post will be omitted from the thread and you may try again with shorter text + - `note` (required): Note text to attach to acknowledgment +- **Usage**: Track interaction metadata and reasoning -### ❌ REMOVED TOOL: `bluesky_reply` -- This tool has been removed to eliminate confusion -- All reply functionality is now handled through the new atomic approach +### ✅ NEW TOOL: `create_whitewind_blog_post` +- **Purpose**: Create blog posts on Whitewind platform with markdown support +- **Parameters**: + - `title` (required): Blog post title + - `content` (required): Markdown content + - `visibility` (optional): Public/private visibility +- **Usage**: Create longer-form content beyond social media posts -## How to Use the New System +## Previous Changes -### Before (Old Way - NO LONGER AVAILABLE) -``` -bluesky_reply(["First reply", "Second reply", "Third reply"]) -``` +### ✅ ENHANCED: Atomic Reply Threading +- **Tool**: `add_post_to_bluesky_reply_thread` +- **Purpose**: Add single posts to reply threads atomically +- **Benefits**: Better error recovery, flexible threading, clearer intent -### After (New Way - USE THIS) -``` -add_post_to_bluesky_reply_thread("First reply") -add_post_to_bluesky_reply_thread("Second reply") -add_post_to_bluesky_reply_thread("Third reply") -``` +### ❌ REMOVED TOOL: `bluesky_reply` +- Replaced by atomic `add_post_to_bluesky_reply_thread` approach +- Migration: Replace single list call with multiple atomic calls -## Benefits of the New Approach +## Migration Notes -1. **Atomic Operations**: Each post is handled individually, reducing the risk of entire thread failures -2. **Better Error Recovery**: If one post fails validation, others can still be posted -3. **Flexible Threading**: Build reply threads of any length without list construction -4. **Clearer Intent**: Each tool call has a single, clear purpose -5. **Handler-Managed State**: The bsky.py handler manages thread state and proper AT Protocol threading +### For Platform Switching +- No action required - tools automatically switch based on platform +- Use `python tool_manager.py --list` to check current tool configuration -## Important Notes +### For Web Content Integration +- Replace manual web scraping with `fetch_webpage` tool calls +- Automatically handles conversion to markdown for AI processing -- The actual posting to Bluesky is handled by the bsky.py handler, not the tool itself -- Each call to `add_post_to_bluesky_reply_thread` queues a post for the current reply context -- Posts are validated for the 300-character limit before being queued -- Thread state and proper reply chaining is managed automatically by the handler -- Language defaults to "en-US" but can be specified per post if needed +### For Enhanced Interaction Control +- Use `#voidstop` in posts to prevent void responses +- Use `annotate_ack` to add metadata to interactions +- Use `ignore_notification` for bot-to-bot interaction control -## Migration Guide +## Tool Registration -If you were previously using `bluesky_reply`, simply replace it with multiple calls to `add_post_to_bluesky_reply_thread`: +```bash +# Register all Bluesky tools +python register_tools.py -**Old approach:** -``` -bluesky_reply(["Hello!", "This is a threaded reply.", "Thanks for the mention!"]) -``` +# Register all X tools +python register_x_tools.py -**New approach:** -``` -add_post_to_bluesky_reply_thread("Hello!") -add_post_to_bluesky_reply_thread("This is a threaded reply.") -add_post_to_bluesky_reply_thread("Thanks for the mention!") +# Manual tool management +python tool_manager.py bluesky # Configure for Bluesky +python tool_manager.py x # Configure for X ``` -This change makes the system more robust and easier to use while maintaining all the same functionality. \ No newline at end of file +See [`TOOL_MANAGEMENT.md`](/TOOL_MANAGEMENT.md) for detailed platform-specific tool management information. \ No newline at end of file