create-message-kit 1.1.5-beta.1 → 1.1.5-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
 - package/index.js +13 -17
 - package/package.json +1 -1
 - package/templates/agent/src/handler/ens.ts +9 -4
 - package/templates/agent/src/index.ts +3 -3
 - package/templates/agent/src/lib/gpt.ts +161 -0
 - package/templates/agent/src/lib/openai.ts +3 -3
 - package/templates/agent/src/lib/resolver.ts +20 -5
 - package/templates/agent/src/prompt.ts +2 -2
 - package/templates/agent/src/skills.ts +13 -0
 - package/templates/gm/src/index.ts +13 -6
 - package/templates/group/src/handler/agent.ts +4 -3
 - package/templates/group/src/handler/game.ts +1 -1
 - package/templates/group/src/handler/splitpayment.ts +3 -3
 - package/templates/group/src/handler/tipping.ts +1 -3
 - package/templates/group/src/handler/transaction.ts +0 -1
 - package/templates/group/src/index.ts +1 -1
 - package/templates/group/src/lib/gpt.ts +161 -0
 - package/templates/group/src/lib/openai.ts +3 -3
 - package/templates/group/src/lib/resolver.ts +20 -5
 - package/templates/group/src/lib/vision.ts +1 -2
 
    
        package/README.md
    CHANGED
    
    
    
        package/index.js
    CHANGED
    
    | 
         @@ -9,11 +9,16 @@ import pc from "picocolors"; 
     | 
|
| 
       9 
9 
     | 
    
         | 
| 
       10 
10 
     | 
    
         
             
            const __dirname = dirname(fileURLToPath(import.meta.url));
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
      
 12 
     | 
    
         
            +
            // Read package.json to get the version
         
     | 
| 
      
 13 
     | 
    
         
            +
            const packageJson = JSON.parse(
         
     | 
| 
      
 14 
     | 
    
         
            +
              fs.readFileSync(resolve(__dirname, "package.json"), "utf8"),
         
     | 
| 
      
 15 
     | 
    
         
            +
            );
         
     | 
| 
      
 16 
     | 
    
         
            +
            const version = packageJson.version;
         
     | 
| 
       12 
17 
     | 
    
         
             
            program
         
     | 
| 
       13 
18 
     | 
    
         
             
              .name("byob")
         
     | 
| 
       14 
19 
     | 
    
         
             
              .description("CLI to initialize projects")
         
     | 
| 
       15 
20 
     | 
    
         
             
              .action(async () => {
         
     | 
| 
       16 
     | 
    
         
            -
                log.info(pc.cyan(`Welcome to MessageKit!`));
         
     | 
| 
      
 21 
     | 
    
         
            +
                log.info(pc.cyan(`Welcome to MessageKit CLI v${version}!`));
         
     | 
| 
       17 
22 
     | 
    
         
             
                const coolLogo = `
         
     | 
| 
       18 
23 
     | 
    
         
             
            ███╗   ███╗███████╗███████╗███████╗ █████╗  ██████╗ ███████╗██╗  ██╗██╗████████╗
         
     | 
| 
       19 
24 
     | 
    
         
             
            ████╗ ████║██╔════╝██╔════╝██╔════╝██╔══██╗██╔════╝ ██╔════╝██║ ██╔╝██║╚══██╔══╝
         
     | 
| 
         @@ -48,7 +53,7 @@ Powered by XMTP`; 
     | 
|
| 
       48 
53 
     | 
    
         
             
                const pkgManager = detectPackageManager();
         
     | 
| 
       49 
54 
     | 
    
         | 
| 
       50 
55 
     | 
    
         
             
                // Create README.md file
         
     | 
| 
       51 
     | 
    
         
            -
                createReadme(destDir, displayName, pkgManager);
         
     | 
| 
      
 56 
     | 
    
         
            +
                createReadme(destDir, templateType, displayName, pkgManager);
         
     | 
| 
       52 
57 
     | 
    
         | 
| 
       53 
58 
     | 
    
         
             
                // Log next steps
         
     | 
| 
       54 
59 
     | 
    
         
             
                logNextSteps(displayName);
         
     | 
| 
         @@ -157,21 +162,12 @@ function replaceDotfiles(destDir) { 
     | 
|
| 
       157 
162 
     | 
    
         
             
            }
         
     | 
| 
       158 
163 
     | 
    
         | 
| 
       159 
164 
     | 
    
         
             
            function updatePackageJson(destDir, name) {
         
     | 
| 
       160 
     | 
    
         
            -
               
     | 
| 
       161 
     | 
    
         
            -
             
     | 
| 
       162 
     | 
    
         
            -
             
     | 
| 
       163 
     | 
    
         
            -
             
     | 
| 
       164 
     | 
    
         
            -
                  return;
         
     | 
| 
       165 
     | 
    
         
            -
                }
         
     | 
| 
       166 
     | 
    
         
            -
             
     | 
| 
       167 
     | 
    
         
            -
                const pkgJson = fs.readJsonSync(pkgJsonPath);
         
     | 
| 
       168 
     | 
    
         
            -
                pkgJson.name = name;
         
     | 
| 
       169 
     | 
    
         
            -
                updateDependenciesToLatest(pkgJson);
         
     | 
| 
       170 
     | 
    
         
            -
                fs.writeJsonSync(pkgJsonPath, pkgJson, { spaces: 2 });
         
     | 
| 
       171 
     | 
    
         
            -
              } catch (error) {
         
     | 
| 
       172 
     | 
    
         
            -
                log.error(`Error updating package.json: ${error.message}`);
         
     | 
| 
       173 
     | 
    
         
            -
              }
         
     | 
| 
      
 165 
     | 
    
         
            +
              const pkgJson = fs.readJsonSync(resolve(destDir, "package.json"));
         
     | 
| 
      
 166 
     | 
    
         
            +
              pkgJson.name = name;
         
     | 
| 
      
 167 
     | 
    
         
            +
              updateDependenciesToLatest(pkgJson);
         
     | 
| 
      
 168 
     | 
    
         
            +
              fs.writeJsonSync(resolve(destDir, "package.json"), pkgJson, { spaces: 2 });
         
     | 
| 
       174 
169 
     | 
    
         
             
            }
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
       175 
171 
     | 
    
         
             
            function logNextSteps(name) {
         
     | 
| 
       176 
172 
     | 
    
         
             
              log.message("Next steps:");
         
     | 
| 
       177 
173 
     | 
    
         
             
              log.step(`1. ${pc.red(`cd ./${name}`)} - Navigate to project`);
         
     | 
| 
         @@ -229,7 +225,7 @@ function createEnvFile(destDir) { 
     | 
|
| 
       229 
225 
     | 
    
         
             
            }
         
     | 
| 
       230 
226 
     | 
    
         | 
| 
       231 
227 
     | 
    
         
             
            // Create README.md file
         
     | 
| 
       232 
     | 
    
         
            -
            function createReadme(destDir, projectName, packageManager) {
         
     | 
| 
      
 228 
     | 
    
         
            +
            function createReadme(destDir, templateType, projectName, packageManager) {
         
     | 
| 
       233 
229 
     | 
    
         
             
              const envExampleContent = fs.readFileSync(
         
     | 
| 
       234 
230 
     | 
    
         
             
                resolve(destDir, ".env.example"),
         
     | 
| 
       235 
231 
     | 
    
         
             
                "utf8",
         
     | 
    
        package/package.json
    CHANGED
    
    
| 
         @@ -1,17 +1,20 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            import { HandlerContext } from "@xmtp/message-kit";
         
     | 
| 
      
 1 
     | 
    
         
            +
            import { HandlerContext, SkillResponse } from "@xmtp/message-kit";
         
     | 
| 
       2 
2 
     | 
    
         
             
            import { getUserInfo, clearInfoCache, isOnXMTP } from "../lib/resolver.js";
         
     | 
| 
       3 
3 
     | 
    
         
             
            import { isAddress } from "viem";
         
     | 
| 
       4 
     | 
    
         
            -
            import { clearMemory } from "../lib/ 
     | 
| 
      
 4 
     | 
    
         
            +
            import { clearMemory } from "../lib/gpt.js";
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
6 
     | 
    
         
             
            export const frameUrl = "https://ens.steer.fun/";
         
     | 
| 
       7 
7 
     | 
    
         
             
            export const ensUrl = "https://app.ens.domains/";
         
     | 
| 
       8 
8 
     | 
    
         
             
            export const baseTxUrl = "https://base-tx-frame.vercel.app";
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
            export async function handleEns( 
     | 
| 
      
 10 
     | 
    
         
            +
            export async function handleEns(
         
     | 
| 
      
 11 
     | 
    
         
            +
              context: HandlerContext,
         
     | 
| 
      
 12 
     | 
    
         
            +
            ): Promise<SkillResponse> {
         
     | 
| 
       11 
13 
     | 
    
         
             
              const {
         
     | 
| 
       12 
14 
     | 
    
         
             
                message: {
         
     | 
| 
       13 
15 
     | 
    
         
             
                  content: { command, params, sender },
         
     | 
| 
       14 
16 
     | 
    
         
             
                },
         
     | 
| 
      
 17 
     | 
    
         
            +
                skill,
         
     | 
| 
       15 
18 
     | 
    
         
             
              } = context;
         
     | 
| 
       16 
19 
     | 
    
         
             
              if (command == "reset") {
         
     | 
| 
       17 
20 
     | 
    
         
             
                clearMemory();
         
     | 
| 
         @@ -115,7 +118,7 @@ export async function handleEns(context: HandlerContext) { 
     | 
|
| 
       115 
118 
     | 
    
         
             
                  };
         
     | 
| 
       116 
119 
     | 
    
         
             
                } else {
         
     | 
| 
       117 
120 
     | 
    
         
             
                  let message = `Looks like ${domain} is already registered!`;
         
     | 
| 
       118 
     | 
    
         
            -
                  await  
     | 
| 
      
 121 
     | 
    
         
            +
                  await skill("/cool " + domain);
         
     | 
| 
       119 
122 
     | 
    
         
             
                  return {
         
     | 
| 
       120 
123 
     | 
    
         
             
                    code: 404,
         
     | 
| 
       121 
124 
     | 
    
         
             
                    message,
         
     | 
| 
         @@ -145,6 +148,8 @@ export async function handleEns(context: HandlerContext) { 
     | 
|
| 
       145 
148 
     | 
    
         
             
                  code: 200,
         
     | 
| 
       146 
149 
     | 
    
         
             
                  message: `${generateCoolAlternatives(domain)}`,
         
     | 
| 
       147 
150 
     | 
    
         
             
                };
         
     | 
| 
      
 151 
     | 
    
         
            +
              } else {
         
     | 
| 
      
 152 
     | 
    
         
            +
                return { code: 400, message: "Command not found." };
         
     | 
| 
       148 
153 
     | 
    
         
             
              }
         
     | 
| 
       149 
154 
     | 
    
         
             
            }
         
     | 
| 
       150 
155 
     | 
    
         | 
| 
         @@ -1,10 +1,10 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            import { run, HandlerContext } from "@xmtp/message-kit";
         
     | 
| 
       2 
     | 
    
         
            -
            import { textGeneration,  
     | 
| 
      
 2 
     | 
    
         
            +
            import { textGeneration, processMultilineResponse } from "./lib/gpt.js";
         
     | 
| 
       3 
3 
     | 
    
         
             
            import { agent_prompt } from "./prompt.js";
         
     | 
| 
       4 
4 
     | 
    
         
             
            import { getUserInfo } from "./lib/resolver.js";
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
6 
     | 
    
         
             
            run(async (context: HandlerContext) => {
         
     | 
| 
       7 
     | 
    
         
            -
              /*All the  
     | 
| 
      
 7 
     | 
    
         
            +
              /*All the skills are handled through the skills file*/
         
     | 
| 
       8 
8 
     | 
    
         
             
              /* If its just text, it will be handled by the ensAgent*/
         
     | 
| 
       9 
9 
     | 
    
         
             
              /* If its a group message, it will be handled by the groupAgent*/
         
     | 
| 
       10 
10 
     | 
    
         
             
              if (!process?.env?.OPEN_AI_API_KEY) {
         
     | 
| 
         @@ -31,7 +31,7 @@ run(async (context: HandlerContext) => { 
     | 
|
| 
       31 
31 
     | 
    
         
             
                  userPrompt,
         
     | 
| 
       32 
32 
     | 
    
         
             
                  await agent_prompt(userInfo),
         
     | 
| 
       33 
33 
     | 
    
         
             
                );
         
     | 
| 
       34 
     | 
    
         
            -
                await  
     | 
| 
      
 34 
     | 
    
         
            +
                await processMultilineResponse(sender.address, reply, context);
         
     | 
| 
       35 
35 
     | 
    
         
             
              } catch (error) {
         
     | 
| 
       36 
36 
     | 
    
         
             
                console.error("Error during OpenAI call:", error);
         
     | 
| 
       37 
37 
     | 
    
         
             
                await context.send("An error occurred while processing your request.");
         
     | 
| 
         @@ -0,0 +1,161 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import "dotenv/config";
         
     | 
| 
      
 2 
     | 
    
         
            +
            import type { SkillGroup } from "@xmtp/message-kit";
         
     | 
| 
      
 3 
     | 
    
         
            +
            import OpenAI from "openai";
         
     | 
| 
      
 4 
     | 
    
         
            +
            const openai = new OpenAI({
         
     | 
| 
      
 5 
     | 
    
         
            +
              apiKey: process.env.OPEN_AI_API_KEY,
         
     | 
| 
      
 6 
     | 
    
         
            +
            });
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            type ChatHistoryEntry = { role: string; content: string };
         
     | 
| 
      
 9 
     | 
    
         
            +
            type ChatHistories = Record<string, ChatHistoryEntry[]>;
         
     | 
| 
      
 10 
     | 
    
         
            +
            // New ChatMemory class
         
     | 
| 
      
 11 
     | 
    
         
            +
            class ChatMemory {
         
     | 
| 
      
 12 
     | 
    
         
            +
              private histories: ChatHistories = {};
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              getHistory(address: string): ChatHistoryEntry[] {
         
     | 
| 
      
 15 
     | 
    
         
            +
                return this.histories[address] || [];
         
     | 
| 
      
 16 
     | 
    
         
            +
              }
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              addEntry(address: string, entry: ChatHistoryEntry) {
         
     | 
| 
      
 19 
     | 
    
         
            +
                if (!this.histories[address]) {
         
     | 
| 
      
 20 
     | 
    
         
            +
                  this.histories[address] = [];
         
     | 
| 
      
 21 
     | 
    
         
            +
                }
         
     | 
| 
      
 22 
     | 
    
         
            +
                this.histories[address].push(entry);
         
     | 
| 
      
 23 
     | 
    
         
            +
              }
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
              initializeWithSystem(address: string, systemPrompt: string) {
         
     | 
| 
      
 26 
     | 
    
         
            +
                if (this.getHistory(address).length === 0) {
         
     | 
| 
      
 27 
     | 
    
         
            +
                  this.addEntry(address, {
         
     | 
| 
      
 28 
     | 
    
         
            +
                    role: "system",
         
     | 
| 
      
 29 
     | 
    
         
            +
                    content: systemPrompt,
         
     | 
| 
      
 30 
     | 
    
         
            +
                  });
         
     | 
| 
      
 31 
     | 
    
         
            +
                }
         
     | 
| 
      
 32 
     | 
    
         
            +
              }
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
              clear() {
         
     | 
| 
      
 35 
     | 
    
         
            +
                this.histories = {};
         
     | 
| 
      
 36 
     | 
    
         
            +
              }
         
     | 
| 
      
 37 
     | 
    
         
            +
            }
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
            // Create singleton instance
         
     | 
| 
      
 40 
     | 
    
         
            +
            export const chatMemory = new ChatMemory();
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
            export const clearMemory = () => {
         
     | 
| 
      
 43 
     | 
    
         
            +
              chatMemory.clear();
         
     | 
| 
      
 44 
     | 
    
         
            +
            };
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
            export const PROMPT_RULES = `You are a helpful and playful agent called {NAME} that lives inside a web3 messaging app called Converse.
         
     | 
| 
      
 47 
     | 
    
         
            +
            - You can respond with multiple messages if needed. Each message should be separated by a newline character.
         
     | 
| 
      
 48 
     | 
    
         
            +
            - You can trigger skills by only sending the command in a newline message.
         
     | 
| 
      
 49 
     | 
    
         
            +
            - Never announce actions without using a command separated by a newline character.
         
     | 
| 
      
 50 
     | 
    
         
            +
            - Dont answer in markdown format, just answer in plaintext.
         
     | 
| 
      
 51 
     | 
    
         
            +
            - Do not make guesses or assumptions
         
     | 
| 
      
 52 
     | 
    
         
            +
            - Only answer if the verified information is in the prompt.
         
     | 
| 
      
 53 
     | 
    
         
            +
            - Check that you are not missing a command
         
     | 
| 
      
 54 
     | 
    
         
            +
            - Focus only on helping users with operations detailed below.
         
     | 
| 
      
 55 
     | 
    
         
            +
            `;
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
            export function PROMPT_SKILLS_AND_EXAMPLES(skills: SkillGroup[], tag: string) {
         
     | 
| 
      
 58 
     | 
    
         
            +
              let foundSkills = skills.filter(
         
     | 
| 
      
 59 
     | 
    
         
            +
                (skill) => skill.tag == `@${tag.toLowerCase()}`,
         
     | 
| 
      
 60 
     | 
    
         
            +
              );
         
     | 
| 
      
 61 
     | 
    
         
            +
              if (!foundSkills.length || !foundSkills[0] || !foundSkills[0].skills)
         
     | 
| 
      
 62 
     | 
    
         
            +
                return "";
         
     | 
| 
      
 63 
     | 
    
         
            +
              let returnPrompt = `\nCommands:\n${foundSkills[0].skills
         
     | 
| 
      
 64 
     | 
    
         
            +
                .map((skill) => skill.command)
         
     | 
| 
      
 65 
     | 
    
         
            +
                .join("\n")}\n\nExamples:\n${foundSkills[0].skills
         
     | 
| 
      
 66 
     | 
    
         
            +
                .map((skill) => skill.examples)
         
     | 
| 
      
 67 
     | 
    
         
            +
                .join("\n")}`;
         
     | 
| 
      
 68 
     | 
    
         
            +
              return returnPrompt;
         
     | 
| 
      
 69 
     | 
    
         
            +
            }
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
            export async function textGeneration(
         
     | 
| 
      
 72 
     | 
    
         
            +
              memoryKey: string,
         
     | 
| 
      
 73 
     | 
    
         
            +
              userPrompt: string,
         
     | 
| 
      
 74 
     | 
    
         
            +
              systemPrompt: string,
         
     | 
| 
      
 75 
     | 
    
         
            +
            ) {
         
     | 
| 
      
 76 
     | 
    
         
            +
              if (!memoryKey) {
         
     | 
| 
      
 77 
     | 
    
         
            +
                clearMemory();
         
     | 
| 
      
 78 
     | 
    
         
            +
              }
         
     | 
| 
      
 79 
     | 
    
         
            +
              let messages = chatMemory.getHistory(memoryKey);
         
     | 
| 
      
 80 
     | 
    
         
            +
              chatMemory.initializeWithSystem(memoryKey, systemPrompt);
         
     | 
| 
      
 81 
     | 
    
         
            +
              if (messages.length === 0) {
         
     | 
| 
      
 82 
     | 
    
         
            +
                messages.push({
         
     | 
| 
      
 83 
     | 
    
         
            +
                  role: "system",
         
     | 
| 
      
 84 
     | 
    
         
            +
                  content: systemPrompt,
         
     | 
| 
      
 85 
     | 
    
         
            +
                });
         
     | 
| 
      
 86 
     | 
    
         
            +
              }
         
     | 
| 
      
 87 
     | 
    
         
            +
              messages.push({
         
     | 
| 
      
 88 
     | 
    
         
            +
                role: "user",
         
     | 
| 
      
 89 
     | 
    
         
            +
                content: userPrompt,
         
     | 
| 
      
 90 
     | 
    
         
            +
              });
         
     | 
| 
      
 91 
     | 
    
         
            +
              try {
         
     | 
| 
      
 92 
     | 
    
         
            +
                const response = await openai.chat.completions.create({
         
     | 
| 
      
 93 
     | 
    
         
            +
                  model: "gpt-4o",
         
     | 
| 
      
 94 
     | 
    
         
            +
                  messages: messages as any,
         
     | 
| 
      
 95 
     | 
    
         
            +
                });
         
     | 
| 
      
 96 
     | 
    
         
            +
                const reply = response.choices[0].message.content;
         
     | 
| 
      
 97 
     | 
    
         
            +
                messages.push({
         
     | 
| 
      
 98 
     | 
    
         
            +
                  role: "assistant",
         
     | 
| 
      
 99 
     | 
    
         
            +
                  content: reply || "No response from OpenAI.",
         
     | 
| 
      
 100 
     | 
    
         
            +
                });
         
     | 
| 
      
 101 
     | 
    
         
            +
                const cleanedReply = parseMarkdown(reply as string);
         
     | 
| 
      
 102 
     | 
    
         
            +
                chatMemory.addEntry(memoryKey, {
         
     | 
| 
      
 103 
     | 
    
         
            +
                  role: "assistant",
         
     | 
| 
      
 104 
     | 
    
         
            +
                  content: cleanedReply,
         
     | 
| 
      
 105 
     | 
    
         
            +
                });
         
     | 
| 
      
 106 
     | 
    
         
            +
                return { reply: cleanedReply, history: messages };
         
     | 
| 
      
 107 
     | 
    
         
            +
              } catch (error) {
         
     | 
| 
      
 108 
     | 
    
         
            +
                console.error("Failed to fetch from OpenAI:", error);
         
     | 
| 
      
 109 
     | 
    
         
            +
                throw error;
         
     | 
| 
      
 110 
     | 
    
         
            +
              }
         
     | 
| 
      
 111 
     | 
    
         
            +
            }
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
            export async function processMultilineResponse(
         
     | 
| 
      
 114 
     | 
    
         
            +
              memoryKey: string,
         
     | 
| 
      
 115 
     | 
    
         
            +
              reply: string,
         
     | 
| 
      
 116 
     | 
    
         
            +
              context: any,
         
     | 
| 
      
 117 
     | 
    
         
            +
            ) {
         
     | 
| 
      
 118 
     | 
    
         
            +
              if (!memoryKey) {
         
     | 
| 
      
 119 
     | 
    
         
            +
                clearMemory();
         
     | 
| 
      
 120 
     | 
    
         
            +
              }
         
     | 
| 
      
 121 
     | 
    
         
            +
              let messages = reply
         
     | 
| 
      
 122 
     | 
    
         
            +
                .split("\n")
         
     | 
| 
      
 123 
     | 
    
         
            +
                .map((message: string) => parseMarkdown(message))
         
     | 
| 
      
 124 
     | 
    
         
            +
                .filter((message): message is string => message.length > 0);
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
              console.log(messages);
         
     | 
| 
      
 127 
     | 
    
         
            +
              for (const message of messages) {
         
     | 
| 
      
 128 
     | 
    
         
            +
                if (message.startsWith("/")) {
         
     | 
| 
      
 129 
     | 
    
         
            +
                  const response = await context.skill(message);
         
     | 
| 
      
 130 
     | 
    
         
            +
                  if (response && typeof response.message === "string") {
         
     | 
| 
      
 131 
     | 
    
         
            +
                    let msg = parseMarkdown(response.message);
         
     | 
| 
      
 132 
     | 
    
         
            +
                    chatMemory.addEntry(memoryKey, {
         
     | 
| 
      
 133 
     | 
    
         
            +
                      role: "system",
         
     | 
| 
      
 134 
     | 
    
         
            +
                      content: msg,
         
     | 
| 
      
 135 
     | 
    
         
            +
                    });
         
     | 
| 
      
 136 
     | 
    
         
            +
                    await context.send(response.message);
         
     | 
| 
      
 137 
     | 
    
         
            +
                  }
         
     | 
| 
      
 138 
     | 
    
         
            +
                } else {
         
     | 
| 
      
 139 
     | 
    
         
            +
                  await context.send(message);
         
     | 
| 
      
 140 
     | 
    
         
            +
                }
         
     | 
| 
      
 141 
     | 
    
         
            +
              }
         
     | 
| 
      
 142 
     | 
    
         
            +
            }
         
     | 
| 
      
 143 
     | 
    
         
            +
            export function parseMarkdown(message: string) {
         
     | 
| 
      
 144 
     | 
    
         
            +
              let trimmedMessage = message;
         
     | 
| 
      
 145 
     | 
    
         
            +
              // Remove bold and underline markdown
         
     | 
| 
      
 146 
     | 
    
         
            +
              trimmedMessage = trimmedMessage?.replace(/(\*\*|__)(.*?)\1/g, "$2");
         
     | 
| 
      
 147 
     | 
    
         
            +
              // Remove markdown links, keeping only the URL
         
     | 
| 
      
 148 
     | 
    
         
            +
              trimmedMessage = trimmedMessage?.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$2");
         
     | 
| 
      
 149 
     | 
    
         
            +
              // Remove markdown headers
         
     | 
| 
      
 150 
     | 
    
         
            +
              trimmedMessage = trimmedMessage?.replace(/^#+\s*(.*)$/gm, "$1");
         
     | 
| 
      
 151 
     | 
    
         
            +
              // Remove inline code formatting
         
     | 
| 
      
 152 
     | 
    
         
            +
              trimmedMessage = trimmedMessage?.replace(/`([^`]+)`/g, "$1");
         
     | 
| 
      
 153 
     | 
    
         
            +
              // Remove single backticks at the start or end of the message
         
     | 
| 
      
 154 
     | 
    
         
            +
              trimmedMessage = trimmedMessage?.replace(/^`|`$/g, "");
         
     | 
| 
      
 155 
     | 
    
         
            +
              // Remove leading and trailing whitespace
         
     | 
| 
      
 156 
     | 
    
         
            +
              trimmedMessage = trimmedMessage?.replace(/^\s+|\s+$/g, "");
         
     | 
| 
      
 157 
     | 
    
         
            +
              // Remove any remaining leading or trailing whitespace
         
     | 
| 
      
 158 
     | 
    
         
            +
              trimmedMessage = trimmedMessage.trim();
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
              return trimmedMessage;
         
     | 
| 
      
 161 
     | 
    
         
            +
            }
         
     | 
| 
         @@ -48,7 +48,7 @@ export const chatMemory = new ChatMemory(); 
     | 
|
| 
       48 
48 
     | 
    
         
             
            let chatHistories: ChatHistories = {};
         
     | 
| 
       49 
49 
     | 
    
         
             
            export const PROMPT_RULES = `You are a helpful and playful agent called {NAME} that lives inside a web3 messaging app called Converse.
         
     | 
| 
       50 
50 
     | 
    
         
             
            - You can respond with multiple messages if needed. Each message should be separated by a newline character.
         
     | 
| 
       51 
     | 
    
         
            -
            - You can trigger  
     | 
| 
      
 51 
     | 
    
         
            +
            - You can trigger skills by only sending the command in a newline message.
         
     | 
| 
       52 
52 
     | 
    
         
             
            - Never announce actions without using a command separated by a newline character.
         
     | 
| 
       53 
53 
     | 
    
         
             
            - Dont answer in markdown format, just answer in plaintext.
         
     | 
| 
       54 
54 
     | 
    
         
             
            - Do not make guesses or assumptions
         
     | 
| 
         @@ -81,7 +81,7 @@ export async function agentResponse( 
     | 
|
| 
       81 
81 
     | 
    
         
             
                  userPrompt,
         
     | 
| 
       82 
82 
     | 
    
         
             
                  systemPrompt,
         
     | 
| 
       83 
83 
     | 
    
         
             
                );
         
     | 
| 
       84 
     | 
    
         
            -
                await  
     | 
| 
      
 84 
     | 
    
         
            +
                await processMultilineResponse(sender.address, reply, context);
         
     | 
| 
       85 
85 
     | 
    
         
             
              } catch (error) {
         
     | 
| 
       86 
86 
     | 
    
         
             
                console.error("Error during OpenAI call:", error);
         
     | 
| 
       87 
87 
     | 
    
         
             
                await context.reply("An error occurred while processing your request.");
         
     | 
| 
         @@ -126,7 +126,7 @@ export async function textGeneration( 
     | 
|
| 
       126 
126 
     | 
    
         
             
              }
         
     | 
| 
       127 
127 
     | 
    
         
             
            }
         
     | 
| 
       128 
128 
     | 
    
         | 
| 
       129 
     | 
    
         
            -
            export async function  
     | 
| 
      
 129 
     | 
    
         
            +
            export async function processMultilineResponse(
         
     | 
| 
       130 
130 
     | 
    
         
             
              address: string,
         
     | 
| 
       131 
131 
     | 
    
         
             
              reply: string,
         
     | 
| 
       132 
132 
     | 
    
         
             
              context: any,
         
     | 
| 
         @@ -1,5 +1,6 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            import { Client } from "@xmtp/xmtp-js";
         
     | 
| 
      
 1 
     | 
    
         
            +
            import type { Client } from "@xmtp/xmtp-js";
         
     | 
| 
       2 
2 
     | 
    
         
             
            import { isAddress } from "viem";
         
     | 
| 
      
 3 
     | 
    
         
            +
            import type { HandlerContext } from "@xmtp/message-kit";
         
     | 
| 
       3 
4 
     | 
    
         | 
| 
       4 
5 
     | 
    
         
             
            export const converseEndpointURL =
         
     | 
| 
       5 
6 
     | 
    
         
             
              "https://converse-website-git-endpoit-ephemerahq.vercel.app";
         
     | 
| 
         @@ -16,8 +17,8 @@ export type ConverseProfile = { 
     | 
|
| 
       16 
17 
     | 
    
         
             
            export type UserInfo = {
         
     | 
| 
       17 
18 
     | 
    
         
             
              ensDomain?: string | undefined;
         
     | 
| 
       18 
19 
     | 
    
         
             
              address?: string | undefined;
         
     | 
| 
      
 20 
     | 
    
         
            +
              preferredName: string | undefined;
         
     | 
| 
       19 
21 
     | 
    
         
             
              converseUsername?: string | undefined;
         
     | 
| 
       20 
     | 
    
         
            -
              preferredName?: string | undefined;
         
     | 
| 
       21 
22 
     | 
    
         
             
              ensInfo?: EnsData | undefined;
         
     | 
| 
       22 
23 
     | 
    
         
             
              avatar?: string | undefined;
         
     | 
| 
       23 
24 
     | 
    
         
             
            };
         
     | 
| 
         @@ -48,12 +49,14 @@ export const clearInfoCache = () => { 
     | 
|
| 
       48 
49 
     | 
    
         
             
            export const getUserInfo = async (
         
     | 
| 
       49 
50 
     | 
    
         
             
              key: string,
         
     | 
| 
       50 
51 
     | 
    
         
             
              clientAddress?: string,
         
     | 
| 
      
 52 
     | 
    
         
            +
              context?: HandlerContext,
         
     | 
| 
       51 
53 
     | 
    
         
             
            ): Promise<UserInfo | null> => {
         
     | 
| 
       52 
54 
     | 
    
         
             
              let data: UserInfo = infoCache.get(key) || {
         
     | 
| 
       53 
55 
     | 
    
         
             
                ensDomain: undefined,
         
     | 
| 
       54 
56 
     | 
    
         
             
                address: undefined,
         
     | 
| 
       55 
57 
     | 
    
         
             
                converseUsername: undefined,
         
     | 
| 
       56 
58 
     | 
    
         
             
                ensInfo: undefined,
         
     | 
| 
      
 59 
     | 
    
         
            +
                preferredName: undefined,
         
     | 
| 
       57 
60 
     | 
    
         
             
              };
         
     | 
| 
       58 
61 
     | 
    
         
             
              if (isAddress(clientAddress || "")) {
         
     | 
| 
       59 
62 
     | 
    
         
             
                data.address = clientAddress;
         
     | 
| 
         @@ -74,12 +77,15 @@ export const getUserInfo = async ( 
     | 
|
| 
       74 
77 
     | 
    
         
             
              } else {
         
     | 
| 
       75 
78 
     | 
    
         
             
                data.converseUsername = key;
         
     | 
| 
       76 
79 
     | 
    
         
             
              }
         
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
      
 80 
     | 
    
         
            +
              data.preferredName = data.ensDomain || data.converseUsername || "Friend";
         
     | 
| 
       78 
81 
     | 
    
         
             
              let keyToUse = data.address || data.ensDomain || data.converseUsername;
         
     | 
| 
       79 
82 
     | 
    
         
             
              let cacheData = keyToUse && infoCache.get(keyToUse);
         
     | 
| 
       80 
     | 
    
         
            -
              console.log("Getting user info", { cacheData, keyToUse, data });
         
     | 
| 
      
 83 
     | 
    
         
            +
              //console.log("Getting user info", { cacheData, keyToUse, data });
         
     | 
| 
       81 
84 
     | 
    
         
             
              if (cacheData) return cacheData;
         
     | 
| 
       82 
85 
     | 
    
         | 
| 
      
 86 
     | 
    
         
            +
              context?.send(
         
     | 
| 
      
 87 
     | 
    
         
            +
                "Hey there! Give me a sec while I fetch info about you first...",
         
     | 
| 
      
 88 
     | 
    
         
            +
              );
         
     | 
| 
       83 
89 
     | 
    
         
             
              if (keyToUse?.includes(".eth")) {
         
     | 
| 
       84 
90 
     | 
    
         
             
                const response = await fetch(`https://ensdata.net/${keyToUse}`);
         
     | 
| 
       85 
91 
     | 
    
         
             
                const ensData: EnsData = (await response.json()) as EnsData;
         
     | 
| 
         @@ -109,6 +115,8 @@ export const getUserInfo = async ( 
     | 
|
| 
       109 
115 
     | 
    
         
             
                data.address = converseData?.address || undefined;
         
     | 
| 
       110 
116 
     | 
    
         
             
                data.avatar = converseData?.avatar || undefined;
         
     | 
| 
       111 
117 
     | 
    
         
             
              }
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
              data.preferredName = data.ensDomain || data.converseUsername || "Friend";
         
     | 
| 
       112 
120 
     | 
    
         
             
              if (data.address) infoCache.set(data.address, data);
         
     | 
| 
       113 
121 
     | 
    
         
             
              return data;
         
     | 
| 
       114 
122 
     | 
    
         
             
            };
         
     | 
| 
         @@ -123,7 +131,8 @@ export const isOnXMTP = async ( 
     | 
|
| 
       123 
131 
     | 
    
         | 
| 
       124 
132 
     | 
    
         
             
            export const PROMPT_USER_CONTENT = (userInfo: UserInfo) => {
         
     | 
| 
       125 
133 
     | 
    
         
             
              let { address, ensDomain, converseUsername, preferredName } = userInfo;
         
     | 
| 
       126 
     | 
    
         
            -
              let prompt = ` 
     | 
| 
      
 134 
     | 
    
         
            +
              let prompt = `
         
     | 
| 
      
 135 
     | 
    
         
            +
            User context: 
         
     | 
| 
       127 
136 
     | 
    
         
             
            - Start by fetch their domain from or Convese username
         
     | 
| 
       128 
137 
     | 
    
         
             
            - Call the user by their name or domain, in case they have one
         
     | 
| 
       129 
138 
     | 
    
         
             
            - Ask for a name (if they don't have one) so you can suggest domains.
         
     | 
| 
         @@ -132,5 +141,11 @@ export const PROMPT_USER_CONTENT = (userInfo: UserInfo) => { 
     | 
|
| 
       132 
141 
     | 
    
         
             
              if (ensDomain) prompt += `\n- User ENS domain is: ${ensDomain}`;
         
     | 
| 
       133 
142 
     | 
    
         
             
              if (converseUsername)
         
     | 
| 
       134 
143 
     | 
    
         
             
                prompt += `\n- Converse username is: ${converseUsername}`;
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
              prompt = prompt.replace("{ADDRESS}", address || "");
         
     | 
| 
      
 146 
     | 
    
         
            +
              prompt = prompt.replace("{ENS_DOMAIN}", ensDomain || "");
         
     | 
| 
      
 147 
     | 
    
         
            +
              prompt = prompt.replace("{CONVERSE_USERNAME}", converseUsername || "");
         
     | 
| 
      
 148 
     | 
    
         
            +
              prompt = prompt.replace("{PREFERRED_NAME}", preferredName || "");
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
       135 
150 
     | 
    
         
             
              return prompt;
         
     | 
| 
       136 
151 
     | 
    
         
             
            };
         
     | 
| 
         @@ -1,6 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            import { skills } from "./skills.js";
         
     | 
| 
       2 
2 
     | 
    
         
             
            import { UserInfo, PROMPT_USER_CONTENT } from "./lib/resolver.js";
         
     | 
| 
       3 
     | 
    
         
            -
            import { PROMPT_RULES, PROMPT_SKILLS_AND_EXAMPLES } from "./lib/ 
     | 
| 
      
 3 
     | 
    
         
            +
            import { PROMPT_RULES, PROMPT_SKILLS_AND_EXAMPLES } from "./lib/gpt.js";
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
            export async function agent_prompt(userInfo: UserInfo) {
         
     | 
| 
       6 
6 
     | 
    
         
             
              let { address, ensDomain, converseUsername, preferredName } = userInfo;
         
     | 
| 
         @@ -12,7 +12,7 @@ export async function agent_prompt(userInfo: UserInfo) { 
     | 
|
| 
       12 
12 
     | 
    
         
             
              systemPrompt += PROMPT_USER_CONTENT(userInfo);
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
       14 
14 
     | 
    
         
             
              //Add skills and examples to the prompt
         
     | 
| 
       15 
     | 
    
         
            -
              systemPrompt += PROMPT_SKILLS_AND_EXAMPLES(skills);
         
     | 
| 
      
 15 
     | 
    
         
            +
              systemPrompt += PROMPT_SKILLS_AND_EXAMPLES(skills, "@ens");
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
17 
     | 
    
         
             
              systemPrompt += `
         
     | 
| 
       18 
18 
     | 
    
         | 
| 
         @@ -20,6 +20,19 @@ export const skills: SkillGroup[] = [ 
     | 
|
| 
       20 
20 
     | 
    
         
             
                      },
         
     | 
| 
       21 
21 
     | 
    
         
             
                    },
         
     | 
| 
       22 
22 
     | 
    
         
             
                  },
         
     | 
| 
      
 23 
     | 
    
         
            +
                  {
         
     | 
| 
      
 24 
     | 
    
         
            +
                    command: "/exists",
         
     | 
| 
      
 25 
     | 
    
         
            +
                    adminOnly: true,
         
     | 
| 
      
 26 
     | 
    
         
            +
                    examples: ["/exists"],
         
     | 
| 
      
 27 
     | 
    
         
            +
                    handler: handleEns,
         
     | 
| 
      
 28 
     | 
    
         
            +
                    triggers: ["/exists"],
         
     | 
| 
      
 29 
     | 
    
         
            +
                    description: "Check if an address is onboarded.",
         
     | 
| 
      
 30 
     | 
    
         
            +
                    params: {
         
     | 
| 
      
 31 
     | 
    
         
            +
                      address: {
         
     | 
| 
      
 32 
     | 
    
         
            +
                        type: "address",
         
     | 
| 
      
 33 
     | 
    
         
            +
                      },
         
     | 
| 
      
 34 
     | 
    
         
            +
                    },
         
     | 
| 
      
 35 
     | 
    
         
            +
                  },
         
     | 
| 
       23 
36 
     | 
    
         
             
                  {
         
     | 
| 
       24 
37 
     | 
    
         
             
                    command: "/info [domain]",
         
     | 
| 
       25 
38 
     | 
    
         
             
                    triggers: ["/info"],
         
     | 
| 
         @@ -1,9 +1,16 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            import { run, HandlerContext } from "@xmtp/message-kit";
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            run( 
     | 
| 
       4 
     | 
    
         
            -
               
     | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
      
 3 
     | 
    
         
            +
            run(
         
     | 
| 
      
 4 
     | 
    
         
            +
              async (context: HandlerContext) => {
         
     | 
| 
      
 5 
     | 
    
         
            +
                // Get the message and the address from the sender
         
     | 
| 
      
 6 
     | 
    
         
            +
                const { content, sender } = context.message;
         
     | 
| 
       6 
7 
     | 
    
         | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
            } 
     | 
| 
      
 8 
     | 
    
         
            +
                // To reply, just call `reply` on the HandlerContext
         
     | 
| 
      
 9 
     | 
    
         
            +
                await context.send(`gm`);
         
     | 
| 
      
 10 
     | 
    
         
            +
              },
         
     | 
| 
      
 11 
     | 
    
         
            +
              {
         
     | 
| 
      
 12 
     | 
    
         
            +
                client: {
         
     | 
| 
      
 13 
     | 
    
         
            +
                  logging: "debug",
         
     | 
| 
      
 14 
     | 
    
         
            +
                },
         
     | 
| 
      
 15 
     | 
    
         
            +
              },
         
     | 
| 
      
 16 
     | 
    
         
            +
            );
         
     | 
| 
         @@ -1,5 +1,5 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            import { HandlerContext, AbstractedMember } from "@xmtp/message-kit";
         
     | 
| 
       2 
     | 
    
         
            -
            import { textGeneration } from "../lib/ 
     | 
| 
      
 2 
     | 
    
         
            +
            import { textGeneration } from "../lib/gpt.js";
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            export async function handler(context: HandlerContext) {
         
     | 
| 
       5 
5 
     | 
    
         
             
              if (!process?.env?.OPEN_AI_API_KEY) {
         
     | 
| 
         @@ -12,6 +12,7 @@ export async function handler(context: HandlerContext) { 
     | 
|
| 
       12 
12 
     | 
    
         
             
                  sender,
         
     | 
| 
       13 
13 
     | 
    
         
             
                  content: { content, params },
         
     | 
| 
       14 
14 
     | 
    
         
             
                },
         
     | 
| 
      
 15 
     | 
    
         
            +
                skill,
         
     | 
| 
       15 
16 
     | 
    
         
             
              } = context;
         
     | 
| 
       16 
17 
     | 
    
         | 
| 
       17 
18 
     | 
    
         
             
              const systemPrompt = generateSystemPrompt(context);
         
     | 
| 
         @@ -23,7 +24,7 @@ export async function handler(context: HandlerContext) { 
     | 
|
| 
       23 
24 
     | 
    
         
             
                  userPrompt,
         
     | 
| 
       24 
25 
     | 
    
         
             
                  systemPrompt,
         
     | 
| 
       25 
26 
     | 
    
         
             
                );
         
     | 
| 
       26 
     | 
    
         
            -
                 
     | 
| 
      
 27 
     | 
    
         
            +
                skill(reply);
         
     | 
| 
       27 
28 
     | 
    
         
             
              } catch (error) {
         
     | 
| 
       28 
29 
     | 
    
         
             
                console.error("Error during OpenAI call:", error);
         
     | 
| 
       29 
30 
     | 
    
         
             
                await context.reply("An error occurred while processing your request.");
         
     | 
| 
         @@ -59,7 +60,7 @@ function generateSystemPrompt(context: HandlerContext) { 
     | 
|
| 
       59 
60 
     | 
    
         
             
              Important:
         
     | 
| 
       60 
61 
     | 
    
         
             
              - If a user asks jokes, make jokes about web3 devs\n
         
     | 
| 
       61 
62 
     | 
    
         
             
              - If the user asks about performing an action and you can think of a command that would help, answer directly with the command and nothing else. 
         
     | 
| 
       62 
     | 
    
         
            -
              - Populate the command with the correct or random values. Always return  
     | 
| 
      
 63 
     | 
    
         
            +
              - Populate the command with the correct or random values. Always return skills with real values only, using usernames with @ and excluding addresses.\n
         
     | 
| 
       63 
64 
     | 
    
         
             
              - If the user asks a question or makes a statement that does not clearly map to a command, respond with helpful information or a clarification question.\n
         
     | 
| 
       64 
65 
     | 
    
         
             
              - If the user is grateful, respond asking for a tip in a playful manner.
         
     | 
| 
       65 
66 
     | 
    
         
             
              `;
         
     | 
| 
         @@ -34,7 +34,7 @@ export async function handler(context: HandlerContext) { 
     | 
|
| 
       34 
34 
     | 
    
         
             
                  context.send("Available games: \n/game wordle\n/game slot");
         
     | 
| 
       35 
35 
     | 
    
         
             
                  break;
         
     | 
| 
       36 
36 
     | 
    
         
             
                default:
         
     | 
| 
       37 
     | 
    
         
            -
                  // Inform the user about unrecognized  
     | 
| 
      
 37 
     | 
    
         
            +
                  // Inform the user about unrecognized skills and provide available options
         
     | 
| 
       38 
38 
     | 
    
         
             
                  context.send(
         
     | 
| 
       39 
39 
     | 
    
         
             
                    "Command not recognized. Available games: wordle, slot, or help.",
         
     | 
| 
       40 
40 
     | 
    
         
             
                  );
         
     | 
| 
         @@ -1,5 +1,5 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            import { HandlerContext } from "@xmtp/message-kit";
         
     | 
| 
       2 
     | 
    
         
            -
            import { textGeneration } from "../lib/ 
     | 
| 
      
 2 
     | 
    
         
            +
            import { textGeneration } from "../lib/gpt.js";
         
     | 
| 
       3 
3 
     | 
    
         
             
            import { vision } from "../lib/vision.js";
         
     | 
| 
       4 
4 
     | 
    
         
             
            import { getUserInfo } from "../lib/resolver.js";
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
         @@ -10,7 +10,7 @@ export async function handler(context: HandlerContext) { 
     | 
|
| 
       10 
10 
     | 
    
         
             
              }
         
     | 
| 
       11 
11 
     | 
    
         
             
              const {
         
     | 
| 
       12 
12 
     | 
    
         
             
                members,
         
     | 
| 
       13 
     | 
    
         
            -
                 
     | 
| 
      
 13 
     | 
    
         
            +
                skill,
         
     | 
| 
       14 
14 
     | 
    
         
             
                message: {
         
     | 
| 
       15 
15 
     | 
    
         
             
                  typeId,
         
     | 
| 
       16 
16 
     | 
    
         
             
                  content: { attachment },
         
     | 
| 
         @@ -57,7 +57,7 @@ export async function handler(context: HandlerContext) { 
     | 
|
| 
       57 
57 
     | 
    
         
             
                  let splitMessages = JSON.parse(reply);
         
     | 
| 
       58 
58 
     | 
    
         
             
                  for (const message of splitMessages) {
         
     | 
| 
       59 
59 
     | 
    
         
             
                    let msg = message as string;
         
     | 
| 
       60 
     | 
    
         
            -
                    if (msg.startsWith("/")) await  
     | 
| 
      
 60 
     | 
    
         
            +
                    if (msg.startsWith("/")) await skill(msg);
         
     | 
| 
       61 
61 
     | 
    
         
             
                    else await context.send(msg);
         
     | 
| 
       62 
62 
     | 
    
         
             
                  }
         
     | 
| 
       63 
63 
     | 
    
         
             
                }
         
     | 
| 
         @@ -7,7 +7,6 @@ export async function handler(context: HandlerContext) { 
     | 
|
| 
       7 
7 
     | 
    
         
             
                getMessageById,
         
     | 
| 
       8 
8 
     | 
    
         
             
                message: { content, sender, typeId },
         
     | 
| 
       9 
9 
     | 
    
         
             
              } = context;
         
     | 
| 
       10 
     | 
    
         
            -
              console.log(sender);
         
     | 
| 
       11 
10 
     | 
    
         
             
              const msg = await getMessageById(content.reference);
         
     | 
| 
       12 
11 
     | 
    
         
             
              const replyReceiver = members?.find(
         
     | 
| 
       13 
12 
     | 
    
         
             
                (member) => member.inboxId === msg?.senderInboxId,
         
     | 
| 
         @@ -28,7 +27,7 @@ export async function handler(context: HandlerContext) { 
     | 
|
| 
       28 
27 
     | 
    
         
             
              } else if (typeId === "text") {
         
     | 
| 
       29 
28 
     | 
    
         
             
                const { content: text, params } = content;
         
     | 
| 
       30 
29 
     | 
    
         
             
                if (text.startsWith("/tip") && params) {
         
     | 
| 
       31 
     | 
    
         
            -
                  // Process text  
     | 
| 
      
 30 
     | 
    
         
            +
                  // Process text skills starting with "/tip"
         
     | 
| 
       32 
31 
     | 
    
         
             
                  const {
         
     | 
| 
       33 
32 
     | 
    
         
             
                    params: { amount: extractedAmount, username },
         
     | 
| 
       34 
33 
     | 
    
         
             
                  } = content;
         
     | 
| 
         @@ -43,7 +42,6 @@ export async function handler(context: HandlerContext) { 
     | 
|
| 
       43 
42 
     | 
    
         
             
                context.reply("Sender or receiver or amount not found.");
         
     | 
| 
       44 
43 
     | 
    
         
             
                return;
         
     | 
| 
       45 
44 
     | 
    
         
             
              }
         
     | 
| 
       46 
     | 
    
         
            -
              console.log(receivers);
         
     | 
| 
       47 
45 
     | 
    
         
             
              const receiverAddresses = receivers.map((receiver) => receiver.address);
         
     | 
| 
       48 
46 
     | 
    
         
             
              // Process sending tokens to each receiver
         
     | 
| 
       49 
47 
     | 
    
         | 
| 
         @@ -21,7 +21,6 @@ export async function handler(context: HandlerContext) { 
     | 
|
| 
       21 
21 
     | 
    
         
             
                    );
         
     | 
| 
       22 
22 
     | 
    
         
             
                    return;
         
     | 
| 
       23 
23 
     | 
    
         
             
                  }
         
     | 
| 
       24 
     | 
    
         
            -
                  let name = senderInfo.converseUsername || senderInfo.address;
         
     | 
| 
       25 
24 
     | 
    
         | 
| 
       26 
25 
     | 
    
         
             
                  let sendUrl = `${baseUrl}/?transaction_type=send&amount=${amountSend}&token=${tokenSend}&receiver=${senderInfo.address}`;
         
     | 
| 
       27 
26 
     | 
    
         
             
                  context.send(`${sendUrl}`);
         
     | 
| 
         @@ -52,6 +52,6 @@ export async function helpHandler(context: HandlerContext) { 
     | 
|
| 
       52 
52 
     | 
    
         
             
                  ?.flatMap((app) => app.skills)
         
     | 
| 
       53 
53 
     | 
    
         
             
                  .map((skill) => `${skill.command} - ${skill.description}`)
         
     | 
| 
       54 
54 
     | 
    
         
             
                  .join("\n") +
         
     | 
| 
       55 
     | 
    
         
            -
                "\nUse these  
     | 
| 
      
 55 
     | 
    
         
            +
                "\nUse these skills to interact with specific apps.";
         
     | 
| 
       56 
56 
     | 
    
         
             
              context.send(intro);
         
     | 
| 
       57 
57 
     | 
    
         
             
            }
         
     | 
| 
         @@ -0,0 +1,161 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import "dotenv/config";
         
     | 
| 
      
 2 
     | 
    
         
            +
            import type { SkillGroup } from "@xmtp/message-kit";
         
     | 
| 
      
 3 
     | 
    
         
            +
            import OpenAI from "openai";
         
     | 
| 
      
 4 
     | 
    
         
            +
            const openai = new OpenAI({
         
     | 
| 
      
 5 
     | 
    
         
            +
              apiKey: process.env.OPEN_AI_API_KEY,
         
     | 
| 
      
 6 
     | 
    
         
            +
            });
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            type ChatHistoryEntry = { role: string; content: string };
         
     | 
| 
      
 9 
     | 
    
         
            +
            type ChatHistories = Record<string, ChatHistoryEntry[]>;
         
     | 
| 
      
 10 
     | 
    
         
            +
            // New ChatMemory class
         
     | 
| 
      
 11 
     | 
    
         
            +
            class ChatMemory {
         
     | 
| 
      
 12 
     | 
    
         
            +
              private histories: ChatHistories = {};
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              getHistory(address: string): ChatHistoryEntry[] {
         
     | 
| 
      
 15 
     | 
    
         
            +
                return this.histories[address] || [];
         
     | 
| 
      
 16 
     | 
    
         
            +
              }
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              addEntry(address: string, entry: ChatHistoryEntry) {
         
     | 
| 
      
 19 
     | 
    
         
            +
                if (!this.histories[address]) {
         
     | 
| 
      
 20 
     | 
    
         
            +
                  this.histories[address] = [];
         
     | 
| 
      
 21 
     | 
    
         
            +
                }
         
     | 
| 
      
 22 
     | 
    
         
            +
                this.histories[address].push(entry);
         
     | 
| 
      
 23 
     | 
    
         
            +
              }
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
              initializeWithSystem(address: string, systemPrompt: string) {
         
     | 
| 
      
 26 
     | 
    
         
            +
                if (this.getHistory(address).length === 0) {
         
     | 
| 
      
 27 
     | 
    
         
            +
                  this.addEntry(address, {
         
     | 
| 
      
 28 
     | 
    
         
            +
                    role: "system",
         
     | 
| 
      
 29 
     | 
    
         
            +
                    content: systemPrompt,
         
     | 
| 
      
 30 
     | 
    
         
            +
                  });
         
     | 
| 
      
 31 
     | 
    
         
            +
                }
         
     | 
| 
      
 32 
     | 
    
         
            +
              }
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
              clear() {
         
     | 
| 
      
 35 
     | 
    
         
            +
                this.histories = {};
         
     | 
| 
      
 36 
     | 
    
         
            +
              }
         
     | 
| 
      
 37 
     | 
    
         
            +
            }
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
            // Create singleton instance
         
     | 
| 
      
 40 
     | 
    
         
            +
            export const chatMemory = new ChatMemory();
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
            export const clearMemory = () => {
         
     | 
| 
      
 43 
     | 
    
         
            +
              chatMemory.clear();
         
     | 
| 
      
 44 
     | 
    
         
            +
            };
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
            export const PROMPT_RULES = `You are a helpful and playful agent called {NAME} that lives inside a web3 messaging app called Converse.
         
     | 
| 
      
 47 
     | 
    
         
            +
            - You can respond with multiple messages if needed. Each message should be separated by a newline character.
         
     | 
| 
      
 48 
     | 
    
         
            +
            - You can trigger skills by only sending the command in a newline message.
         
     | 
| 
      
 49 
     | 
    
         
            +
            - Never announce actions without using a command separated by a newline character.
         
     | 
| 
      
 50 
     | 
    
         
            +
            - Dont answer in markdown format, just answer in plaintext.
         
     | 
| 
      
 51 
     | 
    
         
            +
            - Do not make guesses or assumptions
         
     | 
| 
      
 52 
     | 
    
         
            +
            - Only answer if the verified information is in the prompt.
         
     | 
| 
      
 53 
     | 
    
         
            +
            - Check that you are not missing a command
         
     | 
| 
      
 54 
     | 
    
         
            +
            - Focus only on helping users with operations detailed below.
         
     | 
| 
      
 55 
     | 
    
         
            +
            `;
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
            export function PROMPT_SKILLS_AND_EXAMPLES(skills: SkillGroup[], tag: string) {
         
     | 
| 
      
 58 
     | 
    
         
            +
              let foundSkills = skills.filter(
         
     | 
| 
      
 59 
     | 
    
         
            +
                (skill) => skill.tag == `@${tag.toLowerCase()}`,
         
     | 
| 
      
 60 
     | 
    
         
            +
              );
         
     | 
| 
      
 61 
     | 
    
         
            +
              if (!foundSkills.length || !foundSkills[0] || !foundSkills[0].skills)
         
     | 
| 
      
 62 
     | 
    
         
            +
                return "";
         
     | 
| 
      
 63 
     | 
    
         
            +
              let returnPrompt = `\nCommands:\n${foundSkills[0].skills
         
     | 
| 
      
 64 
     | 
    
         
            +
                .map((skill) => skill.command)
         
     | 
| 
      
 65 
     | 
    
         
            +
                .join("\n")}\n\nExamples:\n${foundSkills[0].skills
         
     | 
| 
      
 66 
     | 
    
         
            +
                .map((skill) => skill.examples)
         
     | 
| 
      
 67 
     | 
    
         
            +
                .join("\n")}`;
         
     | 
| 
      
 68 
     | 
    
         
            +
              return returnPrompt;
         
     | 
| 
      
 69 
     | 
    
         
            +
            }
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
            export async function textGeneration(
         
     | 
| 
      
 72 
     | 
    
         
            +
              memoryKey: string,
         
     | 
| 
      
 73 
     | 
    
         
            +
              userPrompt: string,
         
     | 
| 
      
 74 
     | 
    
         
            +
              systemPrompt: string,
         
     | 
| 
      
 75 
     | 
    
         
            +
            ) {
         
     | 
| 
      
 76 
     | 
    
         
            +
              if (!memoryKey) {
         
     | 
| 
      
 77 
     | 
    
         
            +
                clearMemory();
         
     | 
| 
      
 78 
     | 
    
         
            +
              }
         
     | 
| 
      
 79 
     | 
    
         
            +
              let messages = chatMemory.getHistory(memoryKey);
         
     | 
| 
      
 80 
     | 
    
         
            +
              chatMemory.initializeWithSystem(memoryKey, systemPrompt);
         
     | 
| 
      
 81 
     | 
    
         
            +
              if (messages.length === 0) {
         
     | 
| 
      
 82 
     | 
    
         
            +
                messages.push({
         
     | 
| 
      
 83 
     | 
    
         
            +
                  role: "system",
         
     | 
| 
      
 84 
     | 
    
         
            +
                  content: systemPrompt,
         
     | 
| 
      
 85 
     | 
    
         
            +
                });
         
     | 
| 
      
 86 
     | 
    
         
            +
              }
         
     | 
| 
      
 87 
     | 
    
         
            +
              messages.push({
         
     | 
| 
      
 88 
     | 
    
         
            +
                role: "user",
         
     | 
| 
      
 89 
     | 
    
         
            +
                content: userPrompt,
         
     | 
| 
      
 90 
     | 
    
         
            +
              });
         
     | 
| 
      
 91 
     | 
    
         
            +
              try {
         
     | 
| 
      
 92 
     | 
    
         
            +
                const response = await openai.chat.completions.create({
         
     | 
| 
      
 93 
     | 
    
         
            +
                  model: "gpt-4o",
         
     | 
| 
      
 94 
     | 
    
         
            +
                  messages: messages as any,
         
     | 
| 
      
 95 
     | 
    
         
            +
                });
         
     | 
| 
      
 96 
     | 
    
         
            +
                const reply = response.choices[0].message.content;
         
     | 
| 
      
 97 
     | 
    
         
            +
                messages.push({
         
     | 
| 
      
 98 
     | 
    
         
            +
                  role: "assistant",
         
     | 
| 
      
 99 
     | 
    
         
            +
                  content: reply || "No response from OpenAI.",
         
     | 
| 
      
 100 
     | 
    
         
            +
                });
         
     | 
| 
      
 101 
     | 
    
         
            +
                const cleanedReply = parseMarkdown(reply as string);
         
     | 
| 
      
 102 
     | 
    
         
            +
                chatMemory.addEntry(memoryKey, {
         
     | 
| 
      
 103 
     | 
    
         
            +
                  role: "assistant",
         
     | 
| 
      
 104 
     | 
    
         
            +
                  content: cleanedReply,
         
     | 
| 
      
 105 
     | 
    
         
            +
                });
         
     | 
| 
      
 106 
     | 
    
         
            +
                return { reply: cleanedReply, history: messages };
         
     | 
| 
      
 107 
     | 
    
         
            +
              } catch (error) {
         
     | 
| 
      
 108 
     | 
    
         
            +
                console.error("Failed to fetch from OpenAI:", error);
         
     | 
| 
      
 109 
     | 
    
         
            +
                throw error;
         
     | 
| 
      
 110 
     | 
    
         
            +
              }
         
     | 
| 
      
 111 
     | 
    
         
            +
            }
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
            export async function processMultilineResponse(
         
     | 
| 
      
 114 
     | 
    
         
            +
              memoryKey: string,
         
     | 
| 
      
 115 
     | 
    
         
            +
              reply: string,
         
     | 
| 
      
 116 
     | 
    
         
            +
              context: any,
         
     | 
| 
      
 117 
     | 
    
         
            +
            ) {
         
     | 
| 
      
 118 
     | 
    
         
            +
              if (!memoryKey) {
         
     | 
| 
      
 119 
     | 
    
         
            +
                clearMemory();
         
     | 
| 
      
 120 
     | 
    
         
            +
              }
         
     | 
| 
      
 121 
     | 
    
         
            +
              let messages = reply
         
     | 
| 
      
 122 
     | 
    
         
            +
                .split("\n")
         
     | 
| 
      
 123 
     | 
    
         
            +
                .map((message: string) => parseMarkdown(message))
         
     | 
| 
      
 124 
     | 
    
         
            +
                .filter((message): message is string => message.length > 0);
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
              console.log(messages);
         
     | 
| 
      
 127 
     | 
    
         
            +
              for (const message of messages) {
         
     | 
| 
      
 128 
     | 
    
         
            +
                if (message.startsWith("/")) {
         
     | 
| 
      
 129 
     | 
    
         
            +
                  const response = await context.skill(message);
         
     | 
| 
      
 130 
     | 
    
         
            +
                  if (response && typeof response.message === "string") {
         
     | 
| 
      
 131 
     | 
    
         
            +
                    let msg = parseMarkdown(response.message);
         
     | 
| 
      
 132 
     | 
    
         
            +
                    chatMemory.addEntry(memoryKey, {
         
     | 
| 
      
 133 
     | 
    
         
            +
                      role: "system",
         
     | 
| 
      
 134 
     | 
    
         
            +
                      content: msg,
         
     | 
| 
      
 135 
     | 
    
         
            +
                    });
         
     | 
| 
      
 136 
     | 
    
         
            +
                    await context.send(response.message);
         
     | 
| 
      
 137 
     | 
    
         
            +
                  }
         
     | 
| 
      
 138 
     | 
    
         
            +
                } else {
         
     | 
| 
      
 139 
     | 
    
         
            +
                  await context.send(message);
         
     | 
| 
      
 140 
     | 
    
         
            +
                }
         
     | 
| 
      
 141 
     | 
    
         
            +
              }
         
     | 
| 
      
 142 
     | 
    
         
            +
            }
         
     | 
| 
      
 143 
     | 
    
         
            +
            export function parseMarkdown(message: string) {
         
     | 
| 
      
 144 
     | 
    
         
            +
              let trimmedMessage = message;
         
     | 
| 
      
 145 
     | 
    
         
            +
              // Remove bold and underline markdown
         
     | 
| 
      
 146 
     | 
    
         
            +
              trimmedMessage = trimmedMessage?.replace(/(\*\*|__)(.*?)\1/g, "$2");
         
     | 
| 
      
 147 
     | 
    
         
            +
              // Remove markdown links, keeping only the URL
         
     | 
| 
      
 148 
     | 
    
         
            +
              trimmedMessage = trimmedMessage?.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$2");
         
     | 
| 
      
 149 
     | 
    
         
            +
              // Remove markdown headers
         
     | 
| 
      
 150 
     | 
    
         
            +
              trimmedMessage = trimmedMessage?.replace(/^#+\s*(.*)$/gm, "$1");
         
     | 
| 
      
 151 
     | 
    
         
            +
              // Remove inline code formatting
         
     | 
| 
      
 152 
     | 
    
         
            +
              trimmedMessage = trimmedMessage?.replace(/`([^`]+)`/g, "$1");
         
     | 
| 
      
 153 
     | 
    
         
            +
              // Remove single backticks at the start or end of the message
         
     | 
| 
      
 154 
     | 
    
         
            +
              trimmedMessage = trimmedMessage?.replace(/^`|`$/g, "");
         
     | 
| 
      
 155 
     | 
    
         
            +
              // Remove leading and trailing whitespace
         
     | 
| 
      
 156 
     | 
    
         
            +
              trimmedMessage = trimmedMessage?.replace(/^\s+|\s+$/g, "");
         
     | 
| 
      
 157 
     | 
    
         
            +
              // Remove any remaining leading or trailing whitespace
         
     | 
| 
      
 158 
     | 
    
         
            +
              trimmedMessage = trimmedMessage.trim();
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
              return trimmedMessage;
         
     | 
| 
      
 161 
     | 
    
         
            +
            }
         
     | 
| 
         @@ -48,7 +48,7 @@ export const chatMemory = new ChatMemory(); 
     | 
|
| 
       48 
48 
     | 
    
         
             
            let chatHistories: ChatHistories = {};
         
     | 
| 
       49 
49 
     | 
    
         
             
            export const PROMPT_RULES = `You are a helpful and playful agent called {NAME} that lives inside a web3 messaging app called Converse.
         
     | 
| 
       50 
50 
     | 
    
         
             
            - You can respond with multiple messages if needed. Each message should be separated by a newline character.
         
     | 
| 
       51 
     | 
    
         
            -
            - You can trigger  
     | 
| 
      
 51 
     | 
    
         
            +
            - You can trigger skills by only sending the command in a newline message.
         
     | 
| 
       52 
52 
     | 
    
         
             
            - Never announce actions without using a command separated by a newline character.
         
     | 
| 
       53 
53 
     | 
    
         
             
            - Dont answer in markdown format, just answer in plaintext.
         
     | 
| 
       54 
54 
     | 
    
         
             
            - Do not make guesses or assumptions
         
     | 
| 
         @@ -81,7 +81,7 @@ export async function agentResponse( 
     | 
|
| 
       81 
81 
     | 
    
         
             
                  userPrompt,
         
     | 
| 
       82 
82 
     | 
    
         
             
                  systemPrompt,
         
     | 
| 
       83 
83 
     | 
    
         
             
                );
         
     | 
| 
       84 
     | 
    
         
            -
                await  
     | 
| 
      
 84 
     | 
    
         
            +
                await processMultilineResponse(sender.address, reply, context);
         
     | 
| 
       85 
85 
     | 
    
         
             
              } catch (error) {
         
     | 
| 
       86 
86 
     | 
    
         
             
                console.error("Error during OpenAI call:", error);
         
     | 
| 
       87 
87 
     | 
    
         
             
                await context.reply("An error occurred while processing your request.");
         
     | 
| 
         @@ -126,7 +126,7 @@ export async function textGeneration( 
     | 
|
| 
       126 
126 
     | 
    
         
             
              }
         
     | 
| 
       127 
127 
     | 
    
         
             
            }
         
     | 
| 
       128 
128 
     | 
    
         | 
| 
       129 
     | 
    
         
            -
            export async function  
     | 
| 
      
 129 
     | 
    
         
            +
            export async function processMultilineResponse(
         
     | 
| 
       130 
130 
     | 
    
         
             
              address: string,
         
     | 
| 
       131 
131 
     | 
    
         
             
              reply: string,
         
     | 
| 
       132 
132 
     | 
    
         
             
              context: any,
         
     | 
| 
         @@ -1,5 +1,6 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            import { Client } from "@xmtp/xmtp-js";
         
     | 
| 
      
 1 
     | 
    
         
            +
            import type { Client } from "@xmtp/xmtp-js";
         
     | 
| 
       2 
2 
     | 
    
         
             
            import { isAddress } from "viem";
         
     | 
| 
      
 3 
     | 
    
         
            +
            import type { HandlerContext } from "@xmtp/message-kit";
         
     | 
| 
       3 
4 
     | 
    
         | 
| 
       4 
5 
     | 
    
         
             
            export const converseEndpointURL =
         
     | 
| 
       5 
6 
     | 
    
         
             
              "https://converse-website-git-endpoit-ephemerahq.vercel.app";
         
     | 
| 
         @@ -16,8 +17,8 @@ export type ConverseProfile = { 
     | 
|
| 
       16 
17 
     | 
    
         
             
            export type UserInfo = {
         
     | 
| 
       17 
18 
     | 
    
         
             
              ensDomain?: string | undefined;
         
     | 
| 
       18 
19 
     | 
    
         
             
              address?: string | undefined;
         
     | 
| 
      
 20 
     | 
    
         
            +
              preferredName: string | undefined;
         
     | 
| 
       19 
21 
     | 
    
         
             
              converseUsername?: string | undefined;
         
     | 
| 
       20 
     | 
    
         
            -
              preferredName?: string | undefined;
         
     | 
| 
       21 
22 
     | 
    
         
             
              ensInfo?: EnsData | undefined;
         
     | 
| 
       22 
23 
     | 
    
         
             
              avatar?: string | undefined;
         
     | 
| 
       23 
24 
     | 
    
         
             
            };
         
     | 
| 
         @@ -48,12 +49,14 @@ export const clearInfoCache = () => { 
     | 
|
| 
       48 
49 
     | 
    
         
             
            export const getUserInfo = async (
         
     | 
| 
       49 
50 
     | 
    
         
             
              key: string,
         
     | 
| 
       50 
51 
     | 
    
         
             
              clientAddress?: string,
         
     | 
| 
      
 52 
     | 
    
         
            +
              context?: HandlerContext,
         
     | 
| 
       51 
53 
     | 
    
         
             
            ): Promise<UserInfo | null> => {
         
     | 
| 
       52 
54 
     | 
    
         
             
              let data: UserInfo = infoCache.get(key) || {
         
     | 
| 
       53 
55 
     | 
    
         
             
                ensDomain: undefined,
         
     | 
| 
       54 
56 
     | 
    
         
             
                address: undefined,
         
     | 
| 
       55 
57 
     | 
    
         
             
                converseUsername: undefined,
         
     | 
| 
       56 
58 
     | 
    
         
             
                ensInfo: undefined,
         
     | 
| 
      
 59 
     | 
    
         
            +
                preferredName: undefined,
         
     | 
| 
       57 
60 
     | 
    
         
             
              };
         
     | 
| 
       58 
61 
     | 
    
         
             
              if (isAddress(clientAddress || "")) {
         
     | 
| 
       59 
62 
     | 
    
         
             
                data.address = clientAddress;
         
     | 
| 
         @@ -74,12 +77,15 @@ export const getUserInfo = async ( 
     | 
|
| 
       74 
77 
     | 
    
         
             
              } else {
         
     | 
| 
       75 
78 
     | 
    
         
             
                data.converseUsername = key;
         
     | 
| 
       76 
79 
     | 
    
         
             
              }
         
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
      
 80 
     | 
    
         
            +
              data.preferredName = data.ensDomain || data.converseUsername || "Friend";
         
     | 
| 
       78 
81 
     | 
    
         
             
              let keyToUse = data.address || data.ensDomain || data.converseUsername;
         
     | 
| 
       79 
82 
     | 
    
         
             
              let cacheData = keyToUse && infoCache.get(keyToUse);
         
     | 
| 
       80 
     | 
    
         
            -
              console.log("Getting user info", { cacheData, keyToUse, data });
         
     | 
| 
      
 83 
     | 
    
         
            +
              //console.log("Getting user info", { cacheData, keyToUse, data });
         
     | 
| 
       81 
84 
     | 
    
         
             
              if (cacheData) return cacheData;
         
     | 
| 
       82 
85 
     | 
    
         | 
| 
      
 86 
     | 
    
         
            +
              context?.send(
         
     | 
| 
      
 87 
     | 
    
         
            +
                "Hey there! Give me a sec while I fetch info about you first...",
         
     | 
| 
      
 88 
     | 
    
         
            +
              );
         
     | 
| 
       83 
89 
     | 
    
         
             
              if (keyToUse?.includes(".eth")) {
         
     | 
| 
       84 
90 
     | 
    
         
             
                const response = await fetch(`https://ensdata.net/${keyToUse}`);
         
     | 
| 
       85 
91 
     | 
    
         
             
                const ensData: EnsData = (await response.json()) as EnsData;
         
     | 
| 
         @@ -109,6 +115,8 @@ export const getUserInfo = async ( 
     | 
|
| 
       109 
115 
     | 
    
         
             
                data.address = converseData?.address || undefined;
         
     | 
| 
       110 
116 
     | 
    
         
             
                data.avatar = converseData?.avatar || undefined;
         
     | 
| 
       111 
117 
     | 
    
         
             
              }
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
              data.preferredName = data.ensDomain || data.converseUsername || "Friend";
         
     | 
| 
       112 
120 
     | 
    
         
             
              if (data.address) infoCache.set(data.address, data);
         
     | 
| 
       113 
121 
     | 
    
         
             
              return data;
         
     | 
| 
       114 
122 
     | 
    
         
             
            };
         
     | 
| 
         @@ -123,7 +131,8 @@ export const isOnXMTP = async ( 
     | 
|
| 
       123 
131 
     | 
    
         | 
| 
       124 
132 
     | 
    
         
             
            export const PROMPT_USER_CONTENT = (userInfo: UserInfo) => {
         
     | 
| 
       125 
133 
     | 
    
         
             
              let { address, ensDomain, converseUsername, preferredName } = userInfo;
         
     | 
| 
       126 
     | 
    
         
            -
              let prompt = ` 
     | 
| 
      
 134 
     | 
    
         
            +
              let prompt = `
         
     | 
| 
      
 135 
     | 
    
         
            +
            User context: 
         
     | 
| 
       127 
136 
     | 
    
         
             
            - Start by fetch their domain from or Convese username
         
     | 
| 
       128 
137 
     | 
    
         
             
            - Call the user by their name or domain, in case they have one
         
     | 
| 
       129 
138 
     | 
    
         
             
            - Ask for a name (if they don't have one) so you can suggest domains.
         
     | 
| 
         @@ -132,5 +141,11 @@ export const PROMPT_USER_CONTENT = (userInfo: UserInfo) => { 
     | 
|
| 
       132 
141 
     | 
    
         
             
              if (ensDomain) prompt += `\n- User ENS domain is: ${ensDomain}`;
         
     | 
| 
       133 
142 
     | 
    
         
             
              if (converseUsername)
         
     | 
| 
       134 
143 
     | 
    
         
             
                prompt += `\n- Converse username is: ${converseUsername}`;
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
              prompt = prompt.replace("{ADDRESS}", address || "");
         
     | 
| 
      
 146 
     | 
    
         
            +
              prompt = prompt.replace("{ENS_DOMAIN}", ensDomain || "");
         
     | 
| 
      
 147 
     | 
    
         
            +
              prompt = prompt.replace("{CONVERSE_USERNAME}", converseUsername || "");
         
     | 
| 
      
 148 
     | 
    
         
            +
              prompt = prompt.replace("{PREFERRED_NAME}", preferredName || "");
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
       135 
150 
     | 
    
         
             
              return prompt;
         
     | 
| 
       136 
151 
     | 
    
         
             
            };
         
     |