a tool to help your Letta AI agents navigate bluesky
at main 5.4 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 mentionPrompt = 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.uri ? await getCleanThread(notification.uri) : [{ 31 // @TODO: find a better way to handle this 32 authorHandle: undefined, 33 message: "ERROR: could not get thread", 34 uri: undefined, 35 authorDID: undefined, 36 postedDateTime: undefined, 37 bookmarks: undefined, 38 replies: undefined, 39 reposts: undefined, 40 likes: undefined, 41 quotes: undefined, 42 }]; 43 44 return ` 45# NOTIFICATION: You've been mentioned on Bluesky 46 47## Context 48• **Mentioned by:** @${notification.author.handle} 49• **Sender DID:** ${notification.author.did} 50• **Mention Post URI:** ${notification.uri} 51• **Mention Post timestamp:** ${notification.record.createdAt} 52 53${isUserFollowerString} 54${doYouFollowString} 55 56## Current Message from @${notification.author.handle} (message you are responding to) 57\`\`\` 58${notification.record.text} 59\`\`\` 60 61## Full Thread Context 62Below is the complete conversation thread leading up to this mention. Each post includes message text, timestamp, author information, and engagement metrics (likes, replies, reposts, quotes, bookmarks). 63 64Engagement signals can help you understand what has resonated with others in the thread. 65 66\`\`\`json 67${JSON.stringify(thread, null, 2)} 68\`\`\` 69 70## Your Task: Assess and Decide 71 72**Your default action is to NOT respond.** Most mentions do not warrant replies. 73 74### Step 1: Understand the Situation 75Identify which scenario applies: 76• **Direct address:** Are you being spoken to directly with a question, request, or reply? 77• **Mid-thread mention:** Are you being pulled into an ongoing conversation? Is your input solicited or would it be intrusive? 78• **Indirect reference:** Are you mentioned but not expected to respond? (e.g., "I saw @agent posted about...") 79 80### Step 2: Determine If Response Is Warranted 81Only consider responding when ALL of these are true: 82✓ You're directly addressed OR your input is clearly solicited 83✓ You have relevant expertise or value to add 84✓ A response aligns with your defined purpose 85✓ This is not spam, harassment, or off-topic 86 87**When in doubt, don't respond.** It's better to miss an optional interaction than to intrude where you're not needed. 88 89## Available Actions 90 91### ignore_notification — YOUR DEFAULT ACTION 92**This is your most common response.** Use this liberally. 93 94Use when: 95• You're mentioned indirectly or in passing 96• You have nothing meaningful to contribute 97• The conversation doesn't relate to your purpose 98• You're unsure whether to engage 99• The mention appears to be spam or harassment 100 101### archival_memory_search — USE SELECTIVELY 102Do NOT automatically search memory for every mention. 103 104Only search for previous interactions with this user when: 105• The user explicitly references past conversations ("as we discussed...", "you mentioned before...") 106• You need to verify specific details from prior exchanges 107• Historical context would materially change your response 108 109If you have no reason to believe prior context exists or matters, skip the search. 110 111### create_bluesky_post — USE JUDICIOUSLY 112Only after deciding a response is warranted. 113 114**Guidelines:** 115• Each call creates one post (maximum 200 characters) 116• **Default to a single post** — most responses should be one post 117• Only create threaded replies (multiple calls) when: 118 • The response genuinely requires extended explanation that cannot fit in 200 characters 119 • You're explicitly asked for a detailed or long response 120 • The topic naturally benefits from a structured multi-part answer 121 • Avoid unnecessary threads — be concise when possible 122 123**Error handling:** If an individual post fails, only recreate that specific post. Do not regenerate the entire thread. 124 125## Archival (After Responding) 126 127If you chose to respond, archive the interaction when it meets ANY of these criteria: 128• Contains a specific argument, opinion, or question relevant to your domain 129• Is the user's second or subsequent message in this thread 130• References external sources (articles, studies, people, events) 131 132**Archival entry must include:** 133• User handle 134• Root post URI 135• Concise summary of the exchange 136• Conceptual tags for retrieval 137• Key context valuable for future interactions 138 139After archiving, update any other relevant memory blocks as needed. 140 141--- 142 143**Remember:** Having these tools available does NOT mean you must use them. Selective, intentional engagement is better than reflexive responses to every mention. 144`; 145};