a digital entity named phi that roams bsky phi.zzstoatzz.io
2
fork

Configure Feed

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

add get_own_posts tool, warn against list_records pagination

phi kept looping on list_records (paginating its own posts via pdsx MCP),
exhausting the 50-request limit with no response. get_own_posts wraps
BotClient.get_own_posts() in a single call. operational instructions now
explicitly warn against repeated list_records pagination.

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

zzstoatzz f8d1120b 30b07e50

+30 -1
+1 -1
loq.toml
··· 13 13 14 14 [[rules]] 15 15 path = "src/bot/agent.py" 16 - max_lines = 826 16 + max_lines = 854 17 17 18 18 [[rules]] 19 19 path = "src/bot/memory/namespace_memory.py"
+28
src/bot/agent.py
··· 69 69 - create_feed: build a custom feed from keyword patterns and hashtag filters. OWNER-ONLY (restricted to @{settings.owner_handle}). 70 70 - list_feeds: see your existing graze-powered feeds. 71 71 - follow_user: follow a user on bluesky. OWNER-ONLY (restricted to @{settings.owner_handle}). 72 + 73 + your own posts: 74 + - get_own_posts: read your own recent top-level posts. use this when you need to review what you've posted — do NOT use list_records for your own posts. 75 + 76 + IMPORTANT: never paginate through list_records repeatedly. if you need more data than one call returns, work with what you have. endless pagination wastes your request budget and produces no response. 72 77 """.strip() 73 78 74 79 ··· 638 643 return f"now following @{handle} ({uri})" 639 644 except Exception as e: 640 645 return f"failed to follow @{handle}: {e}" 646 + 647 + @self.agent.tool 648 + async def get_own_posts(ctx: RunContext[PhiDeps], limit: int = 10) -> str: 649 + """Read your own recent top-level posts (no replies). Use this instead of list_records when you need to review what you've posted.""" 650 + try: 651 + posts = await bot_client.get_own_posts(limit=limit) 652 + if not posts: 653 + return "no posts found" 654 + today = date.today() 655 + lines = [] 656 + for item in posts: 657 + post = item.post 658 + text = post.record.text if hasattr(post.record, "text") else "" 659 + age = ( 660 + _relative_age(post.indexed_at, today) 661 + if hasattr(post, "indexed_at") and post.indexed_at 662 + else "" 663 + ) 664 + age_str = f" ({age})" if age else "" 665 + lines.append(f"[{post.uri}]{age_str}: {text[:200]}") 666 + return "\n\n".join(lines) 667 + except Exception as e: 668 + return f"failed to get own posts: {e}" 641 669 642 670 logger.info("phi agent initialized with pdsx + pub-search mcp tools") 643 671
+1
tests/test_tool_usage.py
··· 144 144 assert "read_timeline" in tool_names, f"read_timeline not in {tool_names}" 145 145 assert "read_feed" in tool_names, f"read_feed not in {tool_names}" 146 146 assert "follow_user" in tool_names, f"follow_user not in {tool_names}" 147 + assert "get_own_posts" in tool_names, f"get_own_posts not in {tool_names}" 147 148 148 149 def test_graze_client_instantiated(self): 149 150 if not os.environ.get("ANTHROPIC_API_KEY"):