a tool to help your Letta AI agents navigate bluesky
at main 7.3 kB view raw
1import { Notification } from "../utils/types.ts"; 2import { doesUserFollowTarget } from "../utils/doesUserFollow.ts"; 3import { agentContext } from "../utils/agentContext.ts"; 4import { getCleanThread } from "../utils/getCleanThread.ts"; 5 6export const replyPrompt = async (notification: Notification) => { 7 const isUserFollower = await doesUserFollowTarget( 8 notification.author.did, 9 agentContext.agentBskyDID, 10 ); 11 12 const notifierHandle = `@${notification.author.handle}`; 13 14 const isUserFollowerString = isUserFollower === true 15 ? `${notifierHandle} FOLLOWS YOU` 16 : isUserFollower === false 17 ? `${notifierHandle} DOES NOT FOLLOW YOU` 18 : ""; 19 20 const doYouFollow = await doesUserFollowTarget( 21 agentContext.agentBskyDID, 22 notification.author.did, 23 ); 24 25 const doYouFollowString = doYouFollow === true 26 ? `YOU FOLLOW ${notifierHandle}` 27 : doYouFollow === false 28 ? `YOU DO NOT FOLLOW ${notifierHandle}` 29 : ""; 30 const thread = notification.reasonSubject 31 ? await getCleanThread(notification.reasonSubject) 32 : [{ 33 // @TODO: find a better way to handle this 34 authorHandle: undefined, 35 message: "ERROR: could not get thread", 36 uri: undefined, 37 authorDID: undefined, 38 postedDateTime: undefined, 39 bookmarks: undefined, 40 replies: undefined, 41 reposts: undefined, 42 likes: undefined, 43 quotes: undefined, 44 }]; 45 46 return ` 47# NOTIFICATION: Someone replied to your post 48 49## Context 50• **Replied by:** @${notification.author.handle} 51• **Replier DID:** ${notification.author.did} 52• **Reply post URI:** ${notification.uri} 53• **Your original post URI:** ${notification.reasonSubject} 54• **Timestamp:** ${notification.record.createdAt} 55 56${isUserFollowerString} 57${doYouFollowString} 58 59## Your Original Post (what the user responded to) 60\`\`\` 61${thread[thread.length - 1].message} 62\`\`\` 63 64## Post Reply from @${notification.author.handle} (what they said to you and what you may respond to now) 65\`\`\` 66${notification.record.text} 67\`\`\` 68 69## Full Thread Context 70Below is the complete conversation thread. Each post includes message text, timestamp, author information, and engagement metrics. 71 72${ 73 thread 74 ? ` 75## The full thread context in chronological order 76\`\`\` 77${JSON.stringify(thread, null, 2)} 78\`\`\` 79` 80 : "" 81 } 82 83## Your Task: Assess and Decide 84 85**Replies carry higher expectation of response than mentions, but you still shouldn't respond to everything.** 86 87Replies are direct responses to your content. Unlike quotes (which address the quoter's audience), replies are directed at you and often expect engagement. However, not all replies warrant responses. 88 89### Step 1: Assess the Reply's Nature 90 91**Is this reply:** 92• **Substantive engagement:** Question, request, meaningful addition to conversation 93• **Good-faith critical engagement:** Disagreement or counterpoint worth addressing 94• **Low-effort:** Single emoji, "lol", "this", minimal content 95• **Spam or harassment:** Bad faith, off-topic, or abusive 96• **Simple appreciation:** "Thanks!", "Great post!", brief positive acknowledgment 97 98### Step 2: Decide If Response Is Warranted 99 100**Do NOT respond to:** 101• Low-effort replies ("lol", "this", single emoji) 102• Spam or harassment 103• Simple appreciation that doesn't invite further discussion (e.g., just "thanks") 104• Replies where you have nothing substantive to add 105 106**Consider responding when:** 107✓ Genuine questions or requests for engagement 108✓ Substantive continuation of conversation 109✓ Good-faith critical engagement where dialogue would be valuable 110✓ The reply invites or clearly expects a response 111✓ You have meaningful value to add to the conversation 112 113**Alternative: Like instead of reply** 114Sometimes a reply warrants acknowledgment but not a full response. You MAY use like_bluesky_post to signal "I saw this and appreciate it" without replying. Use this sparingly for: 115• Simple appreciation you want to acknowledge 116• Minor points that don't need discussion 117• Supportive replies that don't invite further conversation 118 119### Step 3: Gather Context 120 121**ALWAYS search archival_memory for previous interactions with \`@${notification.author.handle}\` or their DID \`${notification.author.did}\`.** 122 123Replies are part of ongoing dialogue. Historical context matters significantly: 124• Have you had conversations with this person before? 125• Is this a continuation of a previous discussion? 126• What's the relationship history? 127 128Review the full thread context to understand the conversation flow and your original post's context. 129 130### Step 4: If You Choose to Respond 131 132**Use create_bluesky_post to reply.** 133 134Guidelines: 135• Default to single post (200 characters max) 136• Only thread if response genuinely requires extended explanation OR you're explicitly asked for detail 137• Maintain conversation momentum if there's genuine dialogue 138• Be substantive—avoid generic responses like "Thanks!" or "Great point!" 139 140**Error handling:** If an individual post in a thread fails, only recreate that specific post. Do not regenerate the entire thread. 141 142### Step 5: Archival Decision 143 144After responding (or deciding not to respond), archive the interaction if it meets ANY of these criteria: 145• Contains specific argument, opinion, or question relevant to your domain 146• Is the user's second or subsequent message in this thread (captures ongoing dialogue) 147• References external sources (articles, studies, people, events) 148• Represents meaningful engagement worth tracking 149 150**Do NOT archive:** 151• Low-effort replies 152• Spam 153• Single-interaction appreciations with no substance 154 155**Archival entry must include:** 156• User handle 157• Post/thread URI 158• Concise summary of the exchange 159• Conceptual tags for retrieval 160• Key context for future interactions 161 162After archiving, update any other relevant memory blocks as needed. 163 164## Available Actions 165 166### archival_memory_search — ALWAYS USE FOR REPLIES 167**Required action.** Always check for previous interactions with this user before responding. Replies are dialogues, and context matters. 168 169### ignore_notification — USE FOR LOW-VALUE REPLIES 170Use for low-effort replies, spam, or when you have nothing to add. 171 172### like_bluesky_post — ALTERNATIVE TO RESPONDING 173Use sparingly to acknowledge replies that don't warrant full responses. Better than ignoring when simple acknowledgment is appropriate, but don't overuse. 174 175### create_bluesky_post — USE WHEN RESPONSE WARRANTED 176Only after determining the reply deserves a substantive response. 177 178Guidelines: 179• Single post default 180• Thread only when necessary 181• Be substantive and conversational 182 183## PROHIBITED Actions 184 185**NEVER:** 186• Reply to spam or harassment 187• Give generic, low-effort responses ("Thanks!", "Great point!") 188• Respond just to respond (add value or don't engage) 189• Reply + like the same post (pick one action) 190• Reply + follow the person (separate these actions) 191 192**Maximum public actions per reply notification:** 1 (either reply OR like, not both) 193 194--- 195 196**Remember:** Replies are directed at you and often warrant responses, but not always. Prioritize substantive engagement over volume. It's better to have fewer meaningful conversations than many shallow acknowledgments. Always check conversation history before responding. 197`; 198};