a digital person for bluesky

Add --config flag and bot-specific queue namespacing

- Add --config command-line argument to specify custom config file
- Implement bot.name-based queue directory namespacing
- bot.name: void → queue/ (backward compatible)
- bot.name: herald → queue_herald/
- Other names → queue_{bot_name}/
- Each bot now has isolated queues, databases, and error directories
- Move config/client initialization into main() after argument parsing
- Update config_loader.py to build queue paths from bot.name

This allows multiple bots to run independently without queue conflicts.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

Changed files
+52 -29
+38 -24
bsky.py
··· 17 17 upsert_block, 18 18 upsert_agent 19 19 ) 20 - from config_loader import get_letta_config 20 + from config_loader import get_letta_config, get_config, get_queue_config 21 21 22 22 import bsky_utils 23 23 from tools.blocks import attach_user_blocks, detach_user_blocks ··· 74 74 print(message) 75 75 76 76 77 - # Load Letta configuration from config.yaml 78 - letta_config = get_letta_config() 79 - 80 - # Create a client with configuration from config.yaml 81 - CLIENT_PARAMS = { 82 - 'token': letta_config['api_key'], 83 - 'timeout': letta_config['timeout'] 84 - } 85 - if letta_config.get('base_url'): 86 - CLIENT_PARAMS['base_url'] = letta_config['base_url'] 87 - 88 - CLIENT = Letta(**CLIENT_PARAMS) 77 + # Load Letta configuration from config.yaml (will be initialized later with custom path if provided) 78 + letta_config = None 79 + CLIENT = None 89 80 90 81 # Notification check delay 91 82 FETCH_NOTIFICATIONS_DELAY_SEC = 10 # Check every 10 seconds for faster response ··· 93 84 # Check for new notifications every N queue items 94 85 CHECK_NEW_NOTIFICATIONS_EVERY_N_ITEMS = 2 # Check more frequently during processing 95 86 96 - # Queue directory 97 - QUEUE_DIR = Path("queue") 98 - QUEUE_DIR.mkdir(exist_ok=True) 99 - QUEUE_ERROR_DIR = Path("queue/errors") 100 - QUEUE_ERROR_DIR.mkdir(exist_ok=True, parents=True) 101 - QUEUE_NO_REPLY_DIR = Path("queue/no_reply") 102 - QUEUE_NO_REPLY_DIR.mkdir(exist_ok=True, parents=True) 103 - PROCESSED_NOTIFICATIONS_FILE = Path("queue/processed_notifications.json") 87 + # Queue paths (will be initialized from config in main()) 88 + QUEUE_DIR = None 89 + QUEUE_ERROR_DIR = None 90 + QUEUE_NO_REPLY_DIR = None 91 + PROCESSED_NOTIFICATIONS_FILE = None 104 92 105 93 # Maximum number of processed notifications to track 106 94 MAX_PROCESSED_NOTIFICATIONS = 10000 ··· 1790 1778 def main(): 1791 1779 # Parse command line arguments 1792 1780 parser = argparse.ArgumentParser(description='Void Bot - Bluesky autonomous agent') 1781 + parser.add_argument('--config', type=str, default='config.yaml', help='Path to config file (default: config.yaml)') 1793 1782 parser.add_argument('--test', action='store_true', help='Run in testing mode (no messages sent, queue files preserved)') 1794 1783 parser.add_argument('--no-git', action='store_true', help='Skip git operations when exporting agent state') 1795 1784 parser.add_argument('--simple-logs', action='store_true', help='Use simplified log format (void - LEVEL - message)') ··· 1799 1788 parser.add_argument('--synthesis-interval', type=int, default=600, help='Send synthesis message every N seconds (default: 600 = 10 minutes, 0 to disable)') 1800 1789 parser.add_argument('--synthesis-only', action='store_true', help='Run in synthesis-only mode (only send synthesis messages, no notification processing)') 1801 1790 args = parser.parse_args() 1791 + 1792 + # Initialize configuration with custom path 1793 + global letta_config, CLIENT, QUEUE_DIR, QUEUE_ERROR_DIR, QUEUE_NO_REPLY_DIR, PROCESSED_NOTIFICATIONS_FILE, NOTIFICATION_DB 1794 + get_config(args.config) # Initialize the global config instance 1795 + letta_config = get_letta_config() 1796 + 1797 + # Initialize queue paths from config 1798 + queue_config = get_queue_config() 1799 + QUEUE_DIR = Path(queue_config['base_dir']) 1800 + QUEUE_ERROR_DIR = Path(queue_config['error_dir']) 1801 + QUEUE_NO_REPLY_DIR = Path(queue_config['no_reply_dir']) 1802 + PROCESSED_NOTIFICATIONS_FILE = Path(queue_config['processed_file']) 1803 + 1804 + # Create queue directories 1805 + QUEUE_DIR.mkdir(exist_ok=True) 1806 + QUEUE_ERROR_DIR.mkdir(exist_ok=True, parents=True) 1807 + QUEUE_NO_REPLY_DIR.mkdir(exist_ok=True, parents=True) 1808 + 1809 + # Create Letta client with configuration 1810 + CLIENT_PARAMS = { 1811 + 'token': letta_config['api_key'], 1812 + 'timeout': letta_config['timeout'] 1813 + } 1814 + if letta_config.get('base_url'): 1815 + CLIENT_PARAMS['base_url'] = letta_config['base_url'] 1816 + CLIENT = Letta(**CLIENT_PARAMS) 1802 1817 1803 1818 # Configure logging based on command line arguments 1804 1819 if args.simple_logs: ··· 1897 1912 void_agent = initialize_void() 1898 1913 logger.info(f"Void agent initialized: {void_agent.id}") 1899 1914 1900 - # Initialize notification database 1901 - global NOTIFICATION_DB 1915 + # Initialize notification database with config-based path 1902 1916 logger.info("Initializing notification database...") 1903 - NOTIFICATION_DB = NotificationDB() 1917 + NOTIFICATION_DB = NotificationDB(db_path=queue_config['db_path']) 1904 1918 1905 1919 # Migrate from old JSON format if it exists 1906 1920 if PROCESSED_NOTIFICATIONS_FILE.exists():
+14 -5
config_loader.py
··· 218 218 } 219 219 220 220 def get_queue_config() -> Dict[str, Any]: 221 - """Get queue configuration.""" 221 + """Get queue configuration with bot_name-based namespacing.""" 222 222 config = get_config() 223 + 224 + # Get bot name for queue namespacing (defaults to 'void' for backward compatibility) 225 + bot_name = config.get('bot.name', 'void') 226 + 227 + # Build queue paths with bot name 228 + base_dir = f'queue_{bot_name}' if bot_name != 'void' else 'queue' 229 + 223 230 return { 231 + 'bot_name': bot_name, 224 232 'priority_users': config.get('queue.priority_users', ['cameron.pfiffer.org']), 225 - 'base_dir': config.get('queue.base_dir', 'queue'), 226 - 'error_dir': config.get('queue.error_dir', 'queue/errors'), 227 - 'no_reply_dir': config.get('queue.no_reply_dir', 'queue/no_reply'), 228 - 'processed_file': config.get('queue.processed_file', 'queue/processed_notifications.json'), 233 + 'base_dir': base_dir, 234 + 'error_dir': f'{base_dir}/errors', 235 + 'no_reply_dir': f'{base_dir}/no_reply', 236 + 'processed_file': f'{base_dir}/processed_notifications.json', 237 + 'db_path': f'{base_dir}/notifications.db', 229 238 }