diff --git a/bsky.py b/bsky.py index ee14968..2e5aeae 100644 --- a/bsky.py +++ b/bsky.py @@ -195,7 +195,7 @@ def process_mention(void_agent, atproto_client, notification_data): None: Failed with non-retryable error, move to errors directory """ try: - logger.info(f"Starting process_mention with notification_data type: {type(notification_data)}") + logger.debug(f"Starting process_mention with notification_data type: {type(notification_data)}") # Handle both dict and object inputs for backwards compatibility if isinstance(notification_data, dict): @@ -288,13 +288,13 @@ Use the bluesky_reply tool to send a response less than 300 characters.""" all_handles.update(extract_handles_from_data(thread.model_dump())) unique_handles = list(all_handles) - logger.info(f"Found {len(unique_handles)} unique handles in thread: {unique_handles}") + logger.debug(f"Found {len(unique_handles)} unique handles in thread: {unique_handles}") # Attach user blocks before agent call attached_handles = [] if unique_handles: try: - logger.info(f"Attaching user blocks for handles: {unique_handles}") + logger.debug(f"Attaching user blocks for handles: {unique_handles}") attach_result = attach_user_blocks(unique_handles, void_agent) attached_handles = unique_handles # Track successfully attached handles logger.debug(f"Attach result: {attach_result}") @@ -389,6 +389,24 @@ Use the bluesky_reply tool to send a response less than 300 characters.""" else: logger.debug(f" {i}. {msg_type}: ") + # Check for halt_activity tool call + if hasattr(message, 'tool_call') and message.tool_call: + if message.tool_call.name == 'halt_activity': + logger.info("🛑 HALT_ACTIVITY TOOL CALLED - TERMINATING BOT") + try: + args = json.loads(message.tool_call.arguments) + reason = args.get('reason', 'Agent requested halt') + logger.info(f"Halt reason: {reason}") + except: + logger.info("Halt reason: ") + + # Export agent state before terminating + export_agent_state(CLIENT, void_agent) + + # Exit the program + logger.info("=== BOT TERMINATED BY AGENT ===") + exit(0) + # Collect bluesky_reply tool calls if hasattr(message, 'tool_call') and message.tool_call: if message.tool_call.name == 'bluesky_reply': diff --git a/register_tools.py b/register_tools.py index cb12878..bf93310 100755 --- a/register_tools.py +++ b/register_tools.py @@ -15,6 +15,7 @@ from tools.post import create_new_bluesky_post, PostArgs from tools.feed import get_bluesky_feed, FeedArgs from tools.blocks import attach_user_blocks, detach_user_blocks, AttachUserBlocksArgs, DetachUserBlocksArgs from tools.reply import bluesky_reply, ReplyArgs +from tools.halt import halt_activity, HaltArgs load_dotenv() logging.basicConfig(level=logging.INFO) @@ -60,6 +61,12 @@ TOOL_CONFIGS = [ "description": "Reply indicator for the Letta agent (1-4 messages, each max 300 chars). Creates threaded replies.", "tags": ["bluesky", "reply", "response"] }, + { + "func": halt_activity, + "args_schema": HaltArgs, + "description": "Signal to halt all bot activity and terminate bsky.py", + "tags": ["control", "halt", "terminate"] + }, ] diff --git a/tools/halt.py b/tools/halt.py new file mode 100644 index 0000000..a1bd2b4 --- /dev/null +++ b/tools/halt.py @@ -0,0 +1,26 @@ +"""Halt tool for terminating bsky.py activity.""" +from pydantic import BaseModel, Field + + +class HaltArgs(BaseModel): + reason: str = Field( + default="User requested halt", + description="Optional reason for halting activity" + ) + + +def halt_activity(reason: str = "User requested halt") -> str: + """ + Signal to halt all bot activity and terminate bsky.py. + + This tool allows the agent to request termination of the bot process. + The actual termination is handled externally by bsky.py when it detects + this tool being called. + + Args: + reason: Optional reason for halting (default: "User requested halt") + + Returns: + Halt signal message + """ + return f"Halting activity: {reason}" \ No newline at end of file