this repo has no description
40
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add archival memory recording to AT Protocol

Implemented stream.thought.memory record type to store archival_memory_insert
tool calls on the AT Protocol. This preserves important memories and context
in a queryable format.

Changes:
- Added create_memory_record() function in bsky_utils.py
- Records contain content (required) and tags (optional) fields
- Integrated into both notification processing and synthesis workflows
- Automatically detects archival_memory_insert tool calls
- Parses tags from string or array format
- Includes proper logging with emoji indicators (📝)

Tested with sample data - successfully creates records on the PDS.

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

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

+127 -1
+48 -1
bsky.py
··· 795 795 logger.warning(f"Memory deletion missing reason, skipping: {memory_text[:50]}...") 796 796 except json.JSONDecodeError as e: 797 797 logger.error(f"Failed to parse flag_archival_memory_for_deletion arguments: {e}") 798 - 798 + 799 + # Collect archival_memory_insert tool calls for recording to AT Protocol 800 + elif message.tool_call.name == 'archival_memory_insert': 801 + try: 802 + args = json.loads(message.tool_call.arguments) 803 + content = args.get('content', '') 804 + tags_str = args.get('tags', None) 805 + 806 + # Parse tags from string representation if present 807 + tags = None 808 + if tags_str: 809 + try: 810 + tags = json.loads(tags_str) if isinstance(tags_str, str) else tags_str 811 + except json.JSONDecodeError: 812 + logger.warning(f"Failed to parse tags from archival_memory_insert: {tags_str[:50]}...") 813 + 814 + if content: 815 + # Create stream.thought.memory record 816 + try: 817 + memory_result = bsky_utils.create_memory_record(client, content, tags) 818 + if memory_result: 819 + tags_info = f" ({len(tags)} tags)" if tags else "" 820 + logger.info(f"📝 Recorded archival memory to AT Protocol{tags_info}: {content[:100]}...") 821 + else: 822 + logger.warning(f"Failed to record archival memory to AT Protocol") 823 + except Exception as e: 824 + logger.error(f"Error creating memory record: {e}") 825 + except json.JSONDecodeError as e: 826 + logger.error(f"Failed to parse archival_memory_insert arguments: {e}") 827 + 799 828 # Collect add_post_to_bluesky_reply_thread tool calls - only if they were successful 800 829 elif message.tool_call.name == 'add_post_to_bluesky_reply_thread': 801 830 tool_call_id = message.tool_call.tool_call_id ··· 1546 1575 elif tool_name == 'archival_memory_insert': 1547 1576 content = args.get('content', '') 1548 1577 log_with_panel(content[:200] + "..." if len(content) > 200 else content, f"Tool call: {tool_name}", "blue") 1578 + 1579 + # Record archival memory insert to AT Protocol 1580 + if atproto_client: 1581 + try: 1582 + tags_str = args.get('tags', None) 1583 + tags = None 1584 + if tags_str: 1585 + try: 1586 + tags = json.loads(tags_str) if isinstance(tags_str, str) else tags_str 1587 + except json.JSONDecodeError: 1588 + logger.warning(f"Failed to parse tags from archival_memory_insert: {tags_str[:50]}...") 1589 + 1590 + memory_result = bsky_utils.create_memory_record(atproto_client, content, tags) 1591 + if memory_result: 1592 + tags_info = f" ({len(tags)} tags)" if tags else "" 1593 + logger.info(f"📝 Recorded archival memory to AT Protocol{tags_info}") 1594 + except Exception as e: 1595 + logger.debug(f"Failed to create memory record during synthesis: {e}") 1549 1596 elif tool_name == 'update_block': 1550 1597 label = args.get('label', 'unknown') 1551 1598 value_preview = str(args.get('value', ''))[:100] + "..." if len(str(args.get('value', ''))) > 100 else str(args.get('value', ''))
+79
bsky_utils.py
··· 1053 1053 return None 1054 1054 1055 1055 1056 + def create_memory_record(client: Client, content: str, tags: Optional[List[str]] = None) -> Optional[Dict[str, Any]]: 1057 + """ 1058 + Create a stream.thought.memory record to store archival memory insertions. 1059 + 1060 + This creates a record of archival_memory_insert tool calls, preserving 1061 + important memories and context in the AT Protocol. 1062 + 1063 + Args: 1064 + client: Authenticated Bluesky client 1065 + content: The memory content being archived 1066 + tags: Optional list of tags associated with this memory 1067 + 1068 + Returns: 1069 + The response from creating the memory record or None if failed 1070 + """ 1071 + try: 1072 + import requests 1073 + import json 1074 + from datetime import datetime, timezone 1075 + 1076 + # Get session info from the client 1077 + access_token = None 1078 + user_did = None 1079 + 1080 + # Try different ways to get the session info 1081 + if hasattr(client, '_session') and client._session: 1082 + access_token = client._session.access_jwt 1083 + user_did = client._session.did 1084 + elif hasattr(client, 'access_jwt'): 1085 + access_token = client.access_jwt 1086 + user_did = client.did if hasattr(client, 'did') else None 1087 + else: 1088 + logger.error("Cannot access client session information") 1089 + return None 1090 + 1091 + if not access_token or not user_did: 1092 + logger.error("Missing access token or DID from session") 1093 + return None 1094 + 1095 + # Get PDS URI from config instead of environment variables 1096 + from config_loader import get_bluesky_config 1097 + bluesky_config = get_bluesky_config() 1098 + pds_host = bluesky_config['pds_uri'] 1099 + 1100 + # Create memory record 1101 + now = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z") 1102 + memory_record = { 1103 + "$type": "stream.thought.memory", 1104 + "content": content, 1105 + "createdAt": now 1106 + } 1107 + 1108 + # Add tags if provided (can be null) 1109 + if tags is not None: 1110 + memory_record["tags"] = tags 1111 + 1112 + # Create the record 1113 + headers = {"Authorization": f"Bearer {access_token}"} 1114 + create_record_url = f"{pds_host}/xrpc/com.atproto.repo.createRecord" 1115 + 1116 + create_data = { 1117 + "repo": user_did, 1118 + "collection": "stream.thought.memory", 1119 + "record": memory_record 1120 + } 1121 + 1122 + response = requests.post(create_record_url, headers=headers, json=create_data, timeout=10) 1123 + response.raise_for_status() 1124 + result = response.json() 1125 + 1126 + tags_info = f" with {len(tags)} tags" if tags else " (no tags)" 1127 + logger.debug(f"Successfully recorded memory (length: {len(content)} chars{tags_info})") 1128 + return result 1129 + 1130 + except Exception as e: 1131 + logger.error(f"Error creating memory record: {e}") 1132 + return None 1133 + 1134 + 1056 1135 def sync_followers(client: Client, dry_run: bool = False) -> Dict[str, Any]: 1057 1136 """ 1058 1137 Check who is following the bot and who the bot is following,