a digital person for bluesky

Add annotate_ack tool for attaching notes to acknowledgments

- Create dummy tool that allows agent to add custom notes to stream.thought.ack records
- Update acknowledge_post to accept optional note parameter
- Modify bot loop to capture notes from annotate_ack tool calls
- Add note field to stream.thought.ack records (null if not provided)
- Register tool with proper descriptions and tags

This allows void to optionally annotate acknowledgments with contextual observations.

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

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

+18 -2
bsky.py
··· 602 602 # Extract successful add_post_to_bluesky_reply_thread tool calls from the agent's response 603 603 reply_candidates = [] 604 604 tool_call_results = {} # Map tool_call_id to status 605 + ack_note = None # Track any note from annotate_ack tool 605 606 606 607 logger.debug(f"Processing {len(message_response.messages)} response messages...") 607 608 ··· 694 695 logger.info("=== BOT TERMINATED DUE TO DEPRECATED TOOL USE ===") 695 696 exit(1) 696 697 698 + # Collect annotate_ack tool calls 699 + elif message.tool_call.name == 'annotate_ack': 700 + try: 701 + args = json.loads(message.tool_call.arguments) 702 + note = args.get('note', '') 703 + if note: 704 + ack_note = note 705 + logger.debug(f"Found annotate_ack with note: {note[:50]}...") 706 + except json.JSONDecodeError as e: 707 + logger.error(f"Failed to parse annotate_ack arguments: {e}") 708 + 697 709 # Collect add_post_to_bluesky_reply_thread tool calls - only if they were successful 698 710 elif message.tool_call.name == 'add_post_to_bluesky_reply_thread': 699 711 tool_call_id = message.tool_call.tool_call_id ··· 789 801 ack_result = bsky_utils.acknowledge_post( 790 802 client=atproto_client, 791 803 post_uri=post_uri, 792 - post_cid=post_cid 804 + post_cid=post_cid, 805 + note=ack_note 793 806 ) 794 807 if ack_result: 795 - logger.info(f"Successfully acknowledged post from @{author_handle} with stream.thought.ack") 808 + if ack_note: 809 + logger.info(f"Successfully acknowledged post from @{author_handle} with stream.thought.ack (note: \"{ack_note[:50]}...\")") 810 + else: 811 + logger.info(f"Successfully acknowledged post from @{author_handle} with stream.thought.ack") 796 812 else: 797 813 logger.warning(f"Failed to acknowledge post from @{author_handle}") 798 814 else:
+4 -2
bsky_utils.py
··· 572 572 return None 573 573 574 574 575 - def acknowledge_post(client: Client, post_uri: str, post_cid: str) -> Optional[Dict[str, Any]]: 575 + def acknowledge_post(client: Client, post_uri: str, post_cid: str, note: Optional[str] = None) -> Optional[Dict[str, Any]]: 576 576 """ 577 577 Create a stream.thought.ack record to acknowledge a post. 578 578 ··· 583 583 client: Authenticated Bluesky client 584 584 post_uri: The URI of the post to acknowledge 585 585 post_cid: The CID of the post to acknowledge 586 + note: Optional note to attach to the acknowledgment 586 587 587 588 Returns: 588 589 The response from creating the acknowledgment record or None if failed ··· 621 622 "uri": post_uri, 622 623 "cid": post_cid 623 624 }, 624 - "createdAt": now 625 + "createdAt": now, 626 + "note": note # Will be null if no note provided 625 627 } 626 628 627 629 # Create the record
+7
register_tools.py
··· 18 18 from tools.thread import add_post_to_bluesky_reply_thread, ReplyThreadPostArgs 19 19 from tools.ignore import ignore_notification, IgnoreNotificationArgs 20 20 from tools.whitewind import create_whitewind_blog_post, WhitewindPostArgs 21 + from tools.ack import annotate_ack, AnnotateAckArgs 21 22 22 23 load_dotenv() 23 24 logging.basicConfig(level=logging.INFO) ··· 104 105 "args_schema": WhitewindPostArgs, 105 106 "description": "Create a blog post on Whitewind with markdown support", 106 107 "tags": ["whitewind", "blog", "post", "markdown"] 108 + }, 109 + { 110 + "func": annotate_ack, 111 + "args_schema": AnnotateAckArgs, 112 + "description": "Add a note to the acknowledgment record for the current post interaction", 113 + "tags": ["acknowledgment", "note", "annotation", "metadata"] 107 114 }, 108 115 ] 109 116
+3
tools/__init__.py
··· 5 5 from .feed import get_bluesky_feed, FeedArgs 6 6 from .blocks import attach_user_blocks, detach_user_blocks, AttachUserBlocksArgs, DetachUserBlocksArgs 7 7 from .whitewind import create_whitewind_blog_post, WhitewindPostArgs 8 + from .ack import annotate_ack, AnnotateAckArgs 8 9 9 10 __all__ = [ 10 11 # Functions ··· 14 15 "attach_user_blocks", 15 16 "detach_user_blocks", 16 17 "create_whitewind_blog_post", 18 + "annotate_ack", 17 19 # Pydantic models 18 20 "SearchArgs", 19 21 "PostArgs", ··· 21 23 "AttachUserBlocksArgs", 22 24 "DetachUserBlocksArgs", 23 25 "WhitewindPostArgs", 26 + "AnnotateAckArgs", 24 27 ]
+29
tools/ack.py
··· 1 + """Annotation tool for stream.thought.ack records.""" 2 + from typing import Optional 3 + from pydantic import BaseModel, Field 4 + 5 + 6 + class AnnotateAckArgs(BaseModel): 7 + note: str = Field( 8 + ..., 9 + description="A note or annotation to attach to the acknowledgment record" 10 + ) 11 + 12 + 13 + def annotate_ack(note: str) -> str: 14 + """ 15 + Add a note to the acknowledgment record for the current post interaction. 16 + 17 + This is a "dummy" tool that doesn't directly create records but signals to the system 18 + that a note should be included in the stream.thought.ack record when acknowledging 19 + the post you're replying to. 20 + 21 + Args: 22 + note: A note or annotation to attach to the acknowledgment 23 + 24 + Returns: 25 + Confirmation message 26 + """ 27 + # This is a dummy tool - it just returns a confirmation 28 + # The actual note will be captured by the bot loop and passed to acknowledge_post 29 + return f"Your note will be added to the acknowledgment: \"{note}\""