a digital person for bluesky

Add reason and confirm fields to memory deletion tool

Enhances the flag_archival_memory_for_deletion tool with safety guards:
- Added 'reason' field (required, first argument) to force reasoning
- Added 'confirm' boolean field (required, last argument) for explicit confirmation
- Tool only processes deletions when confirm=true
- Deletion handler extracts and logs the reason with each deletion

This pattern ensures the agent must:
1. Explain why it's deleting (reason)
2. Specify what it's deleting (memory_text)
3. Explicitly confirm the deletion (confirm)

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

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

Changed files
+32 -7
tools
+17 -5
bsky.py
··· 772 elif message.tool_call.name == 'flag_archival_memory_for_deletion': 773 try: 774 args = json.loads(message.tool_call.arguments) 775 memory_text = args.get('memory_text', '') 776 - if memory_text: 777 - flagged_memories.append(memory_text) 778 - logger.debug(f"Found memory flagged for deletion: {memory_text[:50]}...") 779 except json.JSONDecodeError as e: 780 logger.error(f"Failed to parse flag_archival_memory_for_deletion arguments: {e}") 781 ··· 803 # Handle archival memory deletion if any were flagged (only if no halt was received) 804 if flagged_memories: 805 logger.info(f"Processing {len(flagged_memories)} flagged memories for deletion") 806 - for memory_text in flagged_memories: 807 try: 808 # Search for passages with this exact text 809 logger.debug(f"Searching for passages matching: {memory_text[:100]}...") ··· 832 logger.error(f"Failed to delete passage {passage.id}: {delete_error}") 833 834 if deleted_count > 0: 835 - logger.info(f"🗑️ Deleted {deleted_count} archival memory passage(s): {memory_text[:50]}...") 836 else: 837 logger.warning(f"No exact matches found for deletion: {memory_text[:50]}...") 838
··· 772 elif message.tool_call.name == 'flag_archival_memory_for_deletion': 773 try: 774 args = json.loads(message.tool_call.arguments) 775 + reason = args.get('reason', '') 776 memory_text = args.get('memory_text', '') 777 + confirm = args.get('confirm', False) 778 + 779 + # Only flag for deletion if confirmed 780 + if confirm and memory_text: 781 + flagged_memories.append({ 782 + 'reason': reason, 783 + 'memory_text': memory_text 784 + }) 785 + logger.debug(f"Found memory flagged for deletion (reason: {reason}): {memory_text[:50]}...") 786 + elif not confirm: 787 + logger.debug(f"Memory deletion not confirmed, skipping: {memory_text[:50]}...") 788 except json.JSONDecodeError as e: 789 logger.error(f"Failed to parse flag_archival_memory_for_deletion arguments: {e}") 790 ··· 812 # Handle archival memory deletion if any were flagged (only if no halt was received) 813 if flagged_memories: 814 logger.info(f"Processing {len(flagged_memories)} flagged memories for deletion") 815 + for flagged_memory in flagged_memories: 816 + reason = flagged_memory['reason'] 817 + memory_text = flagged_memory['memory_text'] 818 + 819 try: 820 # Search for passages with this exact text 821 logger.debug(f"Searching for passages matching: {memory_text[:100]}...") ··· 844 logger.error(f"Failed to delete passage {passage.id}: {delete_error}") 845 846 if deleted_count > 0: 847 + logger.info(f"🗑️ Deleted {deleted_count} archival memory passage(s) (reason: {reason}): {memory_text[:50]}...") 848 else: 849 logger.warning(f"No exact matches found for deletion: {memory_text[:50]}...") 850
+15 -2
tools/flag_memory_deletion.py
··· 3 4 5 class FlagArchivalMemoryForDeletionArgs(BaseModel): 6 memory_text: str = Field( 7 ..., 8 description="The exact text content of the archival memory to delete" 9 ) 10 11 12 - def flag_archival_memory_for_deletion(memory_text: str) -> str: 13 """ 14 Flag an archival memory for deletion based on its exact text content. 15 ··· 20 The system will search for all archival memories with this exact text and delete them. 21 22 Args: 23 memory_text: The exact text content of the archival memory to delete 24 25 Returns: 26 Confirmation message 27 """ 28 # This is a dummy tool - it just returns a confirmation 29 # The actual deletion will be handled by the bot loop after the agent's turn completes 30 - return f"Memory flagged for deletion. Will be removed at the end of this turn if no halt is received."
··· 3 4 5 class FlagArchivalMemoryForDeletionArgs(BaseModel): 6 + reason: str = Field( 7 + ..., 8 + description="The reason why this memory should be deleted" 9 + ) 10 memory_text: str = Field( 11 ..., 12 description="The exact text content of the archival memory to delete" 13 ) 14 + confirm: bool = Field( 15 + ..., 16 + description="Confirmation that you want to delete this memory (must be true to proceed)" 17 + ) 18 19 20 + def flag_archival_memory_for_deletion(reason: str, memory_text: str, confirm: bool) -> str: 21 """ 22 Flag an archival memory for deletion based on its exact text content. 23 ··· 28 The system will search for all archival memories with this exact text and delete them. 29 30 Args: 31 + reason: The reason why this memory should be deleted 32 memory_text: The exact text content of the archival memory to delete 33 + confirm: Confirmation that you want to delete this memory (must be true) 34 35 Returns: 36 Confirmation message 37 """ 38 # This is a dummy tool - it just returns a confirmation 39 # The actual deletion will be handled by the bot loop after the agent's turn completes 40 + if not confirm: 41 + return "Deletion cancelled - confirm must be set to true to delete the memory." 42 + 43 + return f"Memory flagged for deletion (reason: {reason}). Will be removed at the end of this turn if no halt is received."