a digital person for bluesky

Migrate to Letta SDK v1.0 and rename whitewind tool to blog_post_create

SDK Migration (letta-client 0.1.235 → 1.0.0):
- Letta() constructor: token → api_key
- Method renames: .modify() → .update()
- Streaming: .create_stream() → .stream()
- Pagination: .list() now returns page objects (use .items)

Tool Rename:
- create_whitewind_blog_post → blog_post_create
- WhitewindPostArgs → BlogPostCreateArgs
- Updated docstrings to reference Greengale service (greengale.app)

🤖 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

+108 -91
+4 -3
attach_user_block.py
··· 21 21 """Create and attach a user block to an agent.""" 22 22 23 23 # Create client 24 - client = Letta(token=os.environ["LETTA_API_KEY"]) 24 + client = Letta(api_key=os.environ["LETTA_API_KEY"]) # v1.0: token → api_key 25 25 26 26 # Find the agent 27 27 agents = client.agents.list(name=agent_name) ··· 36 36 print(f"🔍 Creating user block for {handle}...") 37 37 user_block = create_user_block_for_handle(client, handle) 38 38 39 - # Check if already attached 40 - agent_blocks = client.agents.blocks.list(agent_id=agent.id) 39 + # Check if already attached (v1.0: list returns page object) 40 + agent_blocks_page = client.agents.blocks.list(agent_id=agent.id) 41 + agent_blocks = agent_blocks_page.items if hasattr(agent_blocks_page, 'items') else agent_blocks_page 41 42 for block in agent_blocks: 42 43 if block.id == user_block.id: 43 44 print(f"✅ User block for {handle} is already attached to {agent.name}")
+17 -10
bsky.py
··· 412 412 413 413 try: 414 414 # Use streaming to avoid 524 timeout errors 415 - message_stream = CLIENT.agents.messages.create_stream( 415 + message_stream = CLIENT.agents.messages.stream( 416 416 agent_id=void_agent.id, 417 417 messages=[{"role": "user", "content": prompt}], 418 418 stream_tokens=False, # Step streaming only (faster than token streaming) ··· 899 899 try: 900 900 # Search for passages with this exact text 901 901 logger.debug(f"Searching for passages matching: {memory_text[:100]}...") 902 - passages = CLIENT.agents.passages.list( 902 + passages_page = CLIENT.agents.passages.list( 903 903 agent_id=void_agent.id, 904 904 query=memory_text 905 905 ) 906 + passages = passages_page.items if hasattr(passages_page, 'items') else passages_page 906 907 907 908 if not passages: 908 909 logger.warning(f"No passages found matching flagged memory: {memory_text[:50]}...") ··· 1581 1582 logger.info("🧠 Sending enhanced synthesis prompt to agent") 1582 1583 1583 1584 # Send synthesis message with streaming to show tool use 1584 - message_stream = client.agents.messages.create_stream( 1585 + message_stream = client.agents.messages.stream( 1585 1586 agent_id=agent_id, 1586 1587 messages=[{"role": "user", "content": synthesis_prompt}], 1587 1588 stream_tokens=False, ··· 1762 1763 attached_labels = [] 1763 1764 1764 1765 # Get current blocks attached to agent 1765 - current_blocks = client.agents.blocks.list(agent_id=agent_id) 1766 + current_blocks_page = client.agents.blocks.list(agent_id=agent_id) 1767 + current_blocks = current_blocks_page.items if hasattr(current_blocks_page, 'items') else current_blocks_page 1766 1768 current_block_labels = {block.label for block in current_blocks} 1767 1769 current_block_ids = {str(block.id) for block in current_blocks} 1768 1770 ··· 1775 1777 continue 1776 1778 1777 1779 # Check if block exists globally 1778 - blocks = client.blocks.list(label=label) 1780 + blocks_page = client.blocks.list(label=label) 1781 + blocks = blocks_page.items if hasattr(blocks_page, 'items') else blocks_page 1779 1782 1780 1783 if blocks and len(blocks) > 0: 1781 1784 block = blocks[0] ··· 1853 1856 ] 1854 1857 1855 1858 # Get current blocks and build label to ID mapping 1856 - current_blocks = client.agents.blocks.list(agent_id=agent_id) 1859 + current_blocks_page = client.agents.blocks.list(agent_id=agent_id) 1860 + current_blocks = current_blocks_page.items if hasattr(current_blocks_page, 'items') else current_blocks_page 1857 1861 block_label_to_id = {block.label: str(block.id) for block in current_blocks} 1858 1862 1859 1863 detached_count = 0 ··· 1908 1912 attached_labels = [] 1909 1913 1910 1914 try: 1911 - current_blocks = client.agents.blocks.list(agent_id=agent_id) 1915 + current_blocks_page = client.agents.blocks.list(agent_id=agent_id) 1916 + current_blocks = current_blocks_page.items if hasattr(current_blocks_page, 'items') else current_blocks_page 1912 1917 current_block_labels = {block.label for block in current_blocks} 1913 1918 current_block_ids = {str(block.id) for block in current_blocks} 1914 1919 ··· 1923 1928 attached_labels.append(label) 1924 1929 continue 1925 1930 1926 - blocks = client.blocks.list(label=label) 1931 + blocks_page = client.blocks.list(label=label) 1932 + blocks = blocks_page.items if hasattr(blocks_page, 'items') else blocks_page 1927 1933 1928 1934 if blocks and len(blocks) > 0: 1929 1935 block = blocks[0] ··· 1978 1984 return True 1979 1985 1980 1986 try: 1981 - current_blocks = client.agents.blocks.list(agent_id=agent_id) 1987 + current_blocks_page = client.agents.blocks.list(agent_id=agent_id) 1988 + current_blocks = current_blocks_page.items if hasattr(current_blocks_page, 'items') else current_blocks_page 1982 1989 block_label_to_id = {block.label: str(block.id) for block in current_blocks} 1983 1990 1984 1991 detached_count = 0 ··· 2041 2048 2042 2049 # Create Letta client with configuration 2043 2050 CLIENT_PARAMS = { 2044 - 'token': letta_config['api_key'], 2051 + 'api_key': letta_config['api_key'], # v1.0: token → api_key 2045 2052 'timeout': letta_config['timeout'] 2046 2053 } 2047 2054 if letta_config.get('base_url'):
+11 -10
register_tools.py
··· 16 16 from tools.halt import halt_activity, HaltArgs 17 17 from tools.thread import add_post_to_bluesky_reply_thread, ReplyThreadPostArgs 18 18 from tools.ignore import ignore_notification, IgnoreNotificationArgs 19 - from tools.whitewind import create_whitewind_blog_post, WhitewindPostArgs 19 + from tools.whitewind import blog_post_create, BlogPostCreateArgs 20 20 from tools.ack import annotate_ack, AnnotateAckArgs 21 21 from tools.webpage import fetch_webpage, WebpageArgs 22 22 from tools.flag_memory_deletion import flag_archival_memory_for_deletion, FlagArchivalMemoryForDeletionArgs ··· 65 65 "tags": ["notification", "ignore", "control", "bot"] 66 66 }, 67 67 { 68 - "func": create_whitewind_blog_post, 69 - "args_schema": WhitewindPostArgs, 70 - "description": "Create a blog post on Whitewind with markdown support", 71 - "tags": ["whitewind", "blog", "post", "markdown"] 68 + "func": blog_post_create, 69 + "args_schema": BlogPostCreateArgs, 70 + "description": "Create a blog post on Greengale (served at greengale.app) with markdown support", 71 + "tags": ["greengale", "blog", "post", "markdown"] 72 72 }, 73 73 { 74 74 "func": annotate_ack, ··· 109 109 try: 110 110 # Initialize Letta client with API key and base_url from config 111 111 client_params = { 112 - 'token': letta_config['api_key'], 112 + 'api_key': letta_config['api_key'], # v1.0: token → api_key 113 113 'timeout': letta_config['timeout'] 114 114 } 115 115 if letta_config.get('base_url'): ··· 139 139 console.print(f" PDS_URI: {env_vars['PDS_URI']}") 140 140 console.print(f" BSKY_PASSWORD: {'*' * len(env_vars['BSKY_PASSWORD'])}\n") 141 141 142 - # Modify agent with environment variables 143 - client.agents.modify( 142 + # Update agent with environment variables (v1.0: modify → update) 143 + client.agents.update( 144 144 agent_id=agent_id, 145 145 tool_exec_environment_variables=env_vars 146 146 ) ··· 177 177 tags=tool_config["tags"] 178 178 ) 179 179 180 - # Get current agent tools 181 - current_tools = client.agents.tools.list(agent_id=str(agent.id)) 180 + # Get current agent tools (v1.0: list returns page object) 181 + current_tools_page = client.agents.tools.list(agent_id=str(agent.id)) 182 + current_tools = current_tools_page.items if hasattr(current_tools_page, 'items') else current_tools_page 182 183 tool_names = [t.name for t in current_tools] 183 184 184 185 # Check if already attached
+4 -3
register_x_tools.py
··· 101 101 try: 102 102 # Initialize Letta client with API key and base_url from config 103 103 client_params = { 104 - 'token': letta_config['api_key'], 104 + 'api_key': letta_config['api_key'], # v1.0: token → api_key 105 105 'timeout': letta_config['timeout'] 106 106 } 107 107 if letta_config.get('base_url'): ··· 147 147 tags=tool_config["tags"] 148 148 ) 149 149 150 - # Get current agent tools 151 - current_tools = client.agents.tools.list(agent_id=str(agent.id)) 150 + # Get current agent tools (v1.0: list returns page object) 151 + current_tools_page = client.agents.tools.list(agent_id=str(agent.id)) 152 + current_tools = current_tools_page.items if hasattr(current_tools_page, 'items') else current_tools_page 152 153 tool_names = [t.name for t in current_tools] 153 154 154 155 # Check if already attached
+1 -1
requirements.txt
··· 12 12 httpx==0.28.1 13 13 httpx-sse==0.4.0 14 14 idna==3.10 15 - letta-client==0.1.235 15 + letta-client==1.0.0 16 16 libipld==3.1.1 17 17 markdown-it-py==3.0.0 18 18 mdurl==0.1.2
+8 -7
send_to_void.py
··· 11 11 """Send a message to void and stream the response.""" 12 12 load_dotenv() 13 13 14 - # Create Letta client 14 + # Create Letta client (v1.0: token → api_key) 15 15 client = Letta( 16 16 base_url=os.getenv("LETTA_BASE_URL", "http://localhost:8283"), 17 - token=os.getenv("LETTA_API_KEY") 17 + api_key=os.getenv("LETTA_API_KEY") 18 18 ) 19 19 20 - # Get the void agent 21 - agents = client.agents.list() 20 + # Get the void agent (v1.0: list returns page object) 21 + agents_page = client.agents.list() 22 + agents = agents_page.items if hasattr(agents_page, 'items') else agents_page 22 23 void_agent = next((a for a in agents if a.name == "void"), None) 23 24 24 25 if not void_agent: ··· 30 31 31 32 # Send message and stream response 32 33 try: 33 - # Use the streaming interface 34 - message_stream = client.agents.messages.create_stream( 34 + # Use the streaming interface (v1.0: create_stream → stream) 35 + message_stream = client.agents.messages.stream( 35 36 agent_id=void_agent.id, 36 37 messages=[{"role": "user", "content": message}], 37 38 stream_tokens=False, # Step streaming only ··· 87 88 send_message_to_void(message) 88 89 89 90 if __name__ == "__main__": 90 - main() 91 + main()
+16 -10
show_agent_capabilities.py
··· 9 9 def show_agent_capabilities(): 10 10 """Display the capabilities of both agents.""" 11 11 12 - client = Letta(token=os.environ["LETTA_API_KEY"]) 12 + client = Letta(api_key=os.environ["LETTA_API_KEY"]) # v1.0: token → api_key 13 13 14 14 print("🤖 LETTA AGENT CAPABILITIES") 15 15 print("=" * 50) 16 16 17 - # Profile Researcher Agent 18 - researchers = client.agents.list(name="profile-researcher") 17 + # Profile Researcher Agent (v1.0: list returns page object) 18 + researchers_page = client.agents.list(name="profile-researcher") 19 + researchers = researchers_page.items if hasattr(researchers_page, 'items') else researchers_page 19 20 if researchers: 20 21 researcher = researchers[0] 21 22 print(f"\n📊 PROFILE RESEARCHER AGENT") 22 23 print(f" ID: {researcher.id}") 23 24 print(f" Name: {researcher.name}") 24 25 25 - researcher_tools = client.agents.tools.list(agent_id=researcher.id) 26 + researcher_tools_page = client.agents.tools.list(agent_id=researcher.id) 27 + researcher_tools = researcher_tools_page.items if hasattr(researcher_tools_page, 'items') else researcher_tools_page 26 28 print(f" Tools ({len(researcher_tools)}):") 27 29 for tool in researcher_tools: 28 30 print(f" - {tool.name}") 29 31 30 - researcher_blocks = client.agents.blocks.list(agent_id=researcher.id) 32 + researcher_blocks_page = client.agents.blocks.list(agent_id=researcher.id) 33 + researcher_blocks = researcher_blocks_page.items if hasattr(researcher_blocks_page, 'items') else researcher_blocks_page 31 34 print(f" Memory Blocks ({len(researcher_blocks)}):") 32 35 for block in researcher_blocks: 33 36 print(f" - {block.label}") 34 37 35 - # Void Agent 36 - voids = client.agents.list(name="void") 38 + # Void Agent (v1.0: list returns page object) 39 + voids_page = client.agents.list(name="void") 40 + voids = voids_page.items if hasattr(voids_page, 'items') else voids_page 37 41 if voids: 38 42 void = voids[0] 39 43 print(f"\n🌌 VOID AGENT") 40 44 print(f" ID: {void.id}") 41 45 print(f" Name: {void.name}") 42 46 43 - void_tools = client.agents.tools.list(agent_id=void.id) 47 + void_tools_page = client.agents.tools.list(agent_id=void.id) 48 + void_tools = void_tools_page.items if hasattr(void_tools_page, 'items') else void_tools_page 44 49 print(f" Tools ({len(void_tools)}):") 45 50 for tool in void_tools: 46 51 print(f" - {tool.name}") 47 52 48 - void_blocks = client.agents.blocks.list(agent_id=void.id) 53 + void_blocks_page = client.agents.blocks.list(agent_id=void.id) 54 + void_blocks = void_blocks_page.items if hasattr(void_blocks_page, 'items') else void_blocks_page 49 55 print(f" Memory Blocks ({len(void_blocks)}):") 50 56 for block in void_blocks: 51 57 print(f" - {block.label}") ··· 60 66 print(f" Void Agent: 'Attach user block for cameron.pfiffer.org before responding'") 61 67 62 68 if __name__ == "__main__": 63 - show_agent_capabilities() 69 + show_agent_capabilities()
+6 -4
tool_manager.py
··· 37 37 'halt_activity', 38 38 'ignore_notification', 39 39 'annotate_ack', 40 - 'create_whitewind_blog_post', 40 + 'blog_post_create', 41 41 'fetch_webpage', 42 42 } 43 43 ··· 72 72 73 73 try: 74 74 # Initialize Letta client with proper base_url for self-hosted servers 75 - client_params = {'token': api_key} 75 + client_params = {'api_key': api_key} # v1.0: token → api_key 76 76 if letta_config.get('base_url'): 77 77 client_params['base_url'] = letta_config['base_url'] 78 78 client = Letta(**client_params) ··· 159 159 160 160 try: 161 161 # Initialize Letta client with proper base_url for self-hosted servers 162 - client_params = {'token': api_key} 162 + client_params = {'api_key': api_key} # v1.0: token → api_key 163 163 if letta_config.get('base_url'): 164 164 client_params['base_url'] = letta_config['base_url'] 165 165 client = Letta(**client_params) 166 166 agent = client.agents.retrieve(agent_id=agent_id) 167 - current_tools = client.agents.tools.list(agent_id=str(agent.id)) 167 + # v1.0: list returns page object 168 + current_tools_page = client.agents.tools.list(agent_id=str(agent.id)) 169 + current_tools = current_tools_page.items if hasattr(current_tools_page, 'items') else current_tools_page 168 170 return {tool.name for tool in current_tools} 169 171 except Exception as e: 170 172 logger.error(f"Error getting attached tools: {e}")
+3 -3
tools/__init__.py
··· 3 3 from .search import search_bluesky_posts, SearchArgs 4 4 from .post import create_new_bluesky_post, PostArgs 5 5 from .feed import get_bluesky_feed, FeedArgs 6 - from .whitewind import create_whitewind_blog_post, WhitewindPostArgs 6 + from .whitewind import blog_post_create, BlogPostCreateArgs 7 7 from .ack import annotate_ack, AnnotateAckArgs 8 8 9 9 __all__ = [ ··· 11 11 "search_bluesky_posts", 12 12 "create_new_bluesky_post", 13 13 "get_bluesky_feed", 14 - "create_whitewind_blog_post", 14 + "blog_post_create", 15 15 "annotate_ack", 16 16 # Pydantic models 17 17 "SearchArgs", 18 18 "PostArgs", 19 19 "FeedArgs", 20 - "WhitewindPostArgs", 20 + "BlogPostCreateArgs", 21 21 "AnnotateAckArgs", 22 22 ]
+4 -2
tools/bot_detection.py
··· 31 31 32 32 try: 33 33 # Create Letta client inline (for cloud execution) 34 - client = Letta(token=os.environ["LETTA_API_KEY"]) 34 + client = Letta(api_key=os.environ["LETTA_API_KEY"]) # v1.0: token → api_key 35 35 36 36 # Get all blocks attached to the agent to check if known_bots is mounted 37 - attached_blocks = client.agents.blocks.list(agent_id=str(agent_state.id)) 37 + # v1.0: list returns page object 38 + attached_blocks_page = client.agents.blocks.list(agent_id=str(agent_state.id)) 39 + attached_blocks = attached_blocks_page.items if hasattr(attached_blocks_page, 'items') else attached_blocks_page 38 40 attached_labels = {block.label for block in attached_blocks} 39 41 40 42 if "known_bots" not in attached_labels:
+19 -13
tools/whitewind.py
··· 1 - """Whitewind blog post creation tool.""" 1 + """Greengale blog post creation tool. 2 + 3 + Creates blog posts on Greengale (https://greengale.app), an ATProto-based blogging service. 4 + Posts are created using the app.greengale.blog.entry lexicon and served at greengale.app. 5 + """ 2 6 from typing import Optional 3 7 from pydantic import BaseModel, Field 4 8 5 9 6 - class WhitewindPostArgs(BaseModel): 10 + class BlogPostCreateArgs(BaseModel): 7 11 title: str = Field( 8 12 ..., 9 13 description="Title of the blog post" ··· 18 22 ) 19 23 20 24 21 - def create_whitewind_blog_post(title: str, content: str, subtitle: Optional[str] = None) -> str: 25 + def blog_post_create(title: str, content: str, subtitle: Optional[str] = None) -> str: 22 26 """ 23 - Create a new blog post on Whitewind. 27 + Create a new blog post on Greengale. 24 28 25 - This tool creates blog posts using the com.whtwnd.blog.entry lexicon on the ATProto network. 26 - The posts are publicly visible and use the github-light theme. 29 + This tool creates blog posts using the app.greengale.blog.entry lexicon on the ATProto network. 30 + Blog posts are publicly visible and use the github-light theme. 31 + 32 + The blog post will be served at: https://greengale.app/{handle}/{post_id} 27 33 28 34 Args: 29 35 title: Title of the blog post ··· 31 37 subtitle: Optional subtitle for the blog post 32 38 33 39 Returns: 34 - Success message with the blog post URL 40 + Success message with the blog post URL on greengale.app 35 41 36 42 Raises: 37 43 Exception: If the post creation fails ··· 70 76 now = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z") 71 77 72 78 blog_record = { 73 - "$type": "com.whtwnd.blog.entry", 79 + "$type": "app.greengale.blog.entry", 74 80 "theme": "github-light", 75 81 "title": title, 76 82 "content": content, ··· 88 94 89 95 create_data = { 90 96 "repo": user_did, 91 - "collection": "com.whtwnd.blog.entry", 97 + "collection": "app.greengale.blog.entry", 92 98 "record": blog_record 93 99 } 94 100 ··· 100 106 post_uri = result.get("uri") 101 107 if post_uri: 102 108 rkey = post_uri.split("/")[-1] 103 - # Construct the Whitewind blog URL 104 - blog_url = f"https://whtwnd.com/{handle}/{rkey}" 109 + # Construct the Greengale blog URL 110 + blog_url = f"https://greengale.app/{handle}/{rkey}" 105 111 else: 106 112 blog_url = "URL generation failed" 107 113 108 114 # Build success message 109 115 success_parts = [ 110 - f"Successfully created Whitewind blog post!", 116 + f"Successfully created blog post on Greengale!", 111 117 f"Title: {title}" 112 118 ] 113 119 if subtitle: ··· 121 127 return "\n".join(success_parts) 122 128 123 129 except Exception as e: 124 - raise Exception(f"Error creating Whitewind blog post: {str(e)}") 130 + raise Exception(f"Error creating blog post: {str(e)}")
+11 -21
utils.py
··· 6 6 Ensures that a block by this label exists. If the block exists, it will 7 7 replace content provided by kwargs with the values in this function call. 8 8 """ 9 - # Get the list of blocks 10 - blocks = letta.blocks.list(label=label) 9 + # Get the list of blocks (v1.0: list returns page object) 10 + blocks_page = letta.blocks.list(label=label) 11 + blocks = blocks_page.items if hasattr(blocks_page, 'items') else blocks_page 11 12 12 13 # Check if we had any -- if not, create it 13 14 if len(blocks) == 0: ··· 27 28 existing_block = blocks[0] 28 29 29 30 if kwargs.get('update', False): 30 - # Remove 'update' from kwargs before passing to modify 31 + # Remove 'update' from kwargs before passing to update 31 32 kwargs_copy = kwargs.copy() 32 33 kwargs_copy.pop('update', None) 33 34 34 - updated_block = letta.blocks.modify( 35 + updated_block = letta.blocks.update( 35 36 block_id = existing_block.id, 36 37 label = label, 37 38 value = value, ··· 47 48 Ensures that an agent by this label exists. If the agent exists, it will 48 49 update the agent to match kwargs. 49 50 """ 50 - # Get the list of agents 51 - agents = letta.agents.list(name=name) 51 + # Get the list of agents (v1.0: list returns page object) 52 + agents_page = letta.agents.list(name=name) 53 + agents = agents_page.items if hasattr(agents_page, 'items') else agents_page 52 54 53 55 # Check if we had any -- if not, create it 54 56 if len(agents) == 0: ··· 61 63 return new_agent 62 64 63 65 if len(agents) > 1: 64 - raise Exception(f"{len(agents)} agents by the label '{label}' retrieved, label must identify a unique agent") 66 + raise Exception(f"{len(agents)} agents by the name '{name}' retrieved, name must identify a unique agent") 65 67 66 68 else: 67 69 existing_agent = agents[0] 68 70 69 71 if kwargs.get('update', False): 70 - # Remove 'update' from kwargs before passing to modify 72 + # Remove 'update' from kwargs before passing to update 71 73 kwargs_copy = kwargs.copy() 72 74 kwargs_copy.pop('update', None) 73 75 74 - updated_agent = letta.agents.modify( 76 + updated_agent = letta.agents.update( 75 77 agent_id = existing_agent.id, 76 78 **kwargs_copy 77 79 ) ··· 79 81 return updated_agent 80 82 else: 81 83 return existing_agent 82 - 83 - 84 - 85 - 86 - 87 - 88 - 89 - 90 - 91 - 92 - 93 -
+4 -4
x.py
··· 1280 1280 rprint(Panel(prompt, title=f"Prompt ({prompt_char_count} chars)", border_style="blue")) 1281 1281 1282 1282 # Send to Letta agent using streaming 1283 - message_stream = letta_client.agents.messages.create_stream( 1283 + message_stream = letta_client.agents.messages.stream( 1284 1284 agent_id=agent_id, 1285 1285 messages=[{"role": "user", "content": prompt}], 1286 1286 stream_tokens=False, ··· 1590 1590 from letta_client import Letta 1591 1591 1592 1592 config = get_x_letta_config() 1593 - letta_client = Letta(token=config['api_key'], timeout=config['timeout']) 1593 + letta_client = Letta(api_key=config['api_key'], timeout=config['timeout']) # v1.0: token → api_key 1594 1594 1595 1595 prompt_char_count = len(prompt) 1596 1596 logger.debug(f"Sending to LLM: @{author_username} mention | msg: \"{mention_text[:50]}...\" | context: {len(thread_context)} chars | prompt: {prompt_char_count} chars") 1597 1597 1598 1598 try: 1599 1599 # Use streaming to avoid timeout errors 1600 - message_stream = letta_client.agents.messages.create_stream( 1600 + message_stream = letta_client.agents.messages.stream( 1601 1601 agent_id=void_agent.id, 1602 1602 messages=[{"role": "user", "content": prompt}], 1603 1603 stream_tokens=False, ··· 2055 2055 2056 2056 # Get Letta client for periodic cleanup 2057 2057 config = get_x_letta_config() 2058 - letta_client = Letta(token=config['api_key'], timeout=config['timeout']) 2058 + letta_client = Letta(api_key=config['api_key'], timeout=config['timeout']) # v1.0: token → api_key 2059 2059 2060 2060 # Main loop 2061 2061 FETCH_DELAY_SEC = 120 # Check every 2 minutes for X mentions (reduced from 60s to conserve API calls)