@hybrd/xmtp 1.4.3 โ†’ 1.4.4

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/dist/index.cjs CHANGED
@@ -683,89 +683,11 @@ function XMTPPlugin() {
683
683
  const xmtpClient = await createXMTPClient(
684
684
  XMTP_WALLET_KEY
685
685
  );
686
- async function startNodeStream() {
687
- try {
688
- import_utils2.logger.debug("\u{1F3A7} XMTP node client stream initializing");
689
- const stream = await xmtpClient.conversations.streamAllMessages();
690
- import_utils2.logger.debug("\u{1F3A7} XMTP node client stream started");
691
- for await (const msg of stream) {
692
- try {
693
- if (msg.senderInboxId === xmtpClient.inboxId) continue;
694
- const content = typeof msg.content === "string" ? msg.content : (() => {
695
- try {
696
- return JSON.stringify(msg.content);
697
- } catch {
698
- return String(msg.content);
699
- }
700
- })();
701
- const conversation = await xmtpClient.conversations.getConversationById(
702
- msg.conversationId
703
- );
704
- if (!conversation) {
705
- import_utils2.logger.warn(
706
- `\u26A0\uFE0F XMTP conversation not found: ${msg.conversationId}`
707
- );
708
- continue;
709
- }
710
- const messages = [
711
- {
712
- id: (0, import_node_crypto2.randomUUID)(),
713
- role: "user",
714
- parts: [{ type: "text", text: content }]
715
- }
716
- ];
717
- const baseRuntime = {
718
- conversation,
719
- message: msg,
720
- xmtpClient
721
- };
722
- const runtime = await agent.createRuntimeContext(baseRuntime);
723
- if (pluginContext.behaviors) {
724
- const behaviorContext2 = {
725
- runtime,
726
- client: xmtpClient,
727
- conversation,
728
- message: msg
729
- };
730
- await pluginContext.behaviors.executeBefore(behaviorContext2);
731
- if (behaviorContext2.sendOptions?.filtered) {
732
- continue;
733
- }
734
- }
735
- const { text } = await agent.generate(messages, { runtime });
736
- const behaviorContext = {
737
- runtime,
738
- client: xmtpClient,
739
- conversation,
740
- message: msg,
741
- response: text
742
- };
743
- if (pluginContext.behaviors) {
744
- await pluginContext.behaviors.executeAfter(behaviorContext);
745
- }
746
- if (behaviorContext?.sendOptions?.filtered) {
747
- import_utils2.logger.debug(
748
- `\u{1F507} [XMTP Plugin] Skipping response due to message being filtered`
749
- );
750
- return;
751
- }
752
- await sendResponse(conversation, text, msg.id, behaviorContext);
753
- } catch (err) {
754
- import_utils2.logger.error("\u274C Error processing XMTP message:", err);
755
- }
756
- }
757
- } catch (err) {
758
- import_utils2.logger.error("\u274C XMTP node client stream failed:", err);
759
- }
760
- }
761
- const enabledFromEnv = process.env.XMTP_ENABLE_NODE_STREAM;
762
- const isNodeStreamEnabled = enabledFromEnv === void 0 ? true : !/^(false|0|off|no)$/i.test(String(enabledFromEnv));
763
- if (isNodeStreamEnabled) void startNodeStream();
764
686
  const address = user.account.address.toLowerCase();
765
687
  const agentDbPath = await getDbPath(
766
688
  `agent-${XMTP_ENV || "dev"}-${address}`
767
689
  );
768
- import_utils2.logger.debug(`\u{1F4C1} Using agent listener database path: ${agentDbPath}`);
690
+ import_utils2.logger.debug(`\u{1F4C1} Using database path: ${agentDbPath}`);
769
691
  const xmtp = await import_agent_sdk.Agent.create(signer, {
770
692
  env: XMTP_ENV,
771
693
  dbPath: agentDbPath
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/client.ts","../scripts/revoke-installations.ts","../src/plugin.ts","../src/lib/jwt.ts"],"sourcesContent":["export {\n\tAgent,\n\tcreateSigner,\n\tcreateUser,\n\tfilter,\n\tgetTestUrl\n} from \"@xmtp/agent-sdk\"\n\nexport type * from \"./types\"\n\nexport {\n\tDEFAULT_AMOUNT,\n\tDEFAULT_OPTIONS,\n\tMAX_USDC_AMOUNT\n} from \"./constants\"\n// NodeNext/Node16 requires explicit extensions for relative imports\n// NodeNext/Node16 requires explicit extensions for relative imports\n\n// ===================================================================\n// XMTP Client and Connection Management\n// ===================================================================\nexport {\n createXMTPClient,\n createSigner as createXMTPSigner,\n logAgentDetails,\n validateEnvironment,\n XMTPConnectionManager\n} from \"./client\"\nexport type { XMTPConnectionConfig } from \"./client\"\n\n// ===================================================================\n// XMTP Plugin for Agent Integration\n// ===================================================================\nexport { XMTPPlugin } from \"./plugin\"\nexport type { Plugin } from \"./plugin\"\n\n// ===================================================================\n// JWT Utilities for XMTP Tools\n// ===================================================================\nexport { generateXMTPToolsToken } from \"./lib/jwt\"\nexport type { XMTPToolsPayload } from \"./lib/jwt\"\n\n// ===================================================================\n// XMTP Core SDK Exports\n// ===================================================================\nexport {\n\tClient,\n\tIdentifierKind,\n\t// type Conversation,\n\ttype DecodedMessage,\n\ttype Dm,\n\t// type Group,\n\ttype LogLevel,\n\ttype Signer,\n\ttype XmtpEnv\n} from \"@xmtp/node-sdk\"\n\n// ===================================================================\n// XMTP Content Types\n// ===================================================================\nexport {\n\tContentTypeTransactionReference,\n\ttype TransactionReference\n} from \"@xmtp/content-type-transaction-reference\"\n\nexport { ContentTypeText, type TextParameters } from \"@xmtp/content-type-text\"\n\nexport {\n\tContentTypeReaction,\n\ttype Reaction\n} from \"@xmtp/content-type-reaction\"\n\nexport {\n\tContentTypeReply,\n\tReplyCodec,\n\ttype Reply\n} from \"@xmtp/content-type-reply\"\n\nexport {\n\tContentTypeGroupUpdated,\n\tGroupUpdatedCodec,\n\ttype GroupUpdated\n} from \"@xmtp/content-type-group-updated\"\n\nexport {\n\tContentTypeWalletSendCalls,\n\ttype WalletSendCallsParams\n} from \"@xmtp/content-type-wallet-send-calls\"\n","// ===================================================================\n// Betting Configuration\n// ===================================================================\nexport const DEFAULT_OPTIONS = [\"yes\", \"no\"]\nexport const DEFAULT_AMOUNT = \"0.1\"\nexport const MAX_USDC_AMOUNT = 10 // Maximum allowed USDC transaction amount\n","import { logger } from \"@hybrd/utils\"\nimport { ReactionCodec } from \"@xmtp/content-type-reaction\"\nimport { ReplyCodec } from \"@xmtp/content-type-reply\"\nimport { TransactionReferenceCodec } from \"@xmtp/content-type-transaction-reference\"\nimport { WalletSendCallsCodec } from \"@xmtp/content-type-wallet-send-calls\"\nimport { Client, IdentifierKind, type Signer, XmtpEnv } from \"@xmtp/node-sdk\"\nimport { getRandomValues } from \"node:crypto\"\nimport fs from \"node:fs\"\nimport path from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport { fromString, toString as uint8arraysToString } from \"uint8arrays\"\nimport { createWalletClient, http, toBytes } from \"viem\"\nimport { privateKeyToAccount } from \"viem/accounts\"\nimport { sepolia } from \"viem/chains\"\nimport { revokeOldInstallations } from \"../scripts/revoke-installations\"\nimport { XmtpClient } from \"./types\"\n\n// ===================================================================\n// Module Setup\n// ===================================================================\n// ES module equivalent of __dirname\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = path.dirname(__filename)\n\n// ===================================================================\n// Type Definitions\n// ===================================================================\ninterface User {\n\tkey: `0x${string}`\n\taccount: ReturnType<typeof privateKeyToAccount>\n\twallet: any // Simplified to avoid deep type instantiation\n}\n\n// ===================================================================\n// User and Signer Creation\n// ===================================================================\nexport const createUser = (key: string): User => {\n\tconst account = privateKeyToAccount(key as `0x${string}`)\n\treturn {\n\t\tkey: key as `0x${string}`,\n\t\taccount,\n\t\twallet: createWalletClient({\n\t\t\taccount,\n\t\t\tchain: sepolia,\n\t\t\ttransport: http()\n\t\t})\n\t}\n}\n\nexport const createSigner = (key: string): Signer => {\n\tif (!key || typeof key !== \"string\") {\n\t\tthrow new Error(\"XMTP wallet key must be a non-empty string\")\n\t}\n\tconst sanitizedKey = key.startsWith(\"0x\") ? key : `0x${key}`\n\tconst user = createUser(sanitizedKey)\n\treturn {\n\t\ttype: \"EOA\",\n\t\tgetIdentifier: () => ({\n\t\t\tidentifierKind: 0 as IdentifierKind.Ethereum, // Use numeric value to avoid ambient const enum issue\n\t\t\tidentifier: user.account.address.toLowerCase()\n\t\t}),\n\t\tsignMessage: async (message: string) => {\n\t\t\tconst signature = await user.wallet.signMessage({\n\t\t\t\tmessage,\n\t\t\t\taccount: user.account\n\t\t\t})\n\t\t\treturn toBytes(signature)\n\t\t}\n\t}\n}\n\n// XMTP XmtpClient setup\n// const xmtpClient: XmtpClient | null = null\n\n// Function to clear XMTP database when hitting installation limits\nasync function clearXMTPDatabase(address: string, env: string) {\n\tlogger.debug(\"๐Ÿงน Clearing XMTP database to resolve installation limit...\")\n\n\t// Get the storage directory using the same logic as getDbPath\n\tconst getStorageDirectory = () => {\n\t\tconst customStoragePath = process.env.XMTP_STORAGE_PATH\n\n\t\tif (customStoragePath) {\n\t\t\treturn path.isAbsolute(customStoragePath)\n\t\t\t\t? customStoragePath\n\t\t\t\t: path.resolve(process.cwd(), customStoragePath)\n\t\t}\n\n\t\t// Use existing logic as fallback\n\t\tconst projectRoot =\n\t\t\tprocess.env.PROJECT_ROOT || path.resolve(__dirname, \"../../..\")\n\n\t\treturn path.join(projectRoot, \".data/xmtp\") // Local development\n\t}\n\n\t// Clear local database files\n\tconst dbPattern = `${env}-${address}.db3`\n\tconst storageDir = getStorageDirectory()\n\n\t// Primary storage directory\n\tconst possiblePaths = [\n\t\tstorageDir,\n\t\t// Legacy fallback paths for backward compatibility\n\t\tpath.join(process.cwd(), \".data\", \"xmtp\"),\n\t\tpath.join(process.cwd(), \"..\", \".data\", \"xmtp\"),\n\t\tpath.join(process.cwd(), \"..\", \"..\", \".data\", \"xmtp\")\n\t]\n\n\tfor (const dir of possiblePaths) {\n\t\ttry {\n\t\t\tif (fs.existsSync(dir)) {\n\t\t\t\tconst files = fs.readdirSync(dir)\n\t\t\t\tconst matchingFiles = files.filter(\n\t\t\t\t\t(file) =>\n\t\t\t\t\t\tfile.includes(dbPattern) ||\n\t\t\t\t\t\tfile.includes(address) ||\n\t\t\t\t\t\tfile.includes(`xmtp-${env}-${address}`)\n\t\t\t\t)\n\n\t\t\t\tfor (const file of matchingFiles) {\n\t\t\t\t\tconst fullPath = path.join(dir, file)\n\t\t\t\t\ttry {\n\t\t\t\t\t\tfs.unlinkSync(fullPath)\n\t\t\t\t\t\tlogger.debug(`โœ… Removed: ${fullPath}`)\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tlogger.debug(`โš ๏ธ Could not remove ${fullPath}:`, err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (err) {\n\t\t\t// Ignore errors when checking directories\n\t\t}\n\t}\n}\n\nexport async function createXMTPClient(\n\tprivateKey: string,\n\topts?: {\n\t\tpersist?: boolean\n\t\tmaxRetries?: number\n\t\tstoragePath?: string\n\t}\n): Promise<XmtpClient> {\n\tconst { persist = true, maxRetries = 3, storagePath } = opts ?? {}\n\tlet attempt = 0\n\n\t// Extract common variables for error handling\n\t// const actualSigner = signer\n\tconst signer = createSigner(privateKey)\n\n\tif (!signer) {\n\t\tthrow new Error(\n\t\t\t\"No signer provided and XMTP_WALLET_KEY environment variable is not set\"\n\t\t)\n\t}\n\n\tconst { XMTP_DB_ENCRYPTION_KEY, XMTP_ENV } = process.env\n\n\t// Get the wallet address to use the correct database\n\tconst identifier = await signer.getIdentifier()\n\tconst address = identifier.identifier\n\n\twhile (attempt < maxRetries) {\n\t\ttry {\n\t\t\tlogger.debug(\n\t\t\t\t`๐Ÿ”„ Attempt ${attempt + 1}/${maxRetries} to create XMTP client...`\n\t\t\t)\n\n\t\t\t// Always require encryption key and persistence - no stateless mode\n\t\t\tif (!persist) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Stateless mode is not supported. XMTP client must run in persistent mode \" +\n\t\t\t\t\t\t\"to properly receive and process messages. Set persist: true or remove the persist option \" +\n\t\t\t\t\t\t\"to use the default persistent mode.\"\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tif (!XMTP_DB_ENCRYPTION_KEY) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"XMTP_DB_ENCRYPTION_KEY must be set for persistent mode\"\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tconst dbEncryptionKey = getEncryptionKeyFromHex(XMTP_DB_ENCRYPTION_KEY)\n\t\t\tconst dbPath = await getDbPath(\n\t\t\t\t`${XMTP_ENV || \"dev\"}-${address}`,\n\t\t\t\tstoragePath\n\t\t\t)\n\t\t\tlogger.debug(`๐Ÿ“ Using database path: ${dbPath}`)\n\n\t\t\t// Always create a fresh client and sync it\n\t\t\tconst client = await Client.create(signer, {\n\t\t\t\tdbEncryptionKey,\n\t\t\t\tenv: XMTP_ENV as XmtpEnv,\n\t\t\t\tdbPath,\n\t\t\t\tcodecs: [\n\t\t\t\t\tnew ReplyCodec(),\n\t\t\t\t\tnew ReactionCodec(),\n\t\t\t\t\tnew WalletSendCallsCodec(),\n\t\t\t\t\tnew TransactionReferenceCodec()\n\t\t\t\t]\n\t\t\t})\n\n\t\t\t// Force sync conversations to ensure we have the latest data\n\t\t\tlogger.debug(\"๐Ÿ“ก Syncing conversations to ensure latest state...\")\n\t\t\tawait client.conversations.sync()\n\n\t\t\tawait backupDbToPersistentStorage(\n\t\t\t\tdbPath,\n\t\t\t\t`${XMTP_ENV || \"dev\"}-${address}`\n\t\t\t)\n\n\t\t\tconsole.log(`Wallet: ${address}`)\n\t\t\tconsole.log(`Env: ${XMTP_ENV || \"dev\"}`)\n\t\t\tconsole.log(`Storage: persistent`)\n\n\t\t\treturn client as unknown as XmtpClient\n\t\t} catch (error) {\n\t\t\tattempt++\n\n\t\t\tif (\n\t\t\t\terror instanceof Error &&\n\t\t\t\terror.message.includes(\"5/5 installations\")\n\t\t\t) {\n\t\t\t\tconsole.log(\n\t\t\t\t\t`๐Ÿ’ฅ Installation limit reached (attempt ${attempt}/${maxRetries})`\n\t\t\t\t)\n\n\t\t\t\tif (attempt < maxRetries) {\n\t\t\t\t\t// Get wallet address for database clearing\n\t\t\t\t\tconst identifier = await signer.getIdentifier()\n\t\t\t\t\tconst address = identifier.identifier\n\n\t\t\t\t\t// Extract inboxId from the error message\n\t\t\t\t\tconst inboxIdMatch = error.message.match(/InboxID ([a-f0-9]+)/)\n\t\t\t\t\tconst inboxId = inboxIdMatch ? inboxIdMatch[1] : undefined\n\n\t\t\t\t\t// First try to revoke old installations\n\t\t\t\t\tconst revocationSuccess = await revokeOldInstallations(\n\t\t\t\t\t\tsigner,\n\t\t\t\t\t\tinboxId\n\t\t\t\t\t)\n\n\t\t\t\t\tif (revocationSuccess) {\n\t\t\t\t\t\tconsole.log(\"๐ŸŽฏ Installations revoked, retrying connection...\")\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\"โš ๏ธ Installation revocation failed or not needed, clearing database...\"\n\t\t\t\t\t\t)\n\t\t\t\t\t\t// Clear database as fallback\n\t\t\t\t\t\tawait clearXMTPDatabase(address, process.env.XMTP_ENV || \"dev\")\n\t\t\t\t\t}\n\n\t\t\t\t\t// Wait a bit before retrying\n\t\t\t\t\tconst delay = Math.pow(2, attempt) * 1000 // Exponential backoff\n\t\t\t\t\tconsole.log(`โณ Waiting ${delay}ms before retry...`)\n\t\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, delay))\n\t\t\t\t} else {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\"โŒ Failed to resolve installation limit after all retries\"\n\t\t\t\t\t)\n\t\t\t\t\tconsole.error(\"๐Ÿ’ก Possible solutions:\")\n\t\t\t\t\tconsole.error(\" 1. Use a different wallet (generate new keys)\")\n\t\t\t\t\tconsole.error(\" 2. Switch XMTP environments (dev <-> production)\")\n\t\t\t\t\tconsole.error(\" 3. Wait and try again later\")\n\t\t\t\t\tconsole.error(\" 4. Contact XMTP support for manual intervention\")\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t} else if (\n\t\t\t\terror instanceof Error &&\n\t\t\t\terror.message.includes(\"Association error: Missing identity update\")\n\t\t\t) {\n\t\t\t\tconsole.log(\n\t\t\t\t\t`๐Ÿ”„ Identity association error detected (attempt ${attempt}/${maxRetries})`\n\t\t\t\t)\n\n\t\t\t\tif (attempt < maxRetries) {\n\t\t\t\t\tconsole.log(\"๐Ÿ”ง Attempting automatic identity refresh...\")\n\n\t\t\t\t\t// Try to refresh identity by creating a persistent client first\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconsole.log(\"๐Ÿ“ Creating persistent client to refresh identity...\")\n\t\t\t\t\t\tconst tempEncryptionKey = XMTP_DB_ENCRYPTION_KEY\n\t\t\t\t\t\t\t? getEncryptionKeyFromHex(XMTP_DB_ENCRYPTION_KEY)\n\t\t\t\t\t\t\t: getEncryptionKeyFromHex(generateEncryptionKeyHex())\n\t\t\t\t\t\tconst tempClient = await Client.create(signer, {\n\t\t\t\t\t\t\tdbEncryptionKey: tempEncryptionKey,\n\t\t\t\t\t\t\tenv: XMTP_ENV as XmtpEnv,\n\t\t\t\t\t\t\tdbPath: await getDbPath(\n\t\t\t\t\t\t\t\t`${XMTP_ENV || \"dev\"}-${address}`,\n\t\t\t\t\t\t\t\tstoragePath\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tcodecs: [\n\t\t\t\t\t\t\t\tnew ReplyCodec(),\n\t\t\t\t\t\t\t\tnew ReactionCodec(),\n\t\t\t\t\t\t\t\tnew WalletSendCallsCodec(),\n\t\t\t\t\t\t\t\tnew TransactionReferenceCodec()\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\tconsole.log(\"๐Ÿ“ก Syncing identity and conversations...\")\n\t\t\t\t\t\tawait tempClient.conversations.sync()\n\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\"โœ… Identity refresh successful, retrying original request...\"\n\t\t\t\t\t\t)\n\n\t\t\t\t\t\t// Wait a bit before retrying\n\t\t\t\t\t\tconst delay = Math.pow(2, attempt) * 1000 // Exponential backoff\n\t\t\t\t\t\tconsole.log(`โณ Waiting ${delay}ms before retry...`)\n\t\t\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, delay))\n\t\t\t\t\t} catch (refreshError) {\n\t\t\t\t\t\tconsole.log(`โŒ Identity refresh failed:`, refreshError)\n\t\t\t\t\t\t// Continue to the retry logic\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\"โŒ Failed to resolve identity association error after all retries\"\n\t\t\t\t\t)\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\"๐Ÿ’ก Try running: pnpm with-env pnpm --filter @hybrd/xmtp refresh:identity\"\n\t\t\t\t\t)\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// For other errors, don't retry\n\t\t\t\tthrow error\n\t\t\t}\n\t\t}\n\t}\n\n\tthrow new Error(\"Max retries exceeded\")\n}\n\n// ===================================================================\n// Encryption Key Management\n// ===================================================================\n/**\n * Generate a random encryption key\n * @returns The encryption key as a hex string\n */\nexport const generateEncryptionKeyHex = () => {\n\tconst uint8Array = getRandomValues(new Uint8Array(32))\n\treturn uint8arraysToString(uint8Array, \"hex\")\n}\n\n/**\n * Get the encryption key from a hex string\n * @param hex - The hex string\n * @returns The encryption key as Uint8Array\n */\nconst getEncryptionKeyFromHex = (hex: string): Uint8Array => {\n\treturn fromString(hex, \"hex\")\n}\n\n// ===================================================================\n// Database Path Management\n// ===================================================================\nexport const getDbPath = async (description = \"xmtp\", storagePath?: string) => {\n\t// Allow custom storage path via environment variable\n\tconst customStoragePath = process.env.XMTP_STORAGE_PATH\n\n\tlet volumePath: string\n\n\tif (customStoragePath) {\n\t\t// Use custom storage path if provided\n\t\tvolumePath = path.isAbsolute(customStoragePath)\n\t\t\t? customStoragePath\n\t\t\t: path.resolve(process.cwd(), customStoragePath)\n\t} else if (storagePath) {\n\t\tvolumePath = path.isAbsolute(storagePath)\n\t\t\t? storagePath\n\t\t\t: path.resolve(process.cwd(), storagePath)\n\t} else {\n\t\t// Use existing logic as fallback\n\t\tconst projectRoot =\n\t\t\tprocess.env.PROJECT_ROOT || path.resolve(__dirname, \"../../..\")\n\n\t\t// Default storage path for local development\n\t\tvolumePath = path.join(projectRoot, \".data/xmtp\")\n\t}\n\n\tconst dbPath = `${volumePath}/${description}.db3`\n\n\tif (typeof globalThis !== \"undefined\" && \"XMTP_STORAGE\" in globalThis) {\n\t\ttry {\n\t\t\tconsole.log(`๐Ÿ“ฆ Using Cloudflare R2 storage for: ${dbPath}`)\n\n\t\t\tconst r2Bucket = (globalThis as any).XMTP_STORAGE\n\t\t\tconst remotePath = `xmtp-databases/${description}.db3`\n\n\t\t\ttry {\n\t\t\t\tconst existingObject = await r2Bucket.head(remotePath)\n\t\t\t\tif (existingObject) {\n\t\t\t\t\tconsole.log(`๐Ÿ“ฅ Downloading existing database from R2 storage...`)\n\n\t\t\t\t\tif (!fs.existsSync(volumePath)) {\n\t\t\t\t\t\tfs.mkdirSync(volumePath, { recursive: true })\n\t\t\t\t\t}\n\n\t\t\t\t\tconst object = await r2Bucket.get(remotePath)\n\t\t\t\t\tif (object) {\n\t\t\t\t\t\tconst fileData = await object.arrayBuffer()\n\t\t\t\t\t\tfs.writeFileSync(dbPath, new Uint8Array(fileData))\n\t\t\t\t\t\tconsole.log(`โœ… Database downloaded from R2 storage`)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconsole.log(`๐Ÿ“ No existing database found in R2 storage`)\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.log(`โš ๏ธ Failed to download database from R2 storage:`, error)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.log(`โš ๏ธ R2 storage not available:`, error)\n\t\t}\n\t}\n\n\tif (!fs.existsSync(volumePath)) {\n\t\tfs.mkdirSync(volumePath, { recursive: true })\n\t}\n\n\treturn dbPath\n}\n\nconst backupDbToPersistentStorage = async (\n\tdbPath: string,\n\tdescription: string\n) => {\n\tif (\n\t\ttypeof globalThis !== \"undefined\" &&\n\t\t\"XMTP_STORAGE\" in globalThis &&\n\t\tfs.existsSync(dbPath)\n\t) {\n\t\ttry {\n\t\t\tconsole.log(`๐Ÿ“ฆ Backing up database to R2 storage: ${dbPath}`)\n\n\t\t\tconst r2Bucket = (globalThis as any).XMTP_STORAGE\n\t\t\tconst remotePath = `xmtp-databases/${description}.db3`\n\n\t\t\tconst fileData = fs.readFileSync(dbPath)\n\t\t\tawait r2Bucket.put(remotePath, fileData)\n\t\t\tconsole.log(`โœ… Database backed up to R2 storage: ${remotePath}`)\n\t\t} catch (error) {\n\t\t\tconsole.log(`โš ๏ธ Failed to backup database to R2 storage:`, error)\n\t\t}\n\t}\n}\n\n// ===================================================================\n// Logging and Debugging\n// ===================================================================\nexport const logAgentDetails = async (\n\tclients: XmtpClient | XmtpClient[]\n): Promise<void> => {\n\tconst clientsByAddress = Array.isArray(clients)\n\t\t? clients.reduce<Record<string, XmtpClient[]>>((acc, XmtpClient) => {\n\t\t\t\tconst address = XmtpClient.accountIdentifier?.identifier ?? \"\"\n\t\t\t\tacc[address] = acc[address] ?? []\n\t\t\t\tacc[address].push(XmtpClient)\n\t\t\t\treturn acc\n\t\t\t}, {})\n\t\t: {\n\t\t\t\t[clients.accountIdentifier?.identifier ?? \"\"]: [clients]\n\t\t\t}\n\n\tfor (const [address, clientGroup] of Object.entries(clientsByAddress)) {\n\t\tconst firstClient = clientGroup[0]\n\t\tconst inboxId = firstClient?.inboxId\n\t\tconst environments = clientGroup\n\t\t\t.map((c) => c.options?.env ?? \"dev\")\n\t\t\t.join(\", \")\n\t\tconsole.log(`\\x1b[38;2;252;76;52m\n โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— \n โ•šโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ•‘โ•šโ•โ•โ–ˆโ–ˆโ•”โ•โ•โ•โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—\n โ•šโ–ˆโ–ˆโ–ˆโ•”โ• โ–ˆโ–ˆโ•”โ–ˆโ–ˆโ–ˆโ–ˆโ•”โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•\n โ–ˆโ–ˆโ•”โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•‘โ•šโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•”โ•โ•โ•โ• \n โ–ˆโ–ˆโ•”โ• โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•‘ โ•šโ•โ• โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ \n โ•šโ•โ• โ•šโ•โ•โ•šโ•โ• โ•šโ•โ• โ•šโ•โ• โ•šโ•โ• \n \\x1b[0m`)\n\n\t\tconst urls = [`http://xmtp.chat/dm/${address}`]\n\n\t\tconst conversations = await firstClient?.conversations.list()\n\n\t\tconsole.log(`\n โœ“ XMTP XmtpClient:\n โ€ข Address: ${address}\n โ€ข Conversations: ${conversations?.length}\n โ€ข InboxId: ${inboxId}\n โ€ข Networks: ${environments}\n ${urls.map((url) => `โ€ข URL: ${url}`).join(\"\\n\")}`)\n\t}\n}\n\n// ===================================================================\n// Environment Validation\n// ===================================================================\nexport function validateEnvironment(vars: string[]): Record<string, string> {\n\tconst missing = vars.filter((v) => !process.env[v])\n\n\tif (missing.length) {\n\t\ttry {\n\t\t\tconst envPath = path.resolve(process.cwd(), \".env\")\n\t\t\tif (fs.existsSync(envPath)) {\n\t\t\t\tconst envVars = fs\n\t\t\t\t\t.readFileSync(envPath, \"utf-8\")\n\t\t\t\t\t.split(\"\\n\")\n\t\t\t\t\t.filter((line) => line.trim() && !line.startsWith(\"#\"))\n\t\t\t\t\t.reduce<Record<string, string>>((acc, line) => {\n\t\t\t\t\t\tconst [key, ...val] = line.split(\"=\")\n\t\t\t\t\t\tif (key && val.length) acc[key.trim()] = val.join(\"=\").trim()\n\t\t\t\t\t\treturn acc\n\t\t\t\t\t}, {})\n\n\t\t\t\tmissing.forEach((v) => {\n\t\t\t\t\tif (envVars[v]) process.env[v] = envVars[v]\n\t\t\t\t})\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.error(e)\n\t\t\t/* ignore errors */\n\t\t}\n\n\t\tconst stillMissing = vars.filter((v) => !process.env[v])\n\t\tif (stillMissing.length) {\n\t\t\tconsole.error(\"Missing env vars:\", stillMissing.join(\", \"))\n\t\t\tprocess.exit(1)\n\t\t}\n\t}\n\n\treturn vars.reduce<Record<string, string>>((acc, key) => {\n\t\tacc[key] = process.env[key] as string\n\t\treturn acc\n\t}, {})\n}\n\n/**\n * Diagnose XMTP environment and identity issues (internal use only)\n */\nasync function diagnoseXMTPIdentityIssue(\n\tclient: XmtpClient,\n\tinboxId: string,\n\tenvironment: string\n): Promise<{\n\tcanResolve: boolean\n\tsuggestions: string[]\n\tdetails: Record<string, any>\n}> {\n\tconst suggestions: string[] = []\n\tconst details: Record<string, any> = {\n\t\tenvironment,\n\t\tinboxId,\n\t\ttimestamp: new Date().toISOString()\n\t}\n\n\ttry {\n\t\t// Try to resolve the inbox state\n\t\tconst inboxState = await client.preferences.inboxStateFromInboxIds([\n\t\t\tinboxId\n\t\t])\n\n\t\tif (inboxState.length === 0) {\n\t\t\tsuggestions.push(\n\t\t\t\t`Inbox ID ${inboxId} not found in ${environment} environment`\n\t\t\t)\n\t\t\tsuggestions.push(\n\t\t\t\t\"Try switching XMTP_ENV to 'dev' if currently 'production' or vice versa\"\n\t\t\t)\n\t\t\tsuggestions.push(\n\t\t\t\t\"Verify the user has created an identity on this XMTP network\"\n\t\t\t)\n\t\t\tdetails.inboxStateFound = false\n\t\t\treturn { canResolve: false, suggestions, details }\n\t\t}\n\n\t\tconst inbox = inboxState[0]\n\t\tif (!inbox) {\n\t\t\tsuggestions.push(\"Inbox state returned empty data\")\n\t\t\tdetails.inboxStateFound = false\n\t\t\treturn { canResolve: false, suggestions, details }\n\t\t}\n\n\t\tdetails.inboxStateFound = true\n\t\tdetails.identifierCount = inbox.identifiers?.length || 0\n\n\t\tif (!inbox.identifiers || inbox.identifiers.length === 0) {\n\t\t\tsuggestions.push(\"Inbox found but has no identifiers\")\n\t\t\tsuggestions.push(\"This indicates incomplete identity registration\")\n\t\t\tsuggestions.push(\"User may need to re-register their identity on XMTP\")\n\t\t\tdetails.hasIdentifiers = false\n\t\t\treturn { canResolve: false, suggestions, details }\n\t\t}\n\n\t\t// Successfully resolved\n\t\tdetails.hasIdentifiers = true\n\t\tdetails.resolvedAddress = inbox.identifiers[0]?.identifier\n\t\treturn {\n\t\t\tcanResolve: true,\n\t\t\tsuggestions: [\"Identity resolved successfully\"],\n\t\t\tdetails\n\t\t}\n\t} catch (error) {\n\t\tconst errorMessage = error instanceof Error ? error.message : String(error)\n\t\tdetails.error = errorMessage\n\n\t\tif (errorMessage.includes(\"Association error\")) {\n\t\t\tsuggestions.push(\"XMTP identity association error detected\")\n\t\t\tsuggestions.push(\n\t\t\t\t\"Check if user exists on the correct XMTP environment (dev vs production)\"\n\t\t\t)\n\t\t\tsuggestions.push(\n\t\t\t\t\"Identity may need to be recreated on the current environment\"\n\t\t\t)\n\t\t}\n\n\t\tif (errorMessage.includes(\"Missing identity update\")) {\n\t\t\tsuggestions.push(\"Missing identity updates in XMTP network\")\n\t\t\tsuggestions.push(\"This can indicate network sync issues\")\n\t\t\tsuggestions.push(\"Wait a few minutes and retry, or recreate identity\")\n\t\t}\n\n\t\tif (errorMessage.includes(\"database\") || errorMessage.includes(\"storage\")) {\n\t\t\tsuggestions.push(\"XMTP local database/storage issue\")\n\t\t\tsuggestions.push(\"Try clearing XMTP database and resyncing\")\n\t\t\tsuggestions.push(\"Check .data/xmtp directory permissions\")\n\t\t}\n\n\t\tsuggestions.push(\"Consider testing with a fresh XMTP identity\")\n\t\treturn { canResolve: false, suggestions, details }\n\t}\n}\n\n// ===================================================================\n// Enhanced Connection Management & Health Monitoring\n// ===================================================================\n\nexport interface XMTPConnectionConfig {\n\tmaxRetries?: number\n\tretryDelayMs?: number\n\thealthCheckIntervalMs?: number\n\tconnectionTimeoutMs?: number\n\treconnectOnFailure?: boolean\n}\n\nexport interface XMTPConnectionHealth {\n\tisConnected: boolean\n\tlastHealthCheck: Date\n\tconsecutiveFailures: number\n\ttotalReconnects: number\n\tavgResponseTime: number\n}\n\nexport class XMTPConnectionManager {\n\tprivate client: XmtpClient | null = null\n\tprivate privateKey: string\n\tprivate config: Required<XMTPConnectionConfig>\n\tprivate health: XMTPConnectionHealth\n\tprivate healthCheckTimer: NodeJS.Timeout | null = null\n\tprivate isReconnecting = false\n\n\tconstructor(privateKey: string, config: XMTPConnectionConfig = {}) {\n\t\tthis.privateKey = privateKey\n\t\tthis.config = {\n\t\t\tmaxRetries: config.maxRetries ?? 5,\n\t\t\tretryDelayMs: config.retryDelayMs ?? 1000,\n\t\t\thealthCheckIntervalMs: config.healthCheckIntervalMs ?? 30000,\n\t\t\tconnectionTimeoutMs: config.connectionTimeoutMs ?? 10000,\n\t\t\treconnectOnFailure: config.reconnectOnFailure ?? true\n\t\t}\n\n\t\tthis.health = {\n\t\t\tisConnected: false,\n\t\t\tlastHealthCheck: new Date(),\n\t\t\tconsecutiveFailures: 0,\n\t\t\ttotalReconnects: 0,\n\t\t\tavgResponseTime: 0\n\t\t}\n\t}\n\n\tasync connect(persist = false): Promise<XmtpClient> {\n\t\tif (this.client && this.health.isConnected) {\n\t\t\treturn this.client\n\t\t}\n\n\t\tlet attempt = 0\n\t\twhile (attempt < this.config.maxRetries) {\n\t\t\ttry {\n\t\t\t\tconsole.log(\n\t\t\t\t\t`๐Ÿ”„ XMTP connection attempt ${attempt + 1}/${this.config.maxRetries}`\n\t\t\t\t)\n\n\t\t\t\tthis.client = await createXMTPClient(this.privateKey, { persist })\n\t\t\t\tthis.health.isConnected = true\n\t\t\t\tthis.health.consecutiveFailures = 0\n\n\t\t\t\t// Start health monitoring\n\t\t\t\tthis.startHealthMonitoring()\n\n\t\t\t\tconsole.log(\"โœ… XMTP client connected successfully\")\n\t\t\t\treturn this.client\n\t\t\t} catch (error) {\n\t\t\t\tattempt++\n\t\t\t\tthis.health.consecutiveFailures++\n\n\t\t\t\tconsole.error(`โŒ XMTP connection attempt ${attempt} failed:`, error)\n\n\t\t\t\tif (attempt < this.config.maxRetries) {\n\t\t\t\t\tconst delay = this.config.retryDelayMs * Math.pow(2, attempt - 1)\n\t\t\t\t\tconsole.log(`โณ Retrying in ${delay}ms...`)\n\t\t\t\t\tawait this.sleep(delay)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t`Failed to connect to XMTP after ${this.config.maxRetries} attempts`\n\t\t)\n\t}\n\n\t// private async createClientWithTimeout(persist: boolean): Promise<XmtpClient> {\n\t// const timeoutPromise = new Promise<never>((_, reject) => {\n\t// setTimeout(\n\t// () => reject(new Error(\"Connection timeout\")),\n\t// this.config.connectionTimeoutMs\n\t// )\n\t// })\n\n\t// const clientPromise = createXMTPClient(this.signer, { persist })\n\n\t// return Promise.race([clientPromise, timeoutPromise])\n\t// }\n\n\tprivate startHealthMonitoring(): void {\n\t\tif (this.healthCheckTimer) {\n\t\t\tclearInterval(this.healthCheckTimer)\n\t\t}\n\n\t\tthis.healthCheckTimer = setInterval(() => {\n\t\t\tthis.performHealthCheck()\n\t\t}, this.config.healthCheckIntervalMs)\n\t}\n\n\tprivate async performHealthCheck(): Promise<void> {\n\t\tif (!this.client) return\n\n\t\tconst startTime = Date.now()\n\n\t\ttry {\n\t\t\t// Simple health check: try to list conversations\n\t\t\tawait this.client.conversations.list()\n\n\t\t\tconst responseTime = Date.now() - startTime\n\t\t\tthis.health.avgResponseTime =\n\t\t\t\t(this.health.avgResponseTime + responseTime) / 2\n\t\t\tthis.health.lastHealthCheck = new Date()\n\t\t\tthis.health.consecutiveFailures = 0\n\t\t\tthis.health.isConnected = true\n\n\t\t\tconsole.log(`๐Ÿ’“ XMTP health check passed (${responseTime}ms)`)\n\t\t} catch (error) {\n\t\t\tthis.health.consecutiveFailures++\n\t\t\tthis.health.isConnected = false\n\n\t\t\tconsole.error(`๐Ÿ’” XMTP health check failed:`, error)\n\n\t\t\t// Trigger reconnection if enabled\n\t\t\tif (this.config.reconnectOnFailure && !this.isReconnecting) {\n\t\t\t\tthis.handleConnectionFailure()\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async handleConnectionFailure(): Promise<void> {\n\t\tif (this.isReconnecting) return\n\n\t\tthis.isReconnecting = true\n\t\tthis.health.totalReconnects++\n\n\t\tconsole.log(\"๐Ÿ”„ XMTP connection lost, attempting to reconnect...\")\n\n\t\ttry {\n\t\t\tthis.client = null\n\t\t\tawait this.connect()\n\t\t\tconsole.log(\"โœ… XMTP reconnection successful\")\n\t\t} catch (error) {\n\t\t\tconsole.error(\"โŒ XMTP reconnection failed:\", error)\n\t\t} finally {\n\t\t\tthis.isReconnecting = false\n\t\t}\n\t}\n\n\tprivate sleep(ms: number): Promise<void> {\n\t\treturn new Promise((resolve) => setTimeout(resolve, ms))\n\t}\n\n\tgetHealth(): XMTPConnectionHealth {\n\t\treturn { ...this.health }\n\t}\n\n\tgetClient(): XmtpClient | null {\n\t\treturn this.client\n\t}\n\n\tasync disconnect(): Promise<void> {\n\t\tif (this.healthCheckTimer) {\n\t\t\tclearInterval(this.healthCheckTimer)\n\t\t\tthis.healthCheckTimer = null\n\t\t}\n\n\t\tthis.client = null\n\t\tthis.health.isConnected = false\n\t\tconsole.log(\"๐Ÿ”Œ XMTP client disconnected\")\n\t}\n}\n\n// Enhanced client creation with connection management\nexport async function createXMTPConnectionManager(\n\tprivateKey: string,\n\tconfig?: XMTPConnectionConfig\n): Promise<XMTPConnectionManager> {\n\tconst manager = new XMTPConnectionManager(privateKey, config)\n\tawait manager.connect()\n\treturn manager\n}\n\n// ===================================================================\n// User Address Resolution with Auto-Refresh\n// ===================================================================\n","import { Client, type Signer } from \"@xmtp/node-sdk\"\nimport { createSigner } from \"../src/client\"\n\n// Function to revoke old installations when hitting the limit\nexport async function revokeOldInstallations(signer: Signer, inboxId?: string) {\n\tconsole.log(\"๐Ÿ”ง Attempting to revoke old installations...\")\n\n\ttry {\n\t\t// If we don't have the inboxId, we need to extract it from a temporary client attempt\n\t\tif (!inboxId) {\n\t\t\tconsole.log(\"โ„น๏ธ No inboxId provided, cannot revoke installations\")\n\t\t\treturn false\n\t\t}\n\n\t\tconst inboxStates = await Client.inboxStateFromInboxIds(\n\t\t\t[inboxId],\n\t\t\tprocess.env.XMTP_ENV as \"dev\" | \"production\"\n\t\t)\n\n\t\tif (!inboxStates[0]) {\n\t\t\tconsole.log(\"โŒ No inbox state found for the provided inboxId\")\n\t\t\treturn false\n\t\t}\n\n\t\tconst toRevokeInstallationBytes = inboxStates[0].installations.map(\n\t\t\t(i: { bytes: Uint8Array }) => i.bytes\n\t\t)\n\n\t\tawait Client.revokeInstallations(\n\t\t\tsigner,\n\t\t\tinboxId,\n\t\t\ttoRevokeInstallationBytes,\n\t\t\tprocess.env.XMTP_ENV as \"dev\" | \"production\"\n\t\t)\n\n\t\tconst resultingStates = await Client.inboxStateFromInboxIds(\n\t\t\t[inboxId],\n\t\t\tprocess.env.XMTP_ENV as \"dev\" | \"production\"\n\t\t)\n\n\t\tconsole.log(\n\t\t\t`๐Ÿ“‹ Revoked installations: ${toRevokeInstallationBytes.length} installations`\n\t\t)\n\t\tconsole.log(\n\t\t\t`๐Ÿ“‹ Resulting state: ${resultingStates[0]?.installations.length || 0} installations`\n\t\t)\n\n\t\treturn true\n\t} catch (error) {\n\t\tconsole.error(\"โŒ Error during installation revocation:\", error)\n\t\treturn false\n\t}\n}\n\n// CLI script to revoke installations\nasync function main() {\n\tconst { XMTP_WALLET_KEY } = process.env\n\tconst inboxId = process.argv[2]\n\n\tif (!XMTP_WALLET_KEY) {\n\t\tconsole.error(\"โŒ XMTP_WALLET_KEY is required\")\n\t\tprocess.exit(1)\n\t}\n\n\tif (!inboxId) {\n\t\tconsole.error(\"โŒ InboxID is required as CLI argument\")\n\t\tconsole.error(\"Usage: tsx revoke-installations.ts <inboxId>\")\n\t\tprocess.exit(1)\n\t}\n\n\tconst signer = createSigner(XMTP_WALLET_KEY)\n\tconst identifier = await signer.getIdentifier()\n\tconst address = identifier.identifier\n\n\tconsole.log(`๐Ÿ”‘ Wallet Address: ${address}`)\n\tconsole.log(`๐Ÿ“‹ Inbox ID: ${inboxId}`)\n\n\t// Try to revoke installations\n\tconst success = await revokeOldInstallations(signer, inboxId)\n\n\tif (success) {\n\t\tconsole.log(\"โœ… Successfully revoked installations\")\n\t} else {\n\t\tconsole.log(\"โŒ Failed to revoke installations\")\n\t\tprocess.exit(1)\n\t}\n}\n\n// Run if called directly\nif (import.meta.url === `file://${process.argv[1]}`) {\n\tmain().catch((error) => {\n\t\tconsole.error(\"๐Ÿ’ฅ Fatal error:\", error)\n\t\tprocess.exit(1)\n\t})\n}\n","import {\n\tAgent as XmtpAgent,\n\tXmtpEnv,\n\tcreateSigner,\n\tcreateUser\n} from \"@xmtp/agent-sdk\"\n\nimport type {\n\tAgentMessage,\n\tAgentRuntime,\n\tBehaviorContext,\n\tBehaviorRegistry,\n\tPlugin,\n\tPluginContext,\n\tXmtpClient,\n\tXmtpConversation,\n\tXmtpMessage\n} from \"@hybrd/types\"\nimport { logger } from \"@hybrd/utils\"\nimport { randomUUID } from \"node:crypto\"\nimport { createXMTPClient, getDbPath } from \"./client\"\nimport { ContentTypeReply, ContentTypeText, type Reply } from \"./index\"\n\n// Re-export types from @hybrd/types for backward compatibility\nexport type { Plugin }\n\n/**\n * Send a response with threading support\n */\nasync function sendResponse(\n\tconversation: XmtpConversation,\n\ttext: string,\n\toriginalMessageId: string,\n\tbehaviorContext?: BehaviorContext\n) {\n\tconst shouldThread = behaviorContext?.sendOptions?.threaded ?? false\n\n\tif (shouldThread) {\n\t\t// Send as a reply to the original message\n\t\ttry {\n\t\t\tconst reply: Reply = {\n\t\t\t\treference: originalMessageId,\n\t\t\t\tcontentType: ContentTypeText,\n\t\t\t\tcontent: text\n\t\t\t}\n\t\t\tawait conversation.send(reply, ContentTypeReply)\n\t\t\tlogger.debug(\n\t\t\t\t`โœ… [sendResponse] Threaded reply sent successfully to message ${originalMessageId}`\n\t\t\t)\n\t\t} catch (error) {\n\t\t\tlogger.error(\n\t\t\t\t`โŒ [sendResponse] Failed to send threaded reply to message ${originalMessageId}:`,\n\t\t\t\terror\n\t\t\t)\n\t\t\t// Fall back to regular message if threaded reply fails\n\t\t\tlogger.debug(`๐Ÿ”„ [sendResponse] Falling back to regular message`)\n\t\t\tawait conversation.send(text)\n\t\t}\n\t} else {\n\t\t// Send as a regular message\n\t\tawait conversation.send(text)\n\t}\n}\n\n/**\n * XMTP Plugin that provides XMTP functionality to the agent\n *\n * @description\n * This plugin integrates XMTP messaging capabilities into the agent's\n * HTTP server. It mounts the XMTP endpoints for handling XMTP tools requests.\n */\nexport function XMTPPlugin(): Plugin<PluginContext> {\n\treturn {\n\t\tname: \"xmtp\",\n\t\tdescription: \"Provides XMTP messaging functionality\",\n\t\tapply: async (app, context): Promise<void> => {\n\t\t\tconst {\n\t\t\t\tXMTP_WALLET_KEY,\n\t\t\t\tXMTP_DB_ENCRYPTION_KEY,\n\t\t\t\tXMTP_ENV = \"production\"\n\t\t\t} = process.env\n\n\t\t\tconst { agent } = context\n\t\t\tconst pluginContext = context as PluginContext & {\n\t\t\t\tbehaviors?: BehaviorRegistry\n\t\t\t}\n\n\t\t\tif (!XMTP_WALLET_KEY) {\n\t\t\t\tthrow new Error(\"XMTP_WALLET_KEY must be set\")\n\t\t\t}\n\n\t\t\tif (!XMTP_DB_ENCRYPTION_KEY) {\n\t\t\t\tthrow new Error(\"XMTP_DB_ENCRYPTION_KEY must be set\")\n\t\t\t}\n\n\t\t\tconst user = createUser(XMTP_WALLET_KEY as `0x${string}`)\n\t\t\tconst signer = createSigner(user)\n\n\t\t\tconst xmtpClient = await createXMTPClient(\n\t\t\t\tXMTP_WALLET_KEY as `0x${string}`\n\t\t\t)\n\n\t\t\t// Start a reliable node client stream to process incoming messages\n\t\t\tasync function startNodeStream() {\n\t\t\t\ttry {\n\t\t\t\t\tlogger.debug(\"๐ŸŽง XMTP node client stream initializing\")\n\t\t\t\t\tconst stream = await xmtpClient.conversations.streamAllMessages()\n\t\t\t\t\tlogger.debug(\"๐ŸŽง XMTP node client stream started\")\n\t\t\t\t\tfor await (const msg of stream) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tif (msg.senderInboxId === xmtpClient.inboxId) continue\n\n\t\t\t\t\t\t\tconst content =\n\t\t\t\t\t\t\t\ttypeof msg.content === \"string\"\n\t\t\t\t\t\t\t\t\t? msg.content\n\t\t\t\t\t\t\t\t\t: (() => {\n\t\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\t\treturn JSON.stringify(msg.content)\n\t\t\t\t\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t\t\t\t\treturn String(msg.content)\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t})()\n\n\t\t\t\t\t\t\tconst conversation =\n\t\t\t\t\t\t\t\tawait xmtpClient.conversations.getConversationById(\n\t\t\t\t\t\t\t\t\tmsg.conversationId\n\t\t\t\t\t\t\t\t)\n\n\t\t\t\t\t\t\tif (!conversation) {\n\t\t\t\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t\t\t\t`โš ๏ธ XMTP conversation not found: ${msg.conversationId}`\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst messages: AgentMessage[] = [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tid: randomUUID(),\n\t\t\t\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\t\t\t\tparts: [{ type: \"text\", text: content }]\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t]\n\n\t\t\t\t\t\t\tconst baseRuntime: AgentRuntime = {\n\t\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\t\tmessage: msg,\n\t\t\t\t\t\t\t\txmtpClient\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst runtime = await agent.createRuntimeContext(baseRuntime)\n\n\t\t\t\t\t\t\t// Execute pre-response behaviors\n\t\t\t\t\t\t\tif (pluginContext.behaviors) {\n\t\t\t\t\t\t\t\tconst behaviorContext: BehaviorContext = {\n\t\t\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\t\t\tmessage: msg as XmtpMessage\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tawait pluginContext.behaviors.executeBefore(behaviorContext)\n\n\t\t\t\t\t\t\t\t// Check if message was filtered out by any behavior\n\t\t\t\t\t\t\t\tif (behaviorContext.sendOptions?.filtered) {\n\t\t\t\t\t\t\t\t\tcontinue // Skip processing this message\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst { text } = await agent.generate(messages, { runtime })\n\n\t\t\t\t\t\t\t// Create behavior context for send options\n\t\t\t\t\t\t\tconst behaviorContext: BehaviorContext = {\n\t\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\t\tmessage: msg as XmtpMessage,\n\t\t\t\t\t\t\t\tresponse: text\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Execute post-response behaviors\n\t\t\t\t\t\t\tif (pluginContext.behaviors) {\n\t\t\t\t\t\t\t\tawait pluginContext.behaviors.executeAfter(behaviorContext)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Check if message was filtered out by filterMessages behavior\n\t\t\t\t\t\t\tif (behaviorContext?.sendOptions?.filtered) {\n\t\t\t\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t\t\t\t`๐Ÿ”‡ [XMTP Plugin] Skipping response due to message being filtered`\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Send the response with threading support\n\t\t\t\t\t\t\tawait sendResponse(conversation, text, msg.id, behaviorContext)\n\t\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t\tlogger.error(\"โŒ Error processing XMTP message:\", err)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tlogger.error(\"โŒ XMTP node client stream failed:\", err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst enabledFromEnv = process.env.XMTP_ENABLE_NODE_STREAM\n\t\t\tconst isNodeStreamEnabled =\n\t\t\t\tenabledFromEnv === undefined\n\t\t\t\t\t? true\n\t\t\t\t\t: !/^(false|0|off|no)$/i.test(String(enabledFromEnv))\n\n\t\t\tif (isNodeStreamEnabled) void startNodeStream()\n\n\t\t\tconst address = user.account.address.toLowerCase()\n\t\t\tconst agentDbPath = await getDbPath(\n\t\t\t\t`agent-${XMTP_ENV || \"dev\"}-${address}`\n\t\t\t)\n\t\t\tlogger.debug(`๐Ÿ“ Using agent listener database path: ${agentDbPath}`)\n\n\t\t\tconst xmtp = await XmtpAgent.create(signer, {\n\t\t\t\tenv: XMTP_ENV as XmtpEnv,\n\t\t\t\tdbPath: agentDbPath\n\t\t\t})\n\n\t\t\txmtp.on(\"reaction\", async ({ conversation, message }) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst text = message.content.content\n\t\t\t\t\tconst messages: AgentMessage[] = [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tid: randomUUID(),\n\t\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\t\tparts: [{ type: \"text\", text }]\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\n\t\t\t\t\tconst baseRuntime: AgentRuntime = {\n\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\txmtpClient\n\t\t\t\t\t}\n\n\t\t\t\t\tconst runtime = await agent.createRuntimeContext(baseRuntime)\n\n\t\t\t\t\t// Execute pre-response behaviors\n\t\t\t\t\tif (context.behaviors) {\n\t\t\t\t\t\tconst behaviorContext: BehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait context.behaviors.executeBefore(behaviorContext)\n\t\t\t\t\t}\n\n\t\t\t\t\tconst { text: reply } = await agent.generate(messages, { runtime })\n\n\t\t\t\t\t// Execute post-response behaviors\n\t\t\t\t\tlet behaviorContext: BehaviorContext | undefined\n\t\t\t\t\tif (context.behaviors) {\n\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\t\tresponse: reply\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait context.behaviors.executeAfter(behaviorContext)\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Create minimal context for send options\n\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\t\tresponse: reply\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check if message was filtered out by filterMessages behavior\n\t\t\t\t\tif (behaviorContext?.sendOptions?.filtered) {\n\t\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t\t`๐Ÿ”‡ [XMTP Plugin] Skipping reaction response due to message being filtered`\n\t\t\t\t\t\t)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\tawait sendResponse(\n\t\t\t\t\t\tconversation as unknown as XmtpConversation,\n\t\t\t\t\t\treply,\n\t\t\t\t\t\tmessage.id,\n\t\t\t\t\t\tbehaviorContext\n\t\t\t\t\t)\n\t\t\t\t} catch (err) {\n\t\t\t\t\tlogger.error(\"โŒ Error handling reaction:\", err)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\txmtp.on(\"reply\", async ({ conversation, message }) => {\n\t\t\t\ttry {\n\t\t\t\t\t// TODO - why isn't this typed better?\n\t\t\t\t\tconst text = message.content.content as string\n\t\t\t\t\tconst messages: AgentMessage[] = [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tid: randomUUID(),\n\t\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\t\tparts: [{ type: \"text\", text }]\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\n\t\t\t\t\tconst baseRuntime: AgentRuntime = {\n\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\txmtpClient\n\t\t\t\t\t}\n\n\t\t\t\t\tconst runtime = await agent.createRuntimeContext(baseRuntime)\n\n\t\t\t\t\t// Execute pre-response behaviors\n\t\t\t\t\tlet behaviorContext: BehaviorContext | undefined\n\t\t\t\t\tif (context.behaviors) {\n\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait context.behaviors.executeBefore(behaviorContext)\n\n\t\t\t\t\t\t// Check if behaviors were stopped early (e.g., due to filtering)\n\t\t\t\t\t\tif (behaviorContext.stopped) {\n\t\t\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t\t\t`๐Ÿ”‡ [XMTP Plugin] Skipping reply response due to behavior chain being stopped`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst { text: reply } = await agent.generate(messages, { runtime })\n\n\t\t\t\t\t// Execute post-response behaviors\n\t\t\t\t\tif (context.behaviors) {\n\t\t\t\t\t\tif (!behaviorContext) {\n\t\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\t\t\tresponse: reply\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tbehaviorContext.response = reply\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait context.behaviors.executeAfter(behaviorContext)\n\n\t\t\t\t\t\t// Check if post behaviors were stopped early\n\t\t\t\t\t\tif (behaviorContext.stopped) {\n\t\t\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t\t\t`๐Ÿ”‡ [XMTP Plugin] Skipping reply response due to post-behavior chain being stopped`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Create minimal context for send options\n\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\t\tresponse: reply\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tawait sendResponse(\n\t\t\t\t\t\tconversation as unknown as XmtpConversation,\n\t\t\t\t\t\treply,\n\t\t\t\t\t\tmessage.id,\n\t\t\t\t\t\tbehaviorContext\n\t\t\t\t\t)\n\t\t\t\t} catch (err) {\n\t\t\t\t\tlogger.error(\"โŒ Error handling reply:\", err)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\txmtp.on(\"text\", async ({ conversation, message }) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst text = message.content\n\t\t\t\t\tconst messages: AgentMessage[] = [\n\t\t\t\t\t\t{ id: randomUUID(), role: \"user\", parts: [{ type: \"text\", text }] }\n\t\t\t\t\t]\n\n\t\t\t\t\tconst baseRuntime: AgentRuntime = {\n\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\txmtpClient\n\t\t\t\t\t}\n\n\t\t\t\t\tconst runtime = await agent.createRuntimeContext(baseRuntime)\n\n\t\t\t\t\t// Execute pre-response behaviors\n\t\t\t\t\tlet behaviorContext: BehaviorContext | undefined\n\t\t\t\t\tif (context.behaviors) {\n\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait context.behaviors.executeBefore(behaviorContext)\n\n\t\t\t\t\t\t// Check if behaviors were stopped early (e.g., due to filtering)\n\t\t\t\t\t\tif (behaviorContext.stopped) {\n\t\t\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t\t\t`๐Ÿ”‡ [XMTP Plugin] Skipping text response due to behavior chain being stopped`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst { text: reply } = await agent.generate(messages, { runtime })\n\n\t\t\t\t\t// Execute post-response behaviors\n\t\t\t\t\tif (context.behaviors) {\n\t\t\t\t\t\tif (!behaviorContext) {\n\t\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\t\t\tresponse: reply\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tbehaviorContext.response = reply\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait context.behaviors.executeAfter(behaviorContext)\n\n\t\t\t\t\t\t// Check if post behaviors were stopped early\n\t\t\t\t\t\tif (behaviorContext.stopped) {\n\t\t\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t\t\t`๐Ÿ”‡ [XMTP Plugin] Skipping text response due to post-behavior chain being stopped`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Create minimal context for send options\n\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\t\tresponse: reply\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tawait sendResponse(\n\t\t\t\t\t\tconversation as unknown as XmtpConversation,\n\t\t\t\t\t\treply,\n\t\t\t\t\t\tmessage.id,\n\t\t\t\t\t\tbehaviorContext\n\t\t\t\t\t)\n\t\t\t\t} catch (err) {\n\t\t\t\t\tlogger.error(\"โŒ Error handling text:\", err)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\t// Event handlers removed due to incompatibility with current XMTP agent SDK\n\n\t\t\tvoid xmtp\n\t\t\t\t.start()\n\t\t\t\t.then(() => logger.debug(\"โœ… XMTP agent listener started\"))\n\t\t\t\t.catch((err) =>\n\t\t\t\t\tconsole.error(\"โŒ XMTP agent listener failed to start:\", err)\n\t\t\t\t)\n\t\t}\n\t}\n}\n","import { Context } from \"hono\"\nimport jwt from \"jsonwebtoken\"\nimport { logger } from \"@hybrd/utils\"\n\nexport interface XMTPToolsPayload {\n\taction: \"send\" | \"reply\" | \"react\" | \"transaction\" | \"blockchain-event\"\n\tconversationId: string\n\t// Action-specific data\n\tcontent?: string\n\treferenceMessageId?: string\n\temoji?: string\n\tactionType?: \"added\" | \"removed\"\n\tfromAddress?: string\n\tchainId?: string\n\tcalls?: Array<{\n\t\tto: string\n\t\tdata: string\n\t\tmetadata?: {\n\t\t\tdescription: string\n\t\t\ttransactionType: string\n\t\t}\n\t}>\n\t// Metadata\n\tissued: number\n\texpires: number\n}\n\n/**\n * Validates token and returns payload for both GET and POST endpoints\n *\n * @param {Context} c - Hono context object containing request information\n * @returns {XMTPToolsPayload | null} The validated payload or null if invalid\n *\n * @description\n * Supports two authentication methods:\n * - Authorization header with Bearer token (for POST endpoints)\n * - Query parameter token (for GET endpoints)\n *\n * @example\n * ```typescript\n * app.post(\"/api/endpoint\", async (c) => {\n * const payload = getValidatedPayload(c);\n * if (!payload) {\n * return c.json({ error: \"Invalid token\" }, 401);\n * }\n * // Use payload data\n * });\n * ```\n */\nexport function getValidatedPayload(c: Context): XMTPToolsPayload | null {\n\t// Try Authorization header first (for POST endpoints)\n\tconst authHeader = c.req.header(\"Authorization\")\n\tif (authHeader?.startsWith(\"Bearer \")) {\n\t\tconst token = authHeader.substring(7) // Remove \"Bearer \" prefix\n\t\treturn validateXMTPToolsToken(token)\n\t}\n\n\t// Fall back to query parameter (for GET endpoints)\n\tconst token = c.req.query(\"token\")\n\tif (!token) {\n\t\treturn null\n\t}\n\n\treturn validateXMTPToolsToken(token)\n}\n\n/**\n * Gets the JWT secret for token signing, with lazy initialization\n * Uses XMTP_DB_ENCRYPTION_KEY environment variable for consistency\n * Only falls back to development secret in development/test environments\n */\nfunction getJwtSecret(): string {\n\tconst secret = process.env.XMTP_DB_ENCRYPTION_KEY\n\tconst nodeEnv = process.env.NODE_ENV || \"development\"\n\n\t// In production, require a real JWT secret\n\tif (nodeEnv === \"production\" && !secret) {\n\t\tthrow new Error(\n\t\t\t\"XMTP_DB_ENCRYPTION_KEY environment variable is required in production. \" +\n\t\t\t\t\"Generate a secure random secret for JWT token signing.\"\n\t\t)\n\t}\n\n\t// In development/test, allow fallback but warn only when actually used\n\tif (!secret) {\n\t\tlogger.warn(\n\t\t\t\"โš ๏ธ [SECURITY] Using fallback JWT secret for development. \" +\n\t\t\t\t\"Set XMTP_DB_ENCRYPTION_KEY environment variable for production.\"\n\t\t)\n\t\treturn \"fallback-secret-for-dev-only\"\n\t}\n\n\treturn secret\n}\n\n/**\n * Gets the API key for authentication, with lazy initialization\n * Requires XMTP_API_KEY environment variable in production\n * Only falls back to development key in development/test environments\n */\nfunction getApiKey(): string {\n\tconst apiKey = process.env.XMTP_API_KEY\n\tconst nodeEnv = process.env.NODE_ENV || \"development\"\n\n\t// In production, require a real API key\n\tif (nodeEnv === \"production\" && !apiKey) {\n\t\tthrow new Error(\n\t\t\t\"XMTP_API_KEY environment variable is required in production. \" +\n\t\t\t\t\"Generate a secure random API key for authentication.\"\n\t\t)\n\t}\n\n\t// In development/test, allow fallback but warn only when actually used\n\tif (!apiKey) {\n\t\tlogger.warn(\n\t\t\t\"โš ๏ธ [SECURITY] Using fallback API key for development. \" +\n\t\t\t\t\"Set XMTP_API_KEY environment variable for production.\"\n\t\t)\n\t\treturn \"fallback-api-key-for-dev-only\"\n\t}\n\n\treturn apiKey\n}\n\n/**\n * JWT token expiry time in seconds (5 minutes)\n */\nconst JWT_EXPIRY = 5 * 60 // 5 minutes in seconds\n\n/**\n * Generates a signed JWT token for XMTP tools authentication\n *\n * @param {Omit<XMTPToolsPayload, \"issued\" | \"expires\">} payload - Token payload without timestamp fields\n * @returns {string} Signed JWT token\n *\n * @description\n * Creates a JWT token with automatic timestamp fields:\n * - issued: Current timestamp\n * - expires: Current timestamp + JWT_EXPIRY\n *\n * @example\n * ```typescript\n * const token = generateXMTPToolsToken({\n * action: \"send\",\n * conversationId: \"0x123...\"\n * });\n * ```\n */\nexport function generateXMTPToolsToken(\n\tpayload: Omit<XMTPToolsPayload, \"issued\" | \"expires\">\n): string {\n\tconst startTime = performance.now()\n\tlogger.debug(\"๐Ÿ” [JWT] Starting token generation...\")\n\n\tconst now = Math.floor(Date.now() / 1000)\n\tconst fullPayload: XMTPToolsPayload = {\n\t\t...payload,\n\t\tissued: now,\n\t\texpires: now + JWT_EXPIRY\n\t}\n\n\tconst token = jwt.sign(fullPayload, getJwtSecret(), {\n\t\texpiresIn: JWT_EXPIRY\n\t})\n\n\tconst endTime = performance.now()\n\tlogger.debug(\n\t\t`๐Ÿ” [JWT] Token generation completed in ${(endTime - startTime).toFixed(2)}ms`\n\t)\n\n\treturn token\n}\n\n/**\n * Validates an XMTP tools token using either API key or JWT verification\n *\n * @param {string} token - Token to validate (either API key or JWT)\n * @returns {XMTPToolsPayload | null} Validated payload or null if invalid\n *\n * @description\n * Supports two authentication methods in order of precedence:\n * 1. API key authentication - Direct comparison with XMTP_API_KEY\n * 2. JWT token authentication - Signature verification and expiry check\n *\n * For API key authentication, returns a default payload with 1-hour expiry.\n * For JWT authentication, validates signature and checks expiry timestamp.\n *\n * @example\n * ```typescript\n * const payload = validateXMTPToolsToken(userToken);\n * if (payload) {\n * console.log(`Action: ${payload.action}`);\n * console.log(`Conversation: ${payload.conversationId}`);\n * }\n * ```\n */\nexport function validateXMTPToolsToken(token: string): XMTPToolsPayload | null {\n\tconst startTime = performance.now()\n\tlogger.debug(\"๐Ÿ” [JWT] Starting token validation...\")\n\n\t// First try API key authentication\n\tif (token === getApiKey()) {\n\t\tlogger.debug(\"๐Ÿ”‘ [Auth] Using API key authentication\")\n\t\t// Return a valid payload for API key auth\n\t\tconst now = Math.floor(Date.now() / 1000)\n\t\tconst result = {\n\t\t\taction: \"send\" as const, // Default action\n\t\t\tconversationId: \"\", // Will be filled by endpoint\n\t\t\tissued: now,\n\t\t\texpires: now + 3600 // API keys are valid for 1 hour\n\t\t}\n\n\t\tconst endTime = performance.now()\n\t\tlogger.debug(\n\t\t\t`๐Ÿ” [JWT] API key validation completed in ${(endTime - startTime).toFixed(2)}ms`\n\t\t)\n\t\treturn result\n\t}\n\n\t// Then try JWT token authentication\n\ttry {\n\t\tconst decoded = jwt.verify(token, getJwtSecret()) as XMTPToolsPayload\n\t\tlogger.debug(\"๐Ÿ”‘ [Auth] Using JWT token authentication\")\n\n\t\t// Additional expiry check\n\t\tconst now = Math.floor(Date.now() / 1000)\n\t\tif (decoded.expires < now) {\n\t\t\tconsole.log(\"๐Ÿ”’ XMTP tools token has expired\")\n\t\t\tconst endTime = performance.now()\n\t\t\tlogger.debug(\n\t\t\t\t`๐Ÿ” [JWT] Token validation failed (expired) in ${(endTime - startTime).toFixed(2)}ms`\n\t\t\t)\n\t\t\treturn null\n\t\t}\n\n\t\tconst endTime = performance.now()\n\t\tlogger.debug(\n\t\t\t`๐Ÿ” [JWT] JWT validation completed in ${(endTime - startTime).toFixed(2)}ms`\n\t\t)\n\t\treturn decoded\n\t} catch (error) {\n\t\tlogger.error(\n\t\t\t\"๐Ÿ”’ Invalid XMTP tools token and not matching API key:\",\n\t\t\terror\n\t\t)\n\t\tconst endTime = performance.now()\n\t\tlogger.debug(\n\t\t\t`๐Ÿ” [JWT] Token validation failed in ${(endTime - startTime).toFixed(2)}ms`\n\t\t)\n\t\treturn null\n\t}\n}\n"],"mappings":"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","names":["import_agent_sdk","import_node_sdk","import_meta","import_meta","path","fs","identifier","address","uint8arraysToString","import_utils","import_node_crypto","behaviorContext","XmtpAgent","import_utils","jwt","import_node_sdk","import_content_type_transaction_reference","import_content_type_reaction","import_content_type_reply","import_content_type_wallet_send_calls"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/client.ts","../scripts/revoke-installations.ts","../src/plugin.ts","../src/lib/jwt.ts"],"sourcesContent":["export {\n\tAgent,\n\tcreateSigner,\n\tcreateUser,\n\tfilter,\n\tgetTestUrl\n} from \"@xmtp/agent-sdk\"\n\nexport type * from \"./types\"\n\nexport {\n\tDEFAULT_AMOUNT,\n\tDEFAULT_OPTIONS,\n\tMAX_USDC_AMOUNT\n} from \"./constants\"\n// NodeNext/Node16 requires explicit extensions for relative imports\n// NodeNext/Node16 requires explicit extensions for relative imports\n\n// ===================================================================\n// XMTP Client and Connection Management\n// ===================================================================\nexport {\n createXMTPClient,\n createSigner as createXMTPSigner,\n logAgentDetails,\n validateEnvironment,\n XMTPConnectionManager\n} from \"./client\"\nexport type { XMTPConnectionConfig } from \"./client\"\n\n// ===================================================================\n// XMTP Plugin for Agent Integration\n// ===================================================================\nexport { XMTPPlugin } from \"./plugin\"\nexport type { Plugin } from \"./plugin\"\n\n// ===================================================================\n// JWT Utilities for XMTP Tools\n// ===================================================================\nexport { generateXMTPToolsToken } from \"./lib/jwt\"\nexport type { XMTPToolsPayload } from \"./lib/jwt\"\n\n// ===================================================================\n// XMTP Core SDK Exports\n// ===================================================================\nexport {\n\tClient,\n\tIdentifierKind,\n\t// type Conversation,\n\ttype DecodedMessage,\n\ttype Dm,\n\t// type Group,\n\ttype LogLevel,\n\ttype Signer,\n\ttype XmtpEnv\n} from \"@xmtp/node-sdk\"\n\n// ===================================================================\n// XMTP Content Types\n// ===================================================================\nexport {\n\tContentTypeTransactionReference,\n\ttype TransactionReference\n} from \"@xmtp/content-type-transaction-reference\"\n\nexport { ContentTypeText, type TextParameters } from \"@xmtp/content-type-text\"\n\nexport {\n\tContentTypeReaction,\n\ttype Reaction\n} from \"@xmtp/content-type-reaction\"\n\nexport {\n\tContentTypeReply,\n\tReplyCodec,\n\ttype Reply\n} from \"@xmtp/content-type-reply\"\n\nexport {\n\tContentTypeGroupUpdated,\n\tGroupUpdatedCodec,\n\ttype GroupUpdated\n} from \"@xmtp/content-type-group-updated\"\n\nexport {\n\tContentTypeWalletSendCalls,\n\ttype WalletSendCallsParams\n} from \"@xmtp/content-type-wallet-send-calls\"\n","// ===================================================================\n// Betting Configuration\n// ===================================================================\nexport const DEFAULT_OPTIONS = [\"yes\", \"no\"]\nexport const DEFAULT_AMOUNT = \"0.1\"\nexport const MAX_USDC_AMOUNT = 10 // Maximum allowed USDC transaction amount\n","import { logger } from \"@hybrd/utils\"\nimport { ReactionCodec } from \"@xmtp/content-type-reaction\"\nimport { ReplyCodec } from \"@xmtp/content-type-reply\"\nimport { TransactionReferenceCodec } from \"@xmtp/content-type-transaction-reference\"\nimport { WalletSendCallsCodec } from \"@xmtp/content-type-wallet-send-calls\"\nimport { Client, IdentifierKind, type Signer, XmtpEnv } from \"@xmtp/node-sdk\"\nimport { getRandomValues } from \"node:crypto\"\nimport fs from \"node:fs\"\nimport path from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport { fromString, toString as uint8arraysToString } from \"uint8arrays\"\nimport { createWalletClient, http, toBytes } from \"viem\"\nimport { privateKeyToAccount } from \"viem/accounts\"\nimport { sepolia } from \"viem/chains\"\nimport { revokeOldInstallations } from \"../scripts/revoke-installations\"\nimport { XmtpClient } from \"./types\"\n\n// ===================================================================\n// Module Setup\n// ===================================================================\n// ES module equivalent of __dirname\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = path.dirname(__filename)\n\n// ===================================================================\n// Type Definitions\n// ===================================================================\ninterface User {\n\tkey: `0x${string}`\n\taccount: ReturnType<typeof privateKeyToAccount>\n\twallet: any // Simplified to avoid deep type instantiation\n}\n\n// ===================================================================\n// User and Signer Creation\n// ===================================================================\nexport const createUser = (key: string): User => {\n\tconst account = privateKeyToAccount(key as `0x${string}`)\n\treturn {\n\t\tkey: key as `0x${string}`,\n\t\taccount,\n\t\twallet: createWalletClient({\n\t\t\taccount,\n\t\t\tchain: sepolia,\n\t\t\ttransport: http()\n\t\t})\n\t}\n}\n\nexport const createSigner = (key: string): Signer => {\n\tif (!key || typeof key !== \"string\") {\n\t\tthrow new Error(\"XMTP wallet key must be a non-empty string\")\n\t}\n\tconst sanitizedKey = key.startsWith(\"0x\") ? key : `0x${key}`\n\tconst user = createUser(sanitizedKey)\n\treturn {\n\t\ttype: \"EOA\",\n\t\tgetIdentifier: () => ({\n\t\t\tidentifierKind: 0 as IdentifierKind.Ethereum, // Use numeric value to avoid ambient const enum issue\n\t\t\tidentifier: user.account.address.toLowerCase()\n\t\t}),\n\t\tsignMessage: async (message: string) => {\n\t\t\tconst signature = await user.wallet.signMessage({\n\t\t\t\tmessage,\n\t\t\t\taccount: user.account\n\t\t\t})\n\t\t\treturn toBytes(signature)\n\t\t}\n\t}\n}\n\n// XMTP XmtpClient setup\n// const xmtpClient: XmtpClient | null = null\n\n// Function to clear XMTP database when hitting installation limits\nasync function clearXMTPDatabase(address: string, env: string) {\n\tlogger.debug(\"๐Ÿงน Clearing XMTP database to resolve installation limit...\")\n\n\t// Get the storage directory using the same logic as getDbPath\n\tconst getStorageDirectory = () => {\n\t\tconst customStoragePath = process.env.XMTP_STORAGE_PATH\n\n\t\tif (customStoragePath) {\n\t\t\treturn path.isAbsolute(customStoragePath)\n\t\t\t\t? customStoragePath\n\t\t\t\t: path.resolve(process.cwd(), customStoragePath)\n\t\t}\n\n\t\t// Use existing logic as fallback\n\t\tconst projectRoot =\n\t\t\tprocess.env.PROJECT_ROOT || path.resolve(__dirname, \"../../..\")\n\n\t\treturn path.join(projectRoot, \".data/xmtp\") // Local development\n\t}\n\n\t// Clear local database files\n\tconst dbPattern = `${env}-${address}.db3`\n\tconst storageDir = getStorageDirectory()\n\n\t// Primary storage directory\n\tconst possiblePaths = [\n\t\tstorageDir,\n\t\t// Legacy fallback paths for backward compatibility\n\t\tpath.join(process.cwd(), \".data\", \"xmtp\"),\n\t\tpath.join(process.cwd(), \"..\", \".data\", \"xmtp\"),\n\t\tpath.join(process.cwd(), \"..\", \"..\", \".data\", \"xmtp\")\n\t]\n\n\tfor (const dir of possiblePaths) {\n\t\ttry {\n\t\t\tif (fs.existsSync(dir)) {\n\t\t\t\tconst files = fs.readdirSync(dir)\n\t\t\t\tconst matchingFiles = files.filter(\n\t\t\t\t\t(file) =>\n\t\t\t\t\t\tfile.includes(dbPattern) ||\n\t\t\t\t\t\tfile.includes(address) ||\n\t\t\t\t\t\tfile.includes(`xmtp-${env}-${address}`)\n\t\t\t\t)\n\n\t\t\t\tfor (const file of matchingFiles) {\n\t\t\t\t\tconst fullPath = path.join(dir, file)\n\t\t\t\t\ttry {\n\t\t\t\t\t\tfs.unlinkSync(fullPath)\n\t\t\t\t\t\tlogger.debug(`โœ… Removed: ${fullPath}`)\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tlogger.debug(`โš ๏ธ Could not remove ${fullPath}:`, err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (err) {\n\t\t\t// Ignore errors when checking directories\n\t\t}\n\t}\n}\n\nexport async function createXMTPClient(\n\tprivateKey: string,\n\topts?: {\n\t\tpersist?: boolean\n\t\tmaxRetries?: number\n\t\tstoragePath?: string\n\t}\n): Promise<XmtpClient> {\n\tconst { persist = true, maxRetries = 3, storagePath } = opts ?? {}\n\tlet attempt = 0\n\n\t// Extract common variables for error handling\n\t// const actualSigner = signer\n\tconst signer = createSigner(privateKey)\n\n\tif (!signer) {\n\t\tthrow new Error(\n\t\t\t\"No signer provided and XMTP_WALLET_KEY environment variable is not set\"\n\t\t)\n\t}\n\n\tconst { XMTP_DB_ENCRYPTION_KEY, XMTP_ENV } = process.env\n\n\t// Get the wallet address to use the correct database\n\tconst identifier = await signer.getIdentifier()\n\tconst address = identifier.identifier\n\n\twhile (attempt < maxRetries) {\n\t\ttry {\n\t\t\tlogger.debug(\n\t\t\t\t`๐Ÿ”„ Attempt ${attempt + 1}/${maxRetries} to create XMTP client...`\n\t\t\t)\n\n\t\t\t// Always require encryption key and persistence - no stateless mode\n\t\t\tif (!persist) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Stateless mode is not supported. XMTP client must run in persistent mode \" +\n\t\t\t\t\t\t\"to properly receive and process messages. Set persist: true or remove the persist option \" +\n\t\t\t\t\t\t\"to use the default persistent mode.\"\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tif (!XMTP_DB_ENCRYPTION_KEY) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"XMTP_DB_ENCRYPTION_KEY must be set for persistent mode\"\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tconst dbEncryptionKey = getEncryptionKeyFromHex(XMTP_DB_ENCRYPTION_KEY)\n\t\t\tconst dbPath = await getDbPath(\n\t\t\t\t`${XMTP_ENV || \"dev\"}-${address}`,\n\t\t\t\tstoragePath\n\t\t\t)\n\t\t\tlogger.debug(`๐Ÿ“ Using database path: ${dbPath}`)\n\n\t\t\t// Always create a fresh client and sync it\n\t\t\tconst client = await Client.create(signer, {\n\t\t\t\tdbEncryptionKey,\n\t\t\t\tenv: XMTP_ENV as XmtpEnv,\n\t\t\t\tdbPath,\n\t\t\t\tcodecs: [\n\t\t\t\t\tnew ReplyCodec(),\n\t\t\t\t\tnew ReactionCodec(),\n\t\t\t\t\tnew WalletSendCallsCodec(),\n\t\t\t\t\tnew TransactionReferenceCodec()\n\t\t\t\t]\n\t\t\t})\n\n\t\t\t// Force sync conversations to ensure we have the latest data\n\t\t\tlogger.debug(\"๐Ÿ“ก Syncing conversations to ensure latest state...\")\n\t\t\tawait client.conversations.sync()\n\n\t\t\tawait backupDbToPersistentStorage(\n\t\t\t\tdbPath,\n\t\t\t\t`${XMTP_ENV || \"dev\"}-${address}`\n\t\t\t)\n\n\t\t\tconsole.log(`Wallet: ${address}`)\n\t\t\tconsole.log(`Env: ${XMTP_ENV || \"dev\"}`)\n\t\t\tconsole.log(`Storage: persistent`)\n\n\t\t\treturn client as unknown as XmtpClient\n\t\t} catch (error) {\n\t\t\tattempt++\n\n\t\t\tif (\n\t\t\t\terror instanceof Error &&\n\t\t\t\terror.message.includes(\"5/5 installations\")\n\t\t\t) {\n\t\t\t\tconsole.log(\n\t\t\t\t\t`๐Ÿ’ฅ Installation limit reached (attempt ${attempt}/${maxRetries})`\n\t\t\t\t)\n\n\t\t\t\tif (attempt < maxRetries) {\n\t\t\t\t\t// Get wallet address for database clearing\n\t\t\t\t\tconst identifier = await signer.getIdentifier()\n\t\t\t\t\tconst address = identifier.identifier\n\n\t\t\t\t\t// Extract inboxId from the error message\n\t\t\t\t\tconst inboxIdMatch = error.message.match(/InboxID ([a-f0-9]+)/)\n\t\t\t\t\tconst inboxId = inboxIdMatch ? inboxIdMatch[1] : undefined\n\n\t\t\t\t\t// First try to revoke old installations\n\t\t\t\t\tconst revocationSuccess = await revokeOldInstallations(\n\t\t\t\t\t\tsigner,\n\t\t\t\t\t\tinboxId\n\t\t\t\t\t)\n\n\t\t\t\t\tif (revocationSuccess) {\n\t\t\t\t\t\tconsole.log(\"๐ŸŽฏ Installations revoked, retrying connection...\")\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\"โš ๏ธ Installation revocation failed or not needed, clearing database...\"\n\t\t\t\t\t\t)\n\t\t\t\t\t\t// Clear database as fallback\n\t\t\t\t\t\tawait clearXMTPDatabase(address, process.env.XMTP_ENV || \"dev\")\n\t\t\t\t\t}\n\n\t\t\t\t\t// Wait a bit before retrying\n\t\t\t\t\tconst delay = Math.pow(2, attempt) * 1000 // Exponential backoff\n\t\t\t\t\tconsole.log(`โณ Waiting ${delay}ms before retry...`)\n\t\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, delay))\n\t\t\t\t} else {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\"โŒ Failed to resolve installation limit after all retries\"\n\t\t\t\t\t)\n\t\t\t\t\tconsole.error(\"๐Ÿ’ก Possible solutions:\")\n\t\t\t\t\tconsole.error(\" 1. Use a different wallet (generate new keys)\")\n\t\t\t\t\tconsole.error(\" 2. Switch XMTP environments (dev <-> production)\")\n\t\t\t\t\tconsole.error(\" 3. Wait and try again later\")\n\t\t\t\t\tconsole.error(\" 4. Contact XMTP support for manual intervention\")\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t} else if (\n\t\t\t\terror instanceof Error &&\n\t\t\t\terror.message.includes(\"Association error: Missing identity update\")\n\t\t\t) {\n\t\t\t\tconsole.log(\n\t\t\t\t\t`๐Ÿ”„ Identity association error detected (attempt ${attempt}/${maxRetries})`\n\t\t\t\t)\n\n\t\t\t\tif (attempt < maxRetries) {\n\t\t\t\t\tconsole.log(\"๐Ÿ”ง Attempting automatic identity refresh...\")\n\n\t\t\t\t\t// Try to refresh identity by creating a persistent client first\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconsole.log(\"๐Ÿ“ Creating persistent client to refresh identity...\")\n\t\t\t\t\t\tconst tempEncryptionKey = XMTP_DB_ENCRYPTION_KEY\n\t\t\t\t\t\t\t? getEncryptionKeyFromHex(XMTP_DB_ENCRYPTION_KEY)\n\t\t\t\t\t\t\t: getEncryptionKeyFromHex(generateEncryptionKeyHex())\n\t\t\t\t\t\tconst tempClient = await Client.create(signer, {\n\t\t\t\t\t\t\tdbEncryptionKey: tempEncryptionKey,\n\t\t\t\t\t\t\tenv: XMTP_ENV as XmtpEnv,\n\t\t\t\t\t\t\tdbPath: await getDbPath(\n\t\t\t\t\t\t\t\t`${XMTP_ENV || \"dev\"}-${address}`,\n\t\t\t\t\t\t\t\tstoragePath\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tcodecs: [\n\t\t\t\t\t\t\t\tnew ReplyCodec(),\n\t\t\t\t\t\t\t\tnew ReactionCodec(),\n\t\t\t\t\t\t\t\tnew WalletSendCallsCodec(),\n\t\t\t\t\t\t\t\tnew TransactionReferenceCodec()\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\tconsole.log(\"๐Ÿ“ก Syncing identity and conversations...\")\n\t\t\t\t\t\tawait tempClient.conversations.sync()\n\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\"โœ… Identity refresh successful, retrying original request...\"\n\t\t\t\t\t\t)\n\n\t\t\t\t\t\t// Wait a bit before retrying\n\t\t\t\t\t\tconst delay = Math.pow(2, attempt) * 1000 // Exponential backoff\n\t\t\t\t\t\tconsole.log(`โณ Waiting ${delay}ms before retry...`)\n\t\t\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, delay))\n\t\t\t\t\t} catch (refreshError) {\n\t\t\t\t\t\tconsole.log(`โŒ Identity refresh failed:`, refreshError)\n\t\t\t\t\t\t// Continue to the retry logic\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\"โŒ Failed to resolve identity association error after all retries\"\n\t\t\t\t\t)\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\"๐Ÿ’ก Try running: pnpm with-env pnpm --filter @hybrd/xmtp refresh:identity\"\n\t\t\t\t\t)\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// For other errors, don't retry\n\t\t\t\tthrow error\n\t\t\t}\n\t\t}\n\t}\n\n\tthrow new Error(\"Max retries exceeded\")\n}\n\n// ===================================================================\n// Encryption Key Management\n// ===================================================================\n/**\n * Generate a random encryption key\n * @returns The encryption key as a hex string\n */\nexport const generateEncryptionKeyHex = () => {\n\tconst uint8Array = getRandomValues(new Uint8Array(32))\n\treturn uint8arraysToString(uint8Array, \"hex\")\n}\n\n/**\n * Get the encryption key from a hex string\n * @param hex - The hex string\n * @returns The encryption key as Uint8Array\n */\nconst getEncryptionKeyFromHex = (hex: string): Uint8Array => {\n\treturn fromString(hex, \"hex\")\n}\n\n// ===================================================================\n// Database Path Management\n// ===================================================================\nexport const getDbPath = async (description = \"xmtp\", storagePath?: string) => {\n\t// Allow custom storage path via environment variable\n\tconst customStoragePath = process.env.XMTP_STORAGE_PATH\n\n\tlet volumePath: string\n\n\tif (customStoragePath) {\n\t\t// Use custom storage path if provided\n\t\tvolumePath = path.isAbsolute(customStoragePath)\n\t\t\t? customStoragePath\n\t\t\t: path.resolve(process.cwd(), customStoragePath)\n\t} else if (storagePath) {\n\t\tvolumePath = path.isAbsolute(storagePath)\n\t\t\t? storagePath\n\t\t\t: path.resolve(process.cwd(), storagePath)\n\t} else {\n\t\t// Use existing logic as fallback\n\t\tconst projectRoot =\n\t\t\tprocess.env.PROJECT_ROOT || path.resolve(__dirname, \"../../..\")\n\n\t\t// Default storage path for local development\n\t\tvolumePath = path.join(projectRoot, \".data/xmtp\")\n\t}\n\n\tconst dbPath = `${volumePath}/${description}.db3`\n\n\tif (typeof globalThis !== \"undefined\" && \"XMTP_STORAGE\" in globalThis) {\n\t\ttry {\n\t\t\tconsole.log(`๐Ÿ“ฆ Using Cloudflare R2 storage for: ${dbPath}`)\n\n\t\t\tconst r2Bucket = (globalThis as any).XMTP_STORAGE\n\t\t\tconst remotePath = `xmtp-databases/${description}.db3`\n\n\t\t\ttry {\n\t\t\t\tconst existingObject = await r2Bucket.head(remotePath)\n\t\t\t\tif (existingObject) {\n\t\t\t\t\tconsole.log(`๐Ÿ“ฅ Downloading existing database from R2 storage...`)\n\n\t\t\t\t\tif (!fs.existsSync(volumePath)) {\n\t\t\t\t\t\tfs.mkdirSync(volumePath, { recursive: true })\n\t\t\t\t\t}\n\n\t\t\t\t\tconst object = await r2Bucket.get(remotePath)\n\t\t\t\t\tif (object) {\n\t\t\t\t\t\tconst fileData = await object.arrayBuffer()\n\t\t\t\t\t\tfs.writeFileSync(dbPath, new Uint8Array(fileData))\n\t\t\t\t\t\tconsole.log(`โœ… Database downloaded from R2 storage`)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconsole.log(`๐Ÿ“ No existing database found in R2 storage`)\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.log(`โš ๏ธ Failed to download database from R2 storage:`, error)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.log(`โš ๏ธ R2 storage not available:`, error)\n\t\t}\n\t}\n\n\tif (!fs.existsSync(volumePath)) {\n\t\tfs.mkdirSync(volumePath, { recursive: true })\n\t}\n\n\treturn dbPath\n}\n\nconst backupDbToPersistentStorage = async (\n\tdbPath: string,\n\tdescription: string\n) => {\n\tif (\n\t\ttypeof globalThis !== \"undefined\" &&\n\t\t\"XMTP_STORAGE\" in globalThis &&\n\t\tfs.existsSync(dbPath)\n\t) {\n\t\ttry {\n\t\t\tconsole.log(`๐Ÿ“ฆ Backing up database to R2 storage: ${dbPath}`)\n\n\t\t\tconst r2Bucket = (globalThis as any).XMTP_STORAGE\n\t\t\tconst remotePath = `xmtp-databases/${description}.db3`\n\n\t\t\tconst fileData = fs.readFileSync(dbPath)\n\t\t\tawait r2Bucket.put(remotePath, fileData)\n\t\t\tconsole.log(`โœ… Database backed up to R2 storage: ${remotePath}`)\n\t\t} catch (error) {\n\t\t\tconsole.log(`โš ๏ธ Failed to backup database to R2 storage:`, error)\n\t\t}\n\t}\n}\n\n// ===================================================================\n// Logging and Debugging\n// ===================================================================\nexport const logAgentDetails = async (\n\tclients: XmtpClient | XmtpClient[]\n): Promise<void> => {\n\tconst clientsByAddress = Array.isArray(clients)\n\t\t? clients.reduce<Record<string, XmtpClient[]>>((acc, XmtpClient) => {\n\t\t\t\tconst address = XmtpClient.accountIdentifier?.identifier ?? \"\"\n\t\t\t\tacc[address] = acc[address] ?? []\n\t\t\t\tacc[address].push(XmtpClient)\n\t\t\t\treturn acc\n\t\t\t}, {})\n\t\t: {\n\t\t\t\t[clients.accountIdentifier?.identifier ?? \"\"]: [clients]\n\t\t\t}\n\n\tfor (const [address, clientGroup] of Object.entries(clientsByAddress)) {\n\t\tconst firstClient = clientGroup[0]\n\t\tconst inboxId = firstClient?.inboxId\n\t\tconst environments = clientGroup\n\t\t\t.map((c) => c.options?.env ?? \"dev\")\n\t\t\t.join(\", \")\n\t\tconsole.log(`\\x1b[38;2;252;76;52m\n โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— \n โ•šโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ•‘โ•šโ•โ•โ–ˆโ–ˆโ•”โ•โ•โ•โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—\n โ•šโ–ˆโ–ˆโ–ˆโ•”โ• โ–ˆโ–ˆโ•”โ–ˆโ–ˆโ–ˆโ–ˆโ•”โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•\n โ–ˆโ–ˆโ•”โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•‘โ•šโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•”โ•โ•โ•โ• \n โ–ˆโ–ˆโ•”โ• โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•‘ โ•šโ•โ• โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ \n โ•šโ•โ• โ•šโ•โ•โ•šโ•โ• โ•šโ•โ• โ•šโ•โ• โ•šโ•โ• \n \\x1b[0m`)\n\n\t\tconst urls = [`http://xmtp.chat/dm/${address}`]\n\n\t\tconst conversations = await firstClient?.conversations.list()\n\n\t\tconsole.log(`\n โœ“ XMTP XmtpClient:\n โ€ข Address: ${address}\n โ€ข Conversations: ${conversations?.length}\n โ€ข InboxId: ${inboxId}\n โ€ข Networks: ${environments}\n ${urls.map((url) => `โ€ข URL: ${url}`).join(\"\\n\")}`)\n\t}\n}\n\n// ===================================================================\n// Environment Validation\n// ===================================================================\nexport function validateEnvironment(vars: string[]): Record<string, string> {\n\tconst missing = vars.filter((v) => !process.env[v])\n\n\tif (missing.length) {\n\t\ttry {\n\t\t\tconst envPath = path.resolve(process.cwd(), \".env\")\n\t\t\tif (fs.existsSync(envPath)) {\n\t\t\t\tconst envVars = fs\n\t\t\t\t\t.readFileSync(envPath, \"utf-8\")\n\t\t\t\t\t.split(\"\\n\")\n\t\t\t\t\t.filter((line) => line.trim() && !line.startsWith(\"#\"))\n\t\t\t\t\t.reduce<Record<string, string>>((acc, line) => {\n\t\t\t\t\t\tconst [key, ...val] = line.split(\"=\")\n\t\t\t\t\t\tif (key && val.length) acc[key.trim()] = val.join(\"=\").trim()\n\t\t\t\t\t\treturn acc\n\t\t\t\t\t}, {})\n\n\t\t\t\tmissing.forEach((v) => {\n\t\t\t\t\tif (envVars[v]) process.env[v] = envVars[v]\n\t\t\t\t})\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.error(e)\n\t\t\t/* ignore errors */\n\t\t}\n\n\t\tconst stillMissing = vars.filter((v) => !process.env[v])\n\t\tif (stillMissing.length) {\n\t\t\tconsole.error(\"Missing env vars:\", stillMissing.join(\", \"))\n\t\t\tprocess.exit(1)\n\t\t}\n\t}\n\n\treturn vars.reduce<Record<string, string>>((acc, key) => {\n\t\tacc[key] = process.env[key] as string\n\t\treturn acc\n\t}, {})\n}\n\n/**\n * Diagnose XMTP environment and identity issues (internal use only)\n */\nasync function diagnoseXMTPIdentityIssue(\n\tclient: XmtpClient,\n\tinboxId: string,\n\tenvironment: string\n): Promise<{\n\tcanResolve: boolean\n\tsuggestions: string[]\n\tdetails: Record<string, any>\n}> {\n\tconst suggestions: string[] = []\n\tconst details: Record<string, any> = {\n\t\tenvironment,\n\t\tinboxId,\n\t\ttimestamp: new Date().toISOString()\n\t}\n\n\ttry {\n\t\t// Try to resolve the inbox state\n\t\tconst inboxState = await client.preferences.inboxStateFromInboxIds([\n\t\t\tinboxId\n\t\t])\n\n\t\tif (inboxState.length === 0) {\n\t\t\tsuggestions.push(\n\t\t\t\t`Inbox ID ${inboxId} not found in ${environment} environment`\n\t\t\t)\n\t\t\tsuggestions.push(\n\t\t\t\t\"Try switching XMTP_ENV to 'dev' if currently 'production' or vice versa\"\n\t\t\t)\n\t\t\tsuggestions.push(\n\t\t\t\t\"Verify the user has created an identity on this XMTP network\"\n\t\t\t)\n\t\t\tdetails.inboxStateFound = false\n\t\t\treturn { canResolve: false, suggestions, details }\n\t\t}\n\n\t\tconst inbox = inboxState[0]\n\t\tif (!inbox) {\n\t\t\tsuggestions.push(\"Inbox state returned empty data\")\n\t\t\tdetails.inboxStateFound = false\n\t\t\treturn { canResolve: false, suggestions, details }\n\t\t}\n\n\t\tdetails.inboxStateFound = true\n\t\tdetails.identifierCount = inbox.identifiers?.length || 0\n\n\t\tif (!inbox.identifiers || inbox.identifiers.length === 0) {\n\t\t\tsuggestions.push(\"Inbox found but has no identifiers\")\n\t\t\tsuggestions.push(\"This indicates incomplete identity registration\")\n\t\t\tsuggestions.push(\"User may need to re-register their identity on XMTP\")\n\t\t\tdetails.hasIdentifiers = false\n\t\t\treturn { canResolve: false, suggestions, details }\n\t\t}\n\n\t\t// Successfully resolved\n\t\tdetails.hasIdentifiers = true\n\t\tdetails.resolvedAddress = inbox.identifiers[0]?.identifier\n\t\treturn {\n\t\t\tcanResolve: true,\n\t\t\tsuggestions: [\"Identity resolved successfully\"],\n\t\t\tdetails\n\t\t}\n\t} catch (error) {\n\t\tconst errorMessage = error instanceof Error ? error.message : String(error)\n\t\tdetails.error = errorMessage\n\n\t\tif (errorMessage.includes(\"Association error\")) {\n\t\t\tsuggestions.push(\"XMTP identity association error detected\")\n\t\t\tsuggestions.push(\n\t\t\t\t\"Check if user exists on the correct XMTP environment (dev vs production)\"\n\t\t\t)\n\t\t\tsuggestions.push(\n\t\t\t\t\"Identity may need to be recreated on the current environment\"\n\t\t\t)\n\t\t}\n\n\t\tif (errorMessage.includes(\"Missing identity update\")) {\n\t\t\tsuggestions.push(\"Missing identity updates in XMTP network\")\n\t\t\tsuggestions.push(\"This can indicate network sync issues\")\n\t\t\tsuggestions.push(\"Wait a few minutes and retry, or recreate identity\")\n\t\t}\n\n\t\tif (errorMessage.includes(\"database\") || errorMessage.includes(\"storage\")) {\n\t\t\tsuggestions.push(\"XMTP local database/storage issue\")\n\t\t\tsuggestions.push(\"Try clearing XMTP database and resyncing\")\n\t\t\tsuggestions.push(\"Check .data/xmtp directory permissions\")\n\t\t}\n\n\t\tsuggestions.push(\"Consider testing with a fresh XMTP identity\")\n\t\treturn { canResolve: false, suggestions, details }\n\t}\n}\n\n// ===================================================================\n// Enhanced Connection Management & Health Monitoring\n// ===================================================================\n\nexport interface XMTPConnectionConfig {\n\tmaxRetries?: number\n\tretryDelayMs?: number\n\thealthCheckIntervalMs?: number\n\tconnectionTimeoutMs?: number\n\treconnectOnFailure?: boolean\n}\n\nexport interface XMTPConnectionHealth {\n\tisConnected: boolean\n\tlastHealthCheck: Date\n\tconsecutiveFailures: number\n\ttotalReconnects: number\n\tavgResponseTime: number\n}\n\nexport class XMTPConnectionManager {\n\tprivate client: XmtpClient | null = null\n\tprivate privateKey: string\n\tprivate config: Required<XMTPConnectionConfig>\n\tprivate health: XMTPConnectionHealth\n\tprivate healthCheckTimer: NodeJS.Timeout | null = null\n\tprivate isReconnecting = false\n\n\tconstructor(privateKey: string, config: XMTPConnectionConfig = {}) {\n\t\tthis.privateKey = privateKey\n\t\tthis.config = {\n\t\t\tmaxRetries: config.maxRetries ?? 5,\n\t\t\tretryDelayMs: config.retryDelayMs ?? 1000,\n\t\t\thealthCheckIntervalMs: config.healthCheckIntervalMs ?? 30000,\n\t\t\tconnectionTimeoutMs: config.connectionTimeoutMs ?? 10000,\n\t\t\treconnectOnFailure: config.reconnectOnFailure ?? true\n\t\t}\n\n\t\tthis.health = {\n\t\t\tisConnected: false,\n\t\t\tlastHealthCheck: new Date(),\n\t\t\tconsecutiveFailures: 0,\n\t\t\ttotalReconnects: 0,\n\t\t\tavgResponseTime: 0\n\t\t}\n\t}\n\n\tasync connect(persist = false): Promise<XmtpClient> {\n\t\tif (this.client && this.health.isConnected) {\n\t\t\treturn this.client\n\t\t}\n\n\t\tlet attempt = 0\n\t\twhile (attempt < this.config.maxRetries) {\n\t\t\ttry {\n\t\t\t\tconsole.log(\n\t\t\t\t\t`๐Ÿ”„ XMTP connection attempt ${attempt + 1}/${this.config.maxRetries}`\n\t\t\t\t)\n\n\t\t\t\tthis.client = await createXMTPClient(this.privateKey, { persist })\n\t\t\t\tthis.health.isConnected = true\n\t\t\t\tthis.health.consecutiveFailures = 0\n\n\t\t\t\t// Start health monitoring\n\t\t\t\tthis.startHealthMonitoring()\n\n\t\t\t\tconsole.log(\"โœ… XMTP client connected successfully\")\n\t\t\t\treturn this.client\n\t\t\t} catch (error) {\n\t\t\t\tattempt++\n\t\t\t\tthis.health.consecutiveFailures++\n\n\t\t\t\tconsole.error(`โŒ XMTP connection attempt ${attempt} failed:`, error)\n\n\t\t\t\tif (attempt < this.config.maxRetries) {\n\t\t\t\t\tconst delay = this.config.retryDelayMs * Math.pow(2, attempt - 1)\n\t\t\t\t\tconsole.log(`โณ Retrying in ${delay}ms...`)\n\t\t\t\t\tawait this.sleep(delay)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t`Failed to connect to XMTP after ${this.config.maxRetries} attempts`\n\t\t)\n\t}\n\n\t// private async createClientWithTimeout(persist: boolean): Promise<XmtpClient> {\n\t// const timeoutPromise = new Promise<never>((_, reject) => {\n\t// setTimeout(\n\t// () => reject(new Error(\"Connection timeout\")),\n\t// this.config.connectionTimeoutMs\n\t// )\n\t// })\n\n\t// const clientPromise = createXMTPClient(this.signer, { persist })\n\n\t// return Promise.race([clientPromise, timeoutPromise])\n\t// }\n\n\tprivate startHealthMonitoring(): void {\n\t\tif (this.healthCheckTimer) {\n\t\t\tclearInterval(this.healthCheckTimer)\n\t\t}\n\n\t\tthis.healthCheckTimer = setInterval(() => {\n\t\t\tthis.performHealthCheck()\n\t\t}, this.config.healthCheckIntervalMs)\n\t}\n\n\tprivate async performHealthCheck(): Promise<void> {\n\t\tif (!this.client) return\n\n\t\tconst startTime = Date.now()\n\n\t\ttry {\n\t\t\t// Simple health check: try to list conversations\n\t\t\tawait this.client.conversations.list()\n\n\t\t\tconst responseTime = Date.now() - startTime\n\t\t\tthis.health.avgResponseTime =\n\t\t\t\t(this.health.avgResponseTime + responseTime) / 2\n\t\t\tthis.health.lastHealthCheck = new Date()\n\t\t\tthis.health.consecutiveFailures = 0\n\t\t\tthis.health.isConnected = true\n\n\t\t\tconsole.log(`๐Ÿ’“ XMTP health check passed (${responseTime}ms)`)\n\t\t} catch (error) {\n\t\t\tthis.health.consecutiveFailures++\n\t\t\tthis.health.isConnected = false\n\n\t\t\tconsole.error(`๐Ÿ’” XMTP health check failed:`, error)\n\n\t\t\t// Trigger reconnection if enabled\n\t\t\tif (this.config.reconnectOnFailure && !this.isReconnecting) {\n\t\t\t\tthis.handleConnectionFailure()\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async handleConnectionFailure(): Promise<void> {\n\t\tif (this.isReconnecting) return\n\n\t\tthis.isReconnecting = true\n\t\tthis.health.totalReconnects++\n\n\t\tconsole.log(\"๐Ÿ”„ XMTP connection lost, attempting to reconnect...\")\n\n\t\ttry {\n\t\t\tthis.client = null\n\t\t\tawait this.connect()\n\t\t\tconsole.log(\"โœ… XMTP reconnection successful\")\n\t\t} catch (error) {\n\t\t\tconsole.error(\"โŒ XMTP reconnection failed:\", error)\n\t\t} finally {\n\t\t\tthis.isReconnecting = false\n\t\t}\n\t}\n\n\tprivate sleep(ms: number): Promise<void> {\n\t\treturn new Promise((resolve) => setTimeout(resolve, ms))\n\t}\n\n\tgetHealth(): XMTPConnectionHealth {\n\t\treturn { ...this.health }\n\t}\n\n\tgetClient(): XmtpClient | null {\n\t\treturn this.client\n\t}\n\n\tasync disconnect(): Promise<void> {\n\t\tif (this.healthCheckTimer) {\n\t\t\tclearInterval(this.healthCheckTimer)\n\t\t\tthis.healthCheckTimer = null\n\t\t}\n\n\t\tthis.client = null\n\t\tthis.health.isConnected = false\n\t\tconsole.log(\"๐Ÿ”Œ XMTP client disconnected\")\n\t}\n}\n\n// Enhanced client creation with connection management\nexport async function createXMTPConnectionManager(\n\tprivateKey: string,\n\tconfig?: XMTPConnectionConfig\n): Promise<XMTPConnectionManager> {\n\tconst manager = new XMTPConnectionManager(privateKey, config)\n\tawait manager.connect()\n\treturn manager\n}\n\n// ===================================================================\n// User Address Resolution with Auto-Refresh\n// ===================================================================\n","import { Client, type Signer } from \"@xmtp/node-sdk\"\nimport { createSigner } from \"../src/client\"\n\n// Function to revoke old installations when hitting the limit\nexport async function revokeOldInstallations(signer: Signer, inboxId?: string) {\n\tconsole.log(\"๐Ÿ”ง Attempting to revoke old installations...\")\n\n\ttry {\n\t\t// If we don't have the inboxId, we need to extract it from a temporary client attempt\n\t\tif (!inboxId) {\n\t\t\tconsole.log(\"โ„น๏ธ No inboxId provided, cannot revoke installations\")\n\t\t\treturn false\n\t\t}\n\n\t\tconst inboxStates = await Client.inboxStateFromInboxIds(\n\t\t\t[inboxId],\n\t\t\tprocess.env.XMTP_ENV as \"dev\" | \"production\"\n\t\t)\n\n\t\tif (!inboxStates[0]) {\n\t\t\tconsole.log(\"โŒ No inbox state found for the provided inboxId\")\n\t\t\treturn false\n\t\t}\n\n\t\tconst toRevokeInstallationBytes = inboxStates[0].installations.map(\n\t\t\t(i: { bytes: Uint8Array }) => i.bytes\n\t\t)\n\n\t\tawait Client.revokeInstallations(\n\t\t\tsigner,\n\t\t\tinboxId,\n\t\t\ttoRevokeInstallationBytes,\n\t\t\tprocess.env.XMTP_ENV as \"dev\" | \"production\"\n\t\t)\n\n\t\tconst resultingStates = await Client.inboxStateFromInboxIds(\n\t\t\t[inboxId],\n\t\t\tprocess.env.XMTP_ENV as \"dev\" | \"production\"\n\t\t)\n\n\t\tconsole.log(\n\t\t\t`๐Ÿ“‹ Revoked installations: ${toRevokeInstallationBytes.length} installations`\n\t\t)\n\t\tconsole.log(\n\t\t\t`๐Ÿ“‹ Resulting state: ${resultingStates[0]?.installations.length || 0} installations`\n\t\t)\n\n\t\treturn true\n\t} catch (error) {\n\t\tconsole.error(\"โŒ Error during installation revocation:\", error)\n\t\treturn false\n\t}\n}\n\n// CLI script to revoke installations\nasync function main() {\n\tconst { XMTP_WALLET_KEY } = process.env\n\tconst inboxId = process.argv[2]\n\n\tif (!XMTP_WALLET_KEY) {\n\t\tconsole.error(\"โŒ XMTP_WALLET_KEY is required\")\n\t\tprocess.exit(1)\n\t}\n\n\tif (!inboxId) {\n\t\tconsole.error(\"โŒ InboxID is required as CLI argument\")\n\t\tconsole.error(\"Usage: tsx revoke-installations.ts <inboxId>\")\n\t\tprocess.exit(1)\n\t}\n\n\tconst signer = createSigner(XMTP_WALLET_KEY)\n\tconst identifier = await signer.getIdentifier()\n\tconst address = identifier.identifier\n\n\tconsole.log(`๐Ÿ”‘ Wallet Address: ${address}`)\n\tconsole.log(`๐Ÿ“‹ Inbox ID: ${inboxId}`)\n\n\t// Try to revoke installations\n\tconst success = await revokeOldInstallations(signer, inboxId)\n\n\tif (success) {\n\t\tconsole.log(\"โœ… Successfully revoked installations\")\n\t} else {\n\t\tconsole.log(\"โŒ Failed to revoke installations\")\n\t\tprocess.exit(1)\n\t}\n}\n\n// Run if called directly\nif (import.meta.url === `file://${process.argv[1]}`) {\n\tmain().catch((error) => {\n\t\tconsole.error(\"๐Ÿ’ฅ Fatal error:\", error)\n\t\tprocess.exit(1)\n\t})\n}\n","import {\n\tAgent as XmtpAgent,\n\tXmtpEnv,\n\tcreateSigner,\n\tcreateUser\n} from \"@xmtp/agent-sdk\"\n\nimport type {\n\tAgentMessage,\n\tAgentRuntime,\n\tBehaviorContext,\n\tBehaviorRegistry,\n\tPlugin,\n\tPluginContext,\n\tXmtpClient,\n\tXmtpConversation,\n\tXmtpMessage\n} from \"@hybrd/types\"\nimport { logger } from \"@hybrd/utils\"\nimport { randomUUID } from \"node:crypto\"\nimport { createXMTPClient, getDbPath } from \"./client\"\nimport { ContentTypeReply, ContentTypeText, type Reply } from \"./index\"\n\n// Re-export types from @hybrd/types for backward compatibility\nexport type { Plugin }\n\n/**\n * Send a response with threading support\n */\nasync function sendResponse(\n\tconversation: XmtpConversation,\n\ttext: string,\n\toriginalMessageId: string,\n\tbehaviorContext?: BehaviorContext\n) {\n\tconst shouldThread = behaviorContext?.sendOptions?.threaded ?? false\n\n\tif (shouldThread) {\n\t\t// Send as a reply to the original message\n\t\ttry {\n\t\t\tconst reply: Reply = {\n\t\t\t\treference: originalMessageId,\n\t\t\t\tcontentType: ContentTypeText,\n\t\t\t\tcontent: text\n\t\t\t}\n\t\t\tawait conversation.send(reply, ContentTypeReply)\n\t\t\tlogger.debug(\n\t\t\t\t`โœ… [sendResponse] Threaded reply sent successfully to message ${originalMessageId}`\n\t\t\t)\n\t\t} catch (error) {\n\t\t\tlogger.error(\n\t\t\t\t`โŒ [sendResponse] Failed to send threaded reply to message ${originalMessageId}:`,\n\t\t\t\terror\n\t\t\t)\n\t\t\t// Fall back to regular message if threaded reply fails\n\t\t\tlogger.debug(`๐Ÿ”„ [sendResponse] Falling back to regular message`)\n\t\t\tawait conversation.send(text)\n\t\t}\n\t} else {\n\t\t// Send as a regular message\n\t\tawait conversation.send(text)\n\t}\n}\n\n/**\n * XMTP Plugin that provides XMTP functionality to the agent\n *\n * @description\n * This plugin integrates XMTP messaging capabilities into the agent's\n * HTTP server. It mounts the XMTP endpoints for handling XMTP tools requests.\n */\nexport function XMTPPlugin(): Plugin<PluginContext> {\n\treturn {\n\t\tname: \"xmtp\",\n\t\tdescription: \"Provides XMTP messaging functionality\",\n\t\tapply: async (app, context): Promise<void> => {\n\t\t\tconst {\n\t\t\t\tXMTP_WALLET_KEY,\n\t\t\t\tXMTP_DB_ENCRYPTION_KEY,\n\t\t\t\tXMTP_ENV = \"production\"\n\t\t\t} = process.env\n\n\t\t\tconst { agent } = context\n\t\t\tconst pluginContext = context as PluginContext & {\n\t\t\t\tbehaviors?: BehaviorRegistry\n\t\t\t}\n\n\t\t\tif (!XMTP_WALLET_KEY) {\n\t\t\t\tthrow new Error(\"XMTP_WALLET_KEY must be set\")\n\t\t\t}\n\n\t\t\tif (!XMTP_DB_ENCRYPTION_KEY) {\n\t\t\t\tthrow new Error(\"XMTP_DB_ENCRYPTION_KEY must be set\")\n\t\t\t}\n\n\t\t\tconst user = createUser(XMTP_WALLET_KEY as `0x${string}`)\n\t\t\tconst signer = createSigner(user)\n\n\t\t\tconst xmtpClient = await createXMTPClient(\n\t\t\t\tXMTP_WALLET_KEY as `0x${string}`\n\t\t\t)\n\n\t\t\tconst address = user.account.address.toLowerCase()\n\t\t\tconst agentDbPath = await getDbPath(\n\t\t\t\t`agent-${XMTP_ENV || \"dev\"}-${address}`\n\t\t\t)\n\t\t\tlogger.debug(`๐Ÿ“ Using database path: ${agentDbPath}`)\n\n\t\t\tconst xmtp = await XmtpAgent.create(signer, {\n\t\t\t\tenv: XMTP_ENV as XmtpEnv,\n\t\t\t\tdbPath: agentDbPath\n\t\t\t})\n\n\t\t\txmtp.on(\"reaction\", async ({ conversation, message }) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst text = message.content.content\n\t\t\t\t\tconst messages: AgentMessage[] = [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tid: randomUUID(),\n\t\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\t\tparts: [{ type: \"text\", text }]\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\n\t\t\t\t\tconst baseRuntime: AgentRuntime = {\n\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\txmtpClient\n\t\t\t\t\t}\n\n\t\t\t\t\tconst runtime = await agent.createRuntimeContext(baseRuntime)\n\n\t\t\t\t\t// Execute pre-response behaviors\n\t\t\t\t\tif (context.behaviors) {\n\t\t\t\t\t\tconst behaviorContext: BehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait context.behaviors.executeBefore(behaviorContext)\n\t\t\t\t\t}\n\n\t\t\t\t\tconst { text: reply } = await agent.generate(messages, { runtime })\n\n\t\t\t\t\t// Execute post-response behaviors\n\t\t\t\t\tlet behaviorContext: BehaviorContext | undefined\n\t\t\t\t\tif (context.behaviors) {\n\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\t\tresponse: reply\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait context.behaviors.executeAfter(behaviorContext)\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Create minimal context for send options\n\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\t\tresponse: reply\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check if message was filtered out by filterMessages behavior\n\t\t\t\t\tif (behaviorContext?.sendOptions?.filtered) {\n\t\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t\t`๐Ÿ”‡ [XMTP Plugin] Skipping reaction response due to message being filtered`\n\t\t\t\t\t\t)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\tawait sendResponse(\n\t\t\t\t\t\tconversation as unknown as XmtpConversation,\n\t\t\t\t\t\treply,\n\t\t\t\t\t\tmessage.id,\n\t\t\t\t\t\tbehaviorContext\n\t\t\t\t\t)\n\t\t\t\t} catch (err) {\n\t\t\t\t\tlogger.error(\"โŒ Error handling reaction:\", err)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\txmtp.on(\"reply\", async ({ conversation, message }) => {\n\t\t\t\ttry {\n\t\t\t\t\t// TODO - why isn't this typed better?\n\t\t\t\t\tconst text = message.content.content as string\n\t\t\t\t\tconst messages: AgentMessage[] = [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tid: randomUUID(),\n\t\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\t\tparts: [{ type: \"text\", text }]\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\n\t\t\t\t\tconst baseRuntime: AgentRuntime = {\n\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\txmtpClient\n\t\t\t\t\t}\n\n\t\t\t\t\tconst runtime = await agent.createRuntimeContext(baseRuntime)\n\n\t\t\t\t\t// Execute pre-response behaviors\n\t\t\t\t\tlet behaviorContext: BehaviorContext | undefined\n\t\t\t\t\tif (context.behaviors) {\n\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait context.behaviors.executeBefore(behaviorContext)\n\n\t\t\t\t\t\t// Check if behaviors were stopped early (e.g., due to filtering)\n\t\t\t\t\t\tif (behaviorContext.stopped) {\n\t\t\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t\t\t`๐Ÿ”‡ [XMTP Plugin] Skipping reply response due to behavior chain being stopped`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst { text: reply } = await agent.generate(messages, { runtime })\n\n\t\t\t\t\t// Execute post-response behaviors\n\t\t\t\t\tif (context.behaviors) {\n\t\t\t\t\t\tif (!behaviorContext) {\n\t\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\t\t\tresponse: reply\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tbehaviorContext.response = reply\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait context.behaviors.executeAfter(behaviorContext)\n\n\t\t\t\t\t\t// Check if post behaviors were stopped early\n\t\t\t\t\t\tif (behaviorContext.stopped) {\n\t\t\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t\t\t`๐Ÿ”‡ [XMTP Plugin] Skipping reply response due to post-behavior chain being stopped`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Create minimal context for send options\n\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\t\tresponse: reply\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tawait sendResponse(\n\t\t\t\t\t\tconversation as unknown as XmtpConversation,\n\t\t\t\t\t\treply,\n\t\t\t\t\t\tmessage.id,\n\t\t\t\t\t\tbehaviorContext\n\t\t\t\t\t)\n\t\t\t\t} catch (err) {\n\t\t\t\t\tlogger.error(\"โŒ Error handling reply:\", err)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\txmtp.on(\"text\", async ({ conversation, message }) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst text = message.content\n\t\t\t\t\tconst messages: AgentMessage[] = [\n\t\t\t\t\t\t{ id: randomUUID(), role: \"user\", parts: [{ type: \"text\", text }] }\n\t\t\t\t\t]\n\n\t\t\t\t\tconst baseRuntime: AgentRuntime = {\n\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\txmtpClient\n\t\t\t\t\t}\n\n\t\t\t\t\tconst runtime = await agent.createRuntimeContext(baseRuntime)\n\n\t\t\t\t\t// Execute pre-response behaviors\n\t\t\t\t\tlet behaviorContext: BehaviorContext | undefined\n\t\t\t\t\tif (context.behaviors) {\n\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait context.behaviors.executeBefore(behaviorContext)\n\n\t\t\t\t\t\t// Check if behaviors were stopped early (e.g., due to filtering)\n\t\t\t\t\t\tif (behaviorContext.stopped) {\n\t\t\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t\t\t`๐Ÿ”‡ [XMTP Plugin] Skipping text response due to behavior chain being stopped`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst { text: reply } = await agent.generate(messages, { runtime })\n\n\t\t\t\t\t// Execute post-response behaviors\n\t\t\t\t\tif (context.behaviors) {\n\t\t\t\t\t\tif (!behaviorContext) {\n\t\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\t\t\tresponse: reply\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tbehaviorContext.response = reply\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait context.behaviors.executeAfter(behaviorContext)\n\n\t\t\t\t\t\t// Check if post behaviors were stopped early\n\t\t\t\t\t\tif (behaviorContext.stopped) {\n\t\t\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t\t\t`๐Ÿ”‡ [XMTP Plugin] Skipping text response due to post-behavior chain being stopped`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Create minimal context for send options\n\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\t\tresponse: reply\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tawait sendResponse(\n\t\t\t\t\t\tconversation as unknown as XmtpConversation,\n\t\t\t\t\t\treply,\n\t\t\t\t\t\tmessage.id,\n\t\t\t\t\t\tbehaviorContext\n\t\t\t\t\t)\n\t\t\t\t} catch (err) {\n\t\t\t\t\tlogger.error(\"โŒ Error handling text:\", err)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\t// Event handlers removed due to incompatibility with current XMTP agent SDK\n\n\t\t\tvoid xmtp\n\t\t\t\t.start()\n\t\t\t\t.then(() => logger.debug(\"โœ… XMTP agent listener started\"))\n\t\t\t\t.catch((err) =>\n\t\t\t\t\tconsole.error(\"โŒ XMTP agent listener failed to start:\", err)\n\t\t\t\t)\n\t\t}\n\t}\n}\n","import { Context } from \"hono\"\nimport jwt from \"jsonwebtoken\"\nimport { logger } from \"@hybrd/utils\"\n\nexport interface XMTPToolsPayload {\n\taction: \"send\" | \"reply\" | \"react\" | \"transaction\" | \"blockchain-event\"\n\tconversationId: string\n\t// Action-specific data\n\tcontent?: string\n\treferenceMessageId?: string\n\temoji?: string\n\tactionType?: \"added\" | \"removed\"\n\tfromAddress?: string\n\tchainId?: string\n\tcalls?: Array<{\n\t\tto: string\n\t\tdata: string\n\t\tmetadata?: {\n\t\t\tdescription: string\n\t\t\ttransactionType: string\n\t\t}\n\t}>\n\t// Metadata\n\tissued: number\n\texpires: number\n}\n\n/**\n * Validates token and returns payload for both GET and POST endpoints\n *\n * @param {Context} c - Hono context object containing request information\n * @returns {XMTPToolsPayload | null} The validated payload or null if invalid\n *\n * @description\n * Supports two authentication methods:\n * - Authorization header with Bearer token (for POST endpoints)\n * - Query parameter token (for GET endpoints)\n *\n * @example\n * ```typescript\n * app.post(\"/api/endpoint\", async (c) => {\n * const payload = getValidatedPayload(c);\n * if (!payload) {\n * return c.json({ error: \"Invalid token\" }, 401);\n * }\n * // Use payload data\n * });\n * ```\n */\nexport function getValidatedPayload(c: Context): XMTPToolsPayload | null {\n\t// Try Authorization header first (for POST endpoints)\n\tconst authHeader = c.req.header(\"Authorization\")\n\tif (authHeader?.startsWith(\"Bearer \")) {\n\t\tconst token = authHeader.substring(7) // Remove \"Bearer \" prefix\n\t\treturn validateXMTPToolsToken(token)\n\t}\n\n\t// Fall back to query parameter (for GET endpoints)\n\tconst token = c.req.query(\"token\")\n\tif (!token) {\n\t\treturn null\n\t}\n\n\treturn validateXMTPToolsToken(token)\n}\n\n/**\n * Gets the JWT secret for token signing, with lazy initialization\n * Uses XMTP_DB_ENCRYPTION_KEY environment variable for consistency\n * Only falls back to development secret in development/test environments\n */\nfunction getJwtSecret(): string {\n\tconst secret = process.env.XMTP_DB_ENCRYPTION_KEY\n\tconst nodeEnv = process.env.NODE_ENV || \"development\"\n\n\t// In production, require a real JWT secret\n\tif (nodeEnv === \"production\" && !secret) {\n\t\tthrow new Error(\n\t\t\t\"XMTP_DB_ENCRYPTION_KEY environment variable is required in production. \" +\n\t\t\t\t\"Generate a secure random secret for JWT token signing.\"\n\t\t)\n\t}\n\n\t// In development/test, allow fallback but warn only when actually used\n\tif (!secret) {\n\t\tlogger.warn(\n\t\t\t\"โš ๏ธ [SECURITY] Using fallback JWT secret for development. \" +\n\t\t\t\t\"Set XMTP_DB_ENCRYPTION_KEY environment variable for production.\"\n\t\t)\n\t\treturn \"fallback-secret-for-dev-only\"\n\t}\n\n\treturn secret\n}\n\n/**\n * Gets the API key for authentication, with lazy initialization\n * Requires XMTP_API_KEY environment variable in production\n * Only falls back to development key in development/test environments\n */\nfunction getApiKey(): string {\n\tconst apiKey = process.env.XMTP_API_KEY\n\tconst nodeEnv = process.env.NODE_ENV || \"development\"\n\n\t// In production, require a real API key\n\tif (nodeEnv === \"production\" && !apiKey) {\n\t\tthrow new Error(\n\t\t\t\"XMTP_API_KEY environment variable is required in production. \" +\n\t\t\t\t\"Generate a secure random API key for authentication.\"\n\t\t)\n\t}\n\n\t// In development/test, allow fallback but warn only when actually used\n\tif (!apiKey) {\n\t\tlogger.warn(\n\t\t\t\"โš ๏ธ [SECURITY] Using fallback API key for development. \" +\n\t\t\t\t\"Set XMTP_API_KEY environment variable for production.\"\n\t\t)\n\t\treturn \"fallback-api-key-for-dev-only\"\n\t}\n\n\treturn apiKey\n}\n\n/**\n * JWT token expiry time in seconds (5 minutes)\n */\nconst JWT_EXPIRY = 5 * 60 // 5 minutes in seconds\n\n/**\n * Generates a signed JWT token for XMTP tools authentication\n *\n * @param {Omit<XMTPToolsPayload, \"issued\" | \"expires\">} payload - Token payload without timestamp fields\n * @returns {string} Signed JWT token\n *\n * @description\n * Creates a JWT token with automatic timestamp fields:\n * - issued: Current timestamp\n * - expires: Current timestamp + JWT_EXPIRY\n *\n * @example\n * ```typescript\n * const token = generateXMTPToolsToken({\n * action: \"send\",\n * conversationId: \"0x123...\"\n * });\n * ```\n */\nexport function generateXMTPToolsToken(\n\tpayload: Omit<XMTPToolsPayload, \"issued\" | \"expires\">\n): string {\n\tconst startTime = performance.now()\n\tlogger.debug(\"๐Ÿ” [JWT] Starting token generation...\")\n\n\tconst now = Math.floor(Date.now() / 1000)\n\tconst fullPayload: XMTPToolsPayload = {\n\t\t...payload,\n\t\tissued: now,\n\t\texpires: now + JWT_EXPIRY\n\t}\n\n\tconst token = jwt.sign(fullPayload, getJwtSecret(), {\n\t\texpiresIn: JWT_EXPIRY\n\t})\n\n\tconst endTime = performance.now()\n\tlogger.debug(\n\t\t`๐Ÿ” [JWT] Token generation completed in ${(endTime - startTime).toFixed(2)}ms`\n\t)\n\n\treturn token\n}\n\n/**\n * Validates an XMTP tools token using either API key or JWT verification\n *\n * @param {string} token - Token to validate (either API key or JWT)\n * @returns {XMTPToolsPayload | null} Validated payload or null if invalid\n *\n * @description\n * Supports two authentication methods in order of precedence:\n * 1. API key authentication - Direct comparison with XMTP_API_KEY\n * 2. JWT token authentication - Signature verification and expiry check\n *\n * For API key authentication, returns a default payload with 1-hour expiry.\n * For JWT authentication, validates signature and checks expiry timestamp.\n *\n * @example\n * ```typescript\n * const payload = validateXMTPToolsToken(userToken);\n * if (payload) {\n * console.log(`Action: ${payload.action}`);\n * console.log(`Conversation: ${payload.conversationId}`);\n * }\n * ```\n */\nexport function validateXMTPToolsToken(token: string): XMTPToolsPayload | null {\n\tconst startTime = performance.now()\n\tlogger.debug(\"๐Ÿ” [JWT] Starting token validation...\")\n\n\t// First try API key authentication\n\tif (token === getApiKey()) {\n\t\tlogger.debug(\"๐Ÿ”‘ [Auth] Using API key authentication\")\n\t\t// Return a valid payload for API key auth\n\t\tconst now = Math.floor(Date.now() / 1000)\n\t\tconst result = {\n\t\t\taction: \"send\" as const, // Default action\n\t\t\tconversationId: \"\", // Will be filled by endpoint\n\t\t\tissued: now,\n\t\t\texpires: now + 3600 // API keys are valid for 1 hour\n\t\t}\n\n\t\tconst endTime = performance.now()\n\t\tlogger.debug(\n\t\t\t`๐Ÿ” [JWT] API key validation completed in ${(endTime - startTime).toFixed(2)}ms`\n\t\t)\n\t\treturn result\n\t}\n\n\t// Then try JWT token authentication\n\ttry {\n\t\tconst decoded = jwt.verify(token, getJwtSecret()) as XMTPToolsPayload\n\t\tlogger.debug(\"๐Ÿ”‘ [Auth] Using JWT token authentication\")\n\n\t\t// Additional expiry check\n\t\tconst now = Math.floor(Date.now() / 1000)\n\t\tif (decoded.expires < now) {\n\t\t\tconsole.log(\"๐Ÿ”’ XMTP tools token has expired\")\n\t\t\tconst endTime = performance.now()\n\t\t\tlogger.debug(\n\t\t\t\t`๐Ÿ” [JWT] Token validation failed (expired) in ${(endTime - startTime).toFixed(2)}ms`\n\t\t\t)\n\t\t\treturn null\n\t\t}\n\n\t\tconst endTime = performance.now()\n\t\tlogger.debug(\n\t\t\t`๐Ÿ” [JWT] JWT validation completed in ${(endTime - startTime).toFixed(2)}ms`\n\t\t)\n\t\treturn decoded\n\t} catch (error) {\n\t\tlogger.error(\n\t\t\t\"๐Ÿ”’ Invalid XMTP tools token and not matching API key:\",\n\t\t\terror\n\t\t)\n\t\tconst endTime = performance.now()\n\t\tlogger.debug(\n\t\t\t`๐Ÿ” [JWT] Token validation failed in ${(endTime - startTime).toFixed(2)}ms`\n\t\t)\n\t\treturn null\n\t}\n}\n"],"mappings":"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","names":["import_agent_sdk","import_node_sdk","import_meta","import_meta","path","fs","identifier","address","uint8arraysToString","import_utils","import_node_crypto","XmtpAgent","behaviorContext","import_utils","jwt","import_node_sdk","import_content_type_transaction_reference","import_content_type_reaction","import_content_type_reply","import_content_type_wallet_send_calls"]}
package/dist/index.js CHANGED
@@ -635,89 +635,11 @@ function XMTPPlugin() {
635
635
  const xmtpClient = await createXMTPClient(
636
636
  XMTP_WALLET_KEY
637
637
  );
638
- async function startNodeStream() {
639
- try {
640
- logger2.debug("\u{1F3A7} XMTP node client stream initializing");
641
- const stream = await xmtpClient.conversations.streamAllMessages();
642
- logger2.debug("\u{1F3A7} XMTP node client stream started");
643
- for await (const msg of stream) {
644
- try {
645
- if (msg.senderInboxId === xmtpClient.inboxId) continue;
646
- const content = typeof msg.content === "string" ? msg.content : (() => {
647
- try {
648
- return JSON.stringify(msg.content);
649
- } catch {
650
- return String(msg.content);
651
- }
652
- })();
653
- const conversation = await xmtpClient.conversations.getConversationById(
654
- msg.conversationId
655
- );
656
- if (!conversation) {
657
- logger2.warn(
658
- `\u26A0\uFE0F XMTP conversation not found: ${msg.conversationId}`
659
- );
660
- continue;
661
- }
662
- const messages = [
663
- {
664
- id: randomUUID(),
665
- role: "user",
666
- parts: [{ type: "text", text: content }]
667
- }
668
- ];
669
- const baseRuntime = {
670
- conversation,
671
- message: msg,
672
- xmtpClient
673
- };
674
- const runtime = await agent.createRuntimeContext(baseRuntime);
675
- if (pluginContext.behaviors) {
676
- const behaviorContext2 = {
677
- runtime,
678
- client: xmtpClient,
679
- conversation,
680
- message: msg
681
- };
682
- await pluginContext.behaviors.executeBefore(behaviorContext2);
683
- if (behaviorContext2.sendOptions?.filtered) {
684
- continue;
685
- }
686
- }
687
- const { text } = await agent.generate(messages, { runtime });
688
- const behaviorContext = {
689
- runtime,
690
- client: xmtpClient,
691
- conversation,
692
- message: msg,
693
- response: text
694
- };
695
- if (pluginContext.behaviors) {
696
- await pluginContext.behaviors.executeAfter(behaviorContext);
697
- }
698
- if (behaviorContext?.sendOptions?.filtered) {
699
- logger2.debug(
700
- `\u{1F507} [XMTP Plugin] Skipping response due to message being filtered`
701
- );
702
- return;
703
- }
704
- await sendResponse(conversation, text, msg.id, behaviorContext);
705
- } catch (err) {
706
- logger2.error("\u274C Error processing XMTP message:", err);
707
- }
708
- }
709
- } catch (err) {
710
- logger2.error("\u274C XMTP node client stream failed:", err);
711
- }
712
- }
713
- const enabledFromEnv = process.env.XMTP_ENABLE_NODE_STREAM;
714
- const isNodeStreamEnabled = enabledFromEnv === void 0 ? true : !/^(false|0|off|no)$/i.test(String(enabledFromEnv));
715
- if (isNodeStreamEnabled) void startNodeStream();
716
638
  const address = user.account.address.toLowerCase();
717
639
  const agentDbPath = await getDbPath(
718
640
  `agent-${XMTP_ENV || "dev"}-${address}`
719
641
  );
720
- logger2.debug(`\u{1F4C1} Using agent listener database path: ${agentDbPath}`);
642
+ logger2.debug(`\u{1F4C1} Using database path: ${agentDbPath}`);
721
643
  const xmtp = await XmtpAgent.create(signer, {
722
644
  env: XMTP_ENV,
723
645
  dbPath: agentDbPath
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/client.ts","../scripts/revoke-installations.ts","../src/plugin.ts","../src/lib/jwt.ts"],"sourcesContent":["export {\n\tAgent,\n\tcreateSigner,\n\tcreateUser,\n\tfilter,\n\tgetTestUrl\n} from \"@xmtp/agent-sdk\"\n\nexport type * from \"./types\"\n\nexport {\n\tDEFAULT_AMOUNT,\n\tDEFAULT_OPTIONS,\n\tMAX_USDC_AMOUNT\n} from \"./constants\"\n// NodeNext/Node16 requires explicit extensions for relative imports\n// NodeNext/Node16 requires explicit extensions for relative imports\n\n// ===================================================================\n// XMTP Client and Connection Management\n// ===================================================================\nexport {\n createXMTPClient,\n createSigner as createXMTPSigner,\n logAgentDetails,\n validateEnvironment,\n XMTPConnectionManager\n} from \"./client\"\nexport type { XMTPConnectionConfig } from \"./client\"\n\n// ===================================================================\n// XMTP Plugin for Agent Integration\n// ===================================================================\nexport { XMTPPlugin } from \"./plugin\"\nexport type { Plugin } from \"./plugin\"\n\n// ===================================================================\n// JWT Utilities for XMTP Tools\n// ===================================================================\nexport { generateXMTPToolsToken } from \"./lib/jwt\"\nexport type { XMTPToolsPayload } from \"./lib/jwt\"\n\n// ===================================================================\n// XMTP Core SDK Exports\n// ===================================================================\nexport {\n\tClient,\n\tIdentifierKind,\n\t// type Conversation,\n\ttype DecodedMessage,\n\ttype Dm,\n\t// type Group,\n\ttype LogLevel,\n\ttype Signer,\n\ttype XmtpEnv\n} from \"@xmtp/node-sdk\"\n\n// ===================================================================\n// XMTP Content Types\n// ===================================================================\nexport {\n\tContentTypeTransactionReference,\n\ttype TransactionReference\n} from \"@xmtp/content-type-transaction-reference\"\n\nexport { ContentTypeText, type TextParameters } from \"@xmtp/content-type-text\"\n\nexport {\n\tContentTypeReaction,\n\ttype Reaction\n} from \"@xmtp/content-type-reaction\"\n\nexport {\n\tContentTypeReply,\n\tReplyCodec,\n\ttype Reply\n} from \"@xmtp/content-type-reply\"\n\nexport {\n\tContentTypeGroupUpdated,\n\tGroupUpdatedCodec,\n\ttype GroupUpdated\n} from \"@xmtp/content-type-group-updated\"\n\nexport {\n\tContentTypeWalletSendCalls,\n\ttype WalletSendCallsParams\n} from \"@xmtp/content-type-wallet-send-calls\"\n","// ===================================================================\n// Betting Configuration\n// ===================================================================\nexport const DEFAULT_OPTIONS = [\"yes\", \"no\"]\nexport const DEFAULT_AMOUNT = \"0.1\"\nexport const MAX_USDC_AMOUNT = 10 // Maximum allowed USDC transaction amount\n","import { logger } from \"@hybrd/utils\"\nimport { ReactionCodec } from \"@xmtp/content-type-reaction\"\nimport { ReplyCodec } from \"@xmtp/content-type-reply\"\nimport { TransactionReferenceCodec } from \"@xmtp/content-type-transaction-reference\"\nimport { WalletSendCallsCodec } from \"@xmtp/content-type-wallet-send-calls\"\nimport { Client, IdentifierKind, type Signer, XmtpEnv } from \"@xmtp/node-sdk\"\nimport { getRandomValues } from \"node:crypto\"\nimport fs from \"node:fs\"\nimport path from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport { fromString, toString as uint8arraysToString } from \"uint8arrays\"\nimport { createWalletClient, http, toBytes } from \"viem\"\nimport { privateKeyToAccount } from \"viem/accounts\"\nimport { sepolia } from \"viem/chains\"\nimport { revokeOldInstallations } from \"../scripts/revoke-installations\"\nimport { XmtpClient } from \"./types\"\n\n// ===================================================================\n// Module Setup\n// ===================================================================\n// ES module equivalent of __dirname\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = path.dirname(__filename)\n\n// ===================================================================\n// Type Definitions\n// ===================================================================\ninterface User {\n\tkey: `0x${string}`\n\taccount: ReturnType<typeof privateKeyToAccount>\n\twallet: any // Simplified to avoid deep type instantiation\n}\n\n// ===================================================================\n// User and Signer Creation\n// ===================================================================\nexport const createUser = (key: string): User => {\n\tconst account = privateKeyToAccount(key as `0x${string}`)\n\treturn {\n\t\tkey: key as `0x${string}`,\n\t\taccount,\n\t\twallet: createWalletClient({\n\t\t\taccount,\n\t\t\tchain: sepolia,\n\t\t\ttransport: http()\n\t\t})\n\t}\n}\n\nexport const createSigner = (key: string): Signer => {\n\tif (!key || typeof key !== \"string\") {\n\t\tthrow new Error(\"XMTP wallet key must be a non-empty string\")\n\t}\n\tconst sanitizedKey = key.startsWith(\"0x\") ? key : `0x${key}`\n\tconst user = createUser(sanitizedKey)\n\treturn {\n\t\ttype: \"EOA\",\n\t\tgetIdentifier: () => ({\n\t\t\tidentifierKind: 0 as IdentifierKind.Ethereum, // Use numeric value to avoid ambient const enum issue\n\t\t\tidentifier: user.account.address.toLowerCase()\n\t\t}),\n\t\tsignMessage: async (message: string) => {\n\t\t\tconst signature = await user.wallet.signMessage({\n\t\t\t\tmessage,\n\t\t\t\taccount: user.account\n\t\t\t})\n\t\t\treturn toBytes(signature)\n\t\t}\n\t}\n}\n\n// XMTP XmtpClient setup\n// const xmtpClient: XmtpClient | null = null\n\n// Function to clear XMTP database when hitting installation limits\nasync function clearXMTPDatabase(address: string, env: string) {\n\tlogger.debug(\"๐Ÿงน Clearing XMTP database to resolve installation limit...\")\n\n\t// Get the storage directory using the same logic as getDbPath\n\tconst getStorageDirectory = () => {\n\t\tconst customStoragePath = process.env.XMTP_STORAGE_PATH\n\n\t\tif (customStoragePath) {\n\t\t\treturn path.isAbsolute(customStoragePath)\n\t\t\t\t? customStoragePath\n\t\t\t\t: path.resolve(process.cwd(), customStoragePath)\n\t\t}\n\n\t\t// Use existing logic as fallback\n\t\tconst projectRoot =\n\t\t\tprocess.env.PROJECT_ROOT || path.resolve(__dirname, \"../../..\")\n\n\t\treturn path.join(projectRoot, \".data/xmtp\") // Local development\n\t}\n\n\t// Clear local database files\n\tconst dbPattern = `${env}-${address}.db3`\n\tconst storageDir = getStorageDirectory()\n\n\t// Primary storage directory\n\tconst possiblePaths = [\n\t\tstorageDir,\n\t\t// Legacy fallback paths for backward compatibility\n\t\tpath.join(process.cwd(), \".data\", \"xmtp\"),\n\t\tpath.join(process.cwd(), \"..\", \".data\", \"xmtp\"),\n\t\tpath.join(process.cwd(), \"..\", \"..\", \".data\", \"xmtp\")\n\t]\n\n\tfor (const dir of possiblePaths) {\n\t\ttry {\n\t\t\tif (fs.existsSync(dir)) {\n\t\t\t\tconst files = fs.readdirSync(dir)\n\t\t\t\tconst matchingFiles = files.filter(\n\t\t\t\t\t(file) =>\n\t\t\t\t\t\tfile.includes(dbPattern) ||\n\t\t\t\t\t\tfile.includes(address) ||\n\t\t\t\t\t\tfile.includes(`xmtp-${env}-${address}`)\n\t\t\t\t)\n\n\t\t\t\tfor (const file of matchingFiles) {\n\t\t\t\t\tconst fullPath = path.join(dir, file)\n\t\t\t\t\ttry {\n\t\t\t\t\t\tfs.unlinkSync(fullPath)\n\t\t\t\t\t\tlogger.debug(`โœ… Removed: ${fullPath}`)\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tlogger.debug(`โš ๏ธ Could not remove ${fullPath}:`, err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (err) {\n\t\t\t// Ignore errors when checking directories\n\t\t}\n\t}\n}\n\nexport async function createXMTPClient(\n\tprivateKey: string,\n\topts?: {\n\t\tpersist?: boolean\n\t\tmaxRetries?: number\n\t\tstoragePath?: string\n\t}\n): Promise<XmtpClient> {\n\tconst { persist = true, maxRetries = 3, storagePath } = opts ?? {}\n\tlet attempt = 0\n\n\t// Extract common variables for error handling\n\t// const actualSigner = signer\n\tconst signer = createSigner(privateKey)\n\n\tif (!signer) {\n\t\tthrow new Error(\n\t\t\t\"No signer provided and XMTP_WALLET_KEY environment variable is not set\"\n\t\t)\n\t}\n\n\tconst { XMTP_DB_ENCRYPTION_KEY, XMTP_ENV } = process.env\n\n\t// Get the wallet address to use the correct database\n\tconst identifier = await signer.getIdentifier()\n\tconst address = identifier.identifier\n\n\twhile (attempt < maxRetries) {\n\t\ttry {\n\t\t\tlogger.debug(\n\t\t\t\t`๐Ÿ”„ Attempt ${attempt + 1}/${maxRetries} to create XMTP client...`\n\t\t\t)\n\n\t\t\t// Always require encryption key and persistence - no stateless mode\n\t\t\tif (!persist) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Stateless mode is not supported. XMTP client must run in persistent mode \" +\n\t\t\t\t\t\t\"to properly receive and process messages. Set persist: true or remove the persist option \" +\n\t\t\t\t\t\t\"to use the default persistent mode.\"\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tif (!XMTP_DB_ENCRYPTION_KEY) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"XMTP_DB_ENCRYPTION_KEY must be set for persistent mode\"\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tconst dbEncryptionKey = getEncryptionKeyFromHex(XMTP_DB_ENCRYPTION_KEY)\n\t\t\tconst dbPath = await getDbPath(\n\t\t\t\t`${XMTP_ENV || \"dev\"}-${address}`,\n\t\t\t\tstoragePath\n\t\t\t)\n\t\t\tlogger.debug(`๐Ÿ“ Using database path: ${dbPath}`)\n\n\t\t\t// Always create a fresh client and sync it\n\t\t\tconst client = await Client.create(signer, {\n\t\t\t\tdbEncryptionKey,\n\t\t\t\tenv: XMTP_ENV as XmtpEnv,\n\t\t\t\tdbPath,\n\t\t\t\tcodecs: [\n\t\t\t\t\tnew ReplyCodec(),\n\t\t\t\t\tnew ReactionCodec(),\n\t\t\t\t\tnew WalletSendCallsCodec(),\n\t\t\t\t\tnew TransactionReferenceCodec()\n\t\t\t\t]\n\t\t\t})\n\n\t\t\t// Force sync conversations to ensure we have the latest data\n\t\t\tlogger.debug(\"๐Ÿ“ก Syncing conversations to ensure latest state...\")\n\t\t\tawait client.conversations.sync()\n\n\t\t\tawait backupDbToPersistentStorage(\n\t\t\t\tdbPath,\n\t\t\t\t`${XMTP_ENV || \"dev\"}-${address}`\n\t\t\t)\n\n\t\t\tconsole.log(`Wallet: ${address}`)\n\t\t\tconsole.log(`Env: ${XMTP_ENV || \"dev\"}`)\n\t\t\tconsole.log(`Storage: persistent`)\n\n\t\t\treturn client as unknown as XmtpClient\n\t\t} catch (error) {\n\t\t\tattempt++\n\n\t\t\tif (\n\t\t\t\terror instanceof Error &&\n\t\t\t\terror.message.includes(\"5/5 installations\")\n\t\t\t) {\n\t\t\t\tconsole.log(\n\t\t\t\t\t`๐Ÿ’ฅ Installation limit reached (attempt ${attempt}/${maxRetries})`\n\t\t\t\t)\n\n\t\t\t\tif (attempt < maxRetries) {\n\t\t\t\t\t// Get wallet address for database clearing\n\t\t\t\t\tconst identifier = await signer.getIdentifier()\n\t\t\t\t\tconst address = identifier.identifier\n\n\t\t\t\t\t// Extract inboxId from the error message\n\t\t\t\t\tconst inboxIdMatch = error.message.match(/InboxID ([a-f0-9]+)/)\n\t\t\t\t\tconst inboxId = inboxIdMatch ? inboxIdMatch[1] : undefined\n\n\t\t\t\t\t// First try to revoke old installations\n\t\t\t\t\tconst revocationSuccess = await revokeOldInstallations(\n\t\t\t\t\t\tsigner,\n\t\t\t\t\t\tinboxId\n\t\t\t\t\t)\n\n\t\t\t\t\tif (revocationSuccess) {\n\t\t\t\t\t\tconsole.log(\"๐ŸŽฏ Installations revoked, retrying connection...\")\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\"โš ๏ธ Installation revocation failed or not needed, clearing database...\"\n\t\t\t\t\t\t)\n\t\t\t\t\t\t// Clear database as fallback\n\t\t\t\t\t\tawait clearXMTPDatabase(address, process.env.XMTP_ENV || \"dev\")\n\t\t\t\t\t}\n\n\t\t\t\t\t// Wait a bit before retrying\n\t\t\t\t\tconst delay = Math.pow(2, attempt) * 1000 // Exponential backoff\n\t\t\t\t\tconsole.log(`โณ Waiting ${delay}ms before retry...`)\n\t\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, delay))\n\t\t\t\t} else {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\"โŒ Failed to resolve installation limit after all retries\"\n\t\t\t\t\t)\n\t\t\t\t\tconsole.error(\"๐Ÿ’ก Possible solutions:\")\n\t\t\t\t\tconsole.error(\" 1. Use a different wallet (generate new keys)\")\n\t\t\t\t\tconsole.error(\" 2. Switch XMTP environments (dev <-> production)\")\n\t\t\t\t\tconsole.error(\" 3. Wait and try again later\")\n\t\t\t\t\tconsole.error(\" 4. Contact XMTP support for manual intervention\")\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t} else if (\n\t\t\t\terror instanceof Error &&\n\t\t\t\terror.message.includes(\"Association error: Missing identity update\")\n\t\t\t) {\n\t\t\t\tconsole.log(\n\t\t\t\t\t`๐Ÿ”„ Identity association error detected (attempt ${attempt}/${maxRetries})`\n\t\t\t\t)\n\n\t\t\t\tif (attempt < maxRetries) {\n\t\t\t\t\tconsole.log(\"๐Ÿ”ง Attempting automatic identity refresh...\")\n\n\t\t\t\t\t// Try to refresh identity by creating a persistent client first\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconsole.log(\"๐Ÿ“ Creating persistent client to refresh identity...\")\n\t\t\t\t\t\tconst tempEncryptionKey = XMTP_DB_ENCRYPTION_KEY\n\t\t\t\t\t\t\t? getEncryptionKeyFromHex(XMTP_DB_ENCRYPTION_KEY)\n\t\t\t\t\t\t\t: getEncryptionKeyFromHex(generateEncryptionKeyHex())\n\t\t\t\t\t\tconst tempClient = await Client.create(signer, {\n\t\t\t\t\t\t\tdbEncryptionKey: tempEncryptionKey,\n\t\t\t\t\t\t\tenv: XMTP_ENV as XmtpEnv,\n\t\t\t\t\t\t\tdbPath: await getDbPath(\n\t\t\t\t\t\t\t\t`${XMTP_ENV || \"dev\"}-${address}`,\n\t\t\t\t\t\t\t\tstoragePath\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tcodecs: [\n\t\t\t\t\t\t\t\tnew ReplyCodec(),\n\t\t\t\t\t\t\t\tnew ReactionCodec(),\n\t\t\t\t\t\t\t\tnew WalletSendCallsCodec(),\n\t\t\t\t\t\t\t\tnew TransactionReferenceCodec()\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\tconsole.log(\"๐Ÿ“ก Syncing identity and conversations...\")\n\t\t\t\t\t\tawait tempClient.conversations.sync()\n\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\"โœ… Identity refresh successful, retrying original request...\"\n\t\t\t\t\t\t)\n\n\t\t\t\t\t\t// Wait a bit before retrying\n\t\t\t\t\t\tconst delay = Math.pow(2, attempt) * 1000 // Exponential backoff\n\t\t\t\t\t\tconsole.log(`โณ Waiting ${delay}ms before retry...`)\n\t\t\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, delay))\n\t\t\t\t\t} catch (refreshError) {\n\t\t\t\t\t\tconsole.log(`โŒ Identity refresh failed:`, refreshError)\n\t\t\t\t\t\t// Continue to the retry logic\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\"โŒ Failed to resolve identity association error after all retries\"\n\t\t\t\t\t)\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\"๐Ÿ’ก Try running: pnpm with-env pnpm --filter @hybrd/xmtp refresh:identity\"\n\t\t\t\t\t)\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// For other errors, don't retry\n\t\t\t\tthrow error\n\t\t\t}\n\t\t}\n\t}\n\n\tthrow new Error(\"Max retries exceeded\")\n}\n\n// ===================================================================\n// Encryption Key Management\n// ===================================================================\n/**\n * Generate a random encryption key\n * @returns The encryption key as a hex string\n */\nexport const generateEncryptionKeyHex = () => {\n\tconst uint8Array = getRandomValues(new Uint8Array(32))\n\treturn uint8arraysToString(uint8Array, \"hex\")\n}\n\n/**\n * Get the encryption key from a hex string\n * @param hex - The hex string\n * @returns The encryption key as Uint8Array\n */\nconst getEncryptionKeyFromHex = (hex: string): Uint8Array => {\n\treturn fromString(hex, \"hex\")\n}\n\n// ===================================================================\n// Database Path Management\n// ===================================================================\nexport const getDbPath = async (description = \"xmtp\", storagePath?: string) => {\n\t// Allow custom storage path via environment variable\n\tconst customStoragePath = process.env.XMTP_STORAGE_PATH\n\n\tlet volumePath: string\n\n\tif (customStoragePath) {\n\t\t// Use custom storage path if provided\n\t\tvolumePath = path.isAbsolute(customStoragePath)\n\t\t\t? customStoragePath\n\t\t\t: path.resolve(process.cwd(), customStoragePath)\n\t} else if (storagePath) {\n\t\tvolumePath = path.isAbsolute(storagePath)\n\t\t\t? storagePath\n\t\t\t: path.resolve(process.cwd(), storagePath)\n\t} else {\n\t\t// Use existing logic as fallback\n\t\tconst projectRoot =\n\t\t\tprocess.env.PROJECT_ROOT || path.resolve(__dirname, \"../../..\")\n\n\t\t// Default storage path for local development\n\t\tvolumePath = path.join(projectRoot, \".data/xmtp\")\n\t}\n\n\tconst dbPath = `${volumePath}/${description}.db3`\n\n\tif (typeof globalThis !== \"undefined\" && \"XMTP_STORAGE\" in globalThis) {\n\t\ttry {\n\t\t\tconsole.log(`๐Ÿ“ฆ Using Cloudflare R2 storage for: ${dbPath}`)\n\n\t\t\tconst r2Bucket = (globalThis as any).XMTP_STORAGE\n\t\t\tconst remotePath = `xmtp-databases/${description}.db3`\n\n\t\t\ttry {\n\t\t\t\tconst existingObject = await r2Bucket.head(remotePath)\n\t\t\t\tif (existingObject) {\n\t\t\t\t\tconsole.log(`๐Ÿ“ฅ Downloading existing database from R2 storage...`)\n\n\t\t\t\t\tif (!fs.existsSync(volumePath)) {\n\t\t\t\t\t\tfs.mkdirSync(volumePath, { recursive: true })\n\t\t\t\t\t}\n\n\t\t\t\t\tconst object = await r2Bucket.get(remotePath)\n\t\t\t\t\tif (object) {\n\t\t\t\t\t\tconst fileData = await object.arrayBuffer()\n\t\t\t\t\t\tfs.writeFileSync(dbPath, new Uint8Array(fileData))\n\t\t\t\t\t\tconsole.log(`โœ… Database downloaded from R2 storage`)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconsole.log(`๐Ÿ“ No existing database found in R2 storage`)\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.log(`โš ๏ธ Failed to download database from R2 storage:`, error)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.log(`โš ๏ธ R2 storage not available:`, error)\n\t\t}\n\t}\n\n\tif (!fs.existsSync(volumePath)) {\n\t\tfs.mkdirSync(volumePath, { recursive: true })\n\t}\n\n\treturn dbPath\n}\n\nconst backupDbToPersistentStorage = async (\n\tdbPath: string,\n\tdescription: string\n) => {\n\tif (\n\t\ttypeof globalThis !== \"undefined\" &&\n\t\t\"XMTP_STORAGE\" in globalThis &&\n\t\tfs.existsSync(dbPath)\n\t) {\n\t\ttry {\n\t\t\tconsole.log(`๐Ÿ“ฆ Backing up database to R2 storage: ${dbPath}`)\n\n\t\t\tconst r2Bucket = (globalThis as any).XMTP_STORAGE\n\t\t\tconst remotePath = `xmtp-databases/${description}.db3`\n\n\t\t\tconst fileData = fs.readFileSync(dbPath)\n\t\t\tawait r2Bucket.put(remotePath, fileData)\n\t\t\tconsole.log(`โœ… Database backed up to R2 storage: ${remotePath}`)\n\t\t} catch (error) {\n\t\t\tconsole.log(`โš ๏ธ Failed to backup database to R2 storage:`, error)\n\t\t}\n\t}\n}\n\n// ===================================================================\n// Logging and Debugging\n// ===================================================================\nexport const logAgentDetails = async (\n\tclients: XmtpClient | XmtpClient[]\n): Promise<void> => {\n\tconst clientsByAddress = Array.isArray(clients)\n\t\t? clients.reduce<Record<string, XmtpClient[]>>((acc, XmtpClient) => {\n\t\t\t\tconst address = XmtpClient.accountIdentifier?.identifier ?? \"\"\n\t\t\t\tacc[address] = acc[address] ?? []\n\t\t\t\tacc[address].push(XmtpClient)\n\t\t\t\treturn acc\n\t\t\t}, {})\n\t\t: {\n\t\t\t\t[clients.accountIdentifier?.identifier ?? \"\"]: [clients]\n\t\t\t}\n\n\tfor (const [address, clientGroup] of Object.entries(clientsByAddress)) {\n\t\tconst firstClient = clientGroup[0]\n\t\tconst inboxId = firstClient?.inboxId\n\t\tconst environments = clientGroup\n\t\t\t.map((c) => c.options?.env ?? \"dev\")\n\t\t\t.join(\", \")\n\t\tconsole.log(`\\x1b[38;2;252;76;52m\n โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— \n โ•šโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ•‘โ•šโ•โ•โ–ˆโ–ˆโ•”โ•โ•โ•โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—\n โ•šโ–ˆโ–ˆโ–ˆโ•”โ• โ–ˆโ–ˆโ•”โ–ˆโ–ˆโ–ˆโ–ˆโ•”โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•\n โ–ˆโ–ˆโ•”โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•‘โ•šโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•”โ•โ•โ•โ• \n โ–ˆโ–ˆโ•”โ• โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•‘ โ•šโ•โ• โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ \n โ•šโ•โ• โ•šโ•โ•โ•šโ•โ• โ•šโ•โ• โ•šโ•โ• โ•šโ•โ• \n \\x1b[0m`)\n\n\t\tconst urls = [`http://xmtp.chat/dm/${address}`]\n\n\t\tconst conversations = await firstClient?.conversations.list()\n\n\t\tconsole.log(`\n โœ“ XMTP XmtpClient:\n โ€ข Address: ${address}\n โ€ข Conversations: ${conversations?.length}\n โ€ข InboxId: ${inboxId}\n โ€ข Networks: ${environments}\n ${urls.map((url) => `โ€ข URL: ${url}`).join(\"\\n\")}`)\n\t}\n}\n\n// ===================================================================\n// Environment Validation\n// ===================================================================\nexport function validateEnvironment(vars: string[]): Record<string, string> {\n\tconst missing = vars.filter((v) => !process.env[v])\n\n\tif (missing.length) {\n\t\ttry {\n\t\t\tconst envPath = path.resolve(process.cwd(), \".env\")\n\t\t\tif (fs.existsSync(envPath)) {\n\t\t\t\tconst envVars = fs\n\t\t\t\t\t.readFileSync(envPath, \"utf-8\")\n\t\t\t\t\t.split(\"\\n\")\n\t\t\t\t\t.filter((line) => line.trim() && !line.startsWith(\"#\"))\n\t\t\t\t\t.reduce<Record<string, string>>((acc, line) => {\n\t\t\t\t\t\tconst [key, ...val] = line.split(\"=\")\n\t\t\t\t\t\tif (key && val.length) acc[key.trim()] = val.join(\"=\").trim()\n\t\t\t\t\t\treturn acc\n\t\t\t\t\t}, {})\n\n\t\t\t\tmissing.forEach((v) => {\n\t\t\t\t\tif (envVars[v]) process.env[v] = envVars[v]\n\t\t\t\t})\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.error(e)\n\t\t\t/* ignore errors */\n\t\t}\n\n\t\tconst stillMissing = vars.filter((v) => !process.env[v])\n\t\tif (stillMissing.length) {\n\t\t\tconsole.error(\"Missing env vars:\", stillMissing.join(\", \"))\n\t\t\tprocess.exit(1)\n\t\t}\n\t}\n\n\treturn vars.reduce<Record<string, string>>((acc, key) => {\n\t\tacc[key] = process.env[key] as string\n\t\treturn acc\n\t}, {})\n}\n\n/**\n * Diagnose XMTP environment and identity issues (internal use only)\n */\nasync function diagnoseXMTPIdentityIssue(\n\tclient: XmtpClient,\n\tinboxId: string,\n\tenvironment: string\n): Promise<{\n\tcanResolve: boolean\n\tsuggestions: string[]\n\tdetails: Record<string, any>\n}> {\n\tconst suggestions: string[] = []\n\tconst details: Record<string, any> = {\n\t\tenvironment,\n\t\tinboxId,\n\t\ttimestamp: new Date().toISOString()\n\t}\n\n\ttry {\n\t\t// Try to resolve the inbox state\n\t\tconst inboxState = await client.preferences.inboxStateFromInboxIds([\n\t\t\tinboxId\n\t\t])\n\n\t\tif (inboxState.length === 0) {\n\t\t\tsuggestions.push(\n\t\t\t\t`Inbox ID ${inboxId} not found in ${environment} environment`\n\t\t\t)\n\t\t\tsuggestions.push(\n\t\t\t\t\"Try switching XMTP_ENV to 'dev' if currently 'production' or vice versa\"\n\t\t\t)\n\t\t\tsuggestions.push(\n\t\t\t\t\"Verify the user has created an identity on this XMTP network\"\n\t\t\t)\n\t\t\tdetails.inboxStateFound = false\n\t\t\treturn { canResolve: false, suggestions, details }\n\t\t}\n\n\t\tconst inbox = inboxState[0]\n\t\tif (!inbox) {\n\t\t\tsuggestions.push(\"Inbox state returned empty data\")\n\t\t\tdetails.inboxStateFound = false\n\t\t\treturn { canResolve: false, suggestions, details }\n\t\t}\n\n\t\tdetails.inboxStateFound = true\n\t\tdetails.identifierCount = inbox.identifiers?.length || 0\n\n\t\tif (!inbox.identifiers || inbox.identifiers.length === 0) {\n\t\t\tsuggestions.push(\"Inbox found but has no identifiers\")\n\t\t\tsuggestions.push(\"This indicates incomplete identity registration\")\n\t\t\tsuggestions.push(\"User may need to re-register their identity on XMTP\")\n\t\t\tdetails.hasIdentifiers = false\n\t\t\treturn { canResolve: false, suggestions, details }\n\t\t}\n\n\t\t// Successfully resolved\n\t\tdetails.hasIdentifiers = true\n\t\tdetails.resolvedAddress = inbox.identifiers[0]?.identifier\n\t\treturn {\n\t\t\tcanResolve: true,\n\t\t\tsuggestions: [\"Identity resolved successfully\"],\n\t\t\tdetails\n\t\t}\n\t} catch (error) {\n\t\tconst errorMessage = error instanceof Error ? error.message : String(error)\n\t\tdetails.error = errorMessage\n\n\t\tif (errorMessage.includes(\"Association error\")) {\n\t\t\tsuggestions.push(\"XMTP identity association error detected\")\n\t\t\tsuggestions.push(\n\t\t\t\t\"Check if user exists on the correct XMTP environment (dev vs production)\"\n\t\t\t)\n\t\t\tsuggestions.push(\n\t\t\t\t\"Identity may need to be recreated on the current environment\"\n\t\t\t)\n\t\t}\n\n\t\tif (errorMessage.includes(\"Missing identity update\")) {\n\t\t\tsuggestions.push(\"Missing identity updates in XMTP network\")\n\t\t\tsuggestions.push(\"This can indicate network sync issues\")\n\t\t\tsuggestions.push(\"Wait a few minutes and retry, or recreate identity\")\n\t\t}\n\n\t\tif (errorMessage.includes(\"database\") || errorMessage.includes(\"storage\")) {\n\t\t\tsuggestions.push(\"XMTP local database/storage issue\")\n\t\t\tsuggestions.push(\"Try clearing XMTP database and resyncing\")\n\t\t\tsuggestions.push(\"Check .data/xmtp directory permissions\")\n\t\t}\n\n\t\tsuggestions.push(\"Consider testing with a fresh XMTP identity\")\n\t\treturn { canResolve: false, suggestions, details }\n\t}\n}\n\n// ===================================================================\n// Enhanced Connection Management & Health Monitoring\n// ===================================================================\n\nexport interface XMTPConnectionConfig {\n\tmaxRetries?: number\n\tretryDelayMs?: number\n\thealthCheckIntervalMs?: number\n\tconnectionTimeoutMs?: number\n\treconnectOnFailure?: boolean\n}\n\nexport interface XMTPConnectionHealth {\n\tisConnected: boolean\n\tlastHealthCheck: Date\n\tconsecutiveFailures: number\n\ttotalReconnects: number\n\tavgResponseTime: number\n}\n\nexport class XMTPConnectionManager {\n\tprivate client: XmtpClient | null = null\n\tprivate privateKey: string\n\tprivate config: Required<XMTPConnectionConfig>\n\tprivate health: XMTPConnectionHealth\n\tprivate healthCheckTimer: NodeJS.Timeout | null = null\n\tprivate isReconnecting = false\n\n\tconstructor(privateKey: string, config: XMTPConnectionConfig = {}) {\n\t\tthis.privateKey = privateKey\n\t\tthis.config = {\n\t\t\tmaxRetries: config.maxRetries ?? 5,\n\t\t\tretryDelayMs: config.retryDelayMs ?? 1000,\n\t\t\thealthCheckIntervalMs: config.healthCheckIntervalMs ?? 30000,\n\t\t\tconnectionTimeoutMs: config.connectionTimeoutMs ?? 10000,\n\t\t\treconnectOnFailure: config.reconnectOnFailure ?? true\n\t\t}\n\n\t\tthis.health = {\n\t\t\tisConnected: false,\n\t\t\tlastHealthCheck: new Date(),\n\t\t\tconsecutiveFailures: 0,\n\t\t\ttotalReconnects: 0,\n\t\t\tavgResponseTime: 0\n\t\t}\n\t}\n\n\tasync connect(persist = false): Promise<XmtpClient> {\n\t\tif (this.client && this.health.isConnected) {\n\t\t\treturn this.client\n\t\t}\n\n\t\tlet attempt = 0\n\t\twhile (attempt < this.config.maxRetries) {\n\t\t\ttry {\n\t\t\t\tconsole.log(\n\t\t\t\t\t`๐Ÿ”„ XMTP connection attempt ${attempt + 1}/${this.config.maxRetries}`\n\t\t\t\t)\n\n\t\t\t\tthis.client = await createXMTPClient(this.privateKey, { persist })\n\t\t\t\tthis.health.isConnected = true\n\t\t\t\tthis.health.consecutiveFailures = 0\n\n\t\t\t\t// Start health monitoring\n\t\t\t\tthis.startHealthMonitoring()\n\n\t\t\t\tconsole.log(\"โœ… XMTP client connected successfully\")\n\t\t\t\treturn this.client\n\t\t\t} catch (error) {\n\t\t\t\tattempt++\n\t\t\t\tthis.health.consecutiveFailures++\n\n\t\t\t\tconsole.error(`โŒ XMTP connection attempt ${attempt} failed:`, error)\n\n\t\t\t\tif (attempt < this.config.maxRetries) {\n\t\t\t\t\tconst delay = this.config.retryDelayMs * Math.pow(2, attempt - 1)\n\t\t\t\t\tconsole.log(`โณ Retrying in ${delay}ms...`)\n\t\t\t\t\tawait this.sleep(delay)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t`Failed to connect to XMTP after ${this.config.maxRetries} attempts`\n\t\t)\n\t}\n\n\t// private async createClientWithTimeout(persist: boolean): Promise<XmtpClient> {\n\t// const timeoutPromise = new Promise<never>((_, reject) => {\n\t// setTimeout(\n\t// () => reject(new Error(\"Connection timeout\")),\n\t// this.config.connectionTimeoutMs\n\t// )\n\t// })\n\n\t// const clientPromise = createXMTPClient(this.signer, { persist })\n\n\t// return Promise.race([clientPromise, timeoutPromise])\n\t// }\n\n\tprivate startHealthMonitoring(): void {\n\t\tif (this.healthCheckTimer) {\n\t\t\tclearInterval(this.healthCheckTimer)\n\t\t}\n\n\t\tthis.healthCheckTimer = setInterval(() => {\n\t\t\tthis.performHealthCheck()\n\t\t}, this.config.healthCheckIntervalMs)\n\t}\n\n\tprivate async performHealthCheck(): Promise<void> {\n\t\tif (!this.client) return\n\n\t\tconst startTime = Date.now()\n\n\t\ttry {\n\t\t\t// Simple health check: try to list conversations\n\t\t\tawait this.client.conversations.list()\n\n\t\t\tconst responseTime = Date.now() - startTime\n\t\t\tthis.health.avgResponseTime =\n\t\t\t\t(this.health.avgResponseTime + responseTime) / 2\n\t\t\tthis.health.lastHealthCheck = new Date()\n\t\t\tthis.health.consecutiveFailures = 0\n\t\t\tthis.health.isConnected = true\n\n\t\t\tconsole.log(`๐Ÿ’“ XMTP health check passed (${responseTime}ms)`)\n\t\t} catch (error) {\n\t\t\tthis.health.consecutiveFailures++\n\t\t\tthis.health.isConnected = false\n\n\t\t\tconsole.error(`๐Ÿ’” XMTP health check failed:`, error)\n\n\t\t\t// Trigger reconnection if enabled\n\t\t\tif (this.config.reconnectOnFailure && !this.isReconnecting) {\n\t\t\t\tthis.handleConnectionFailure()\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async handleConnectionFailure(): Promise<void> {\n\t\tif (this.isReconnecting) return\n\n\t\tthis.isReconnecting = true\n\t\tthis.health.totalReconnects++\n\n\t\tconsole.log(\"๐Ÿ”„ XMTP connection lost, attempting to reconnect...\")\n\n\t\ttry {\n\t\t\tthis.client = null\n\t\t\tawait this.connect()\n\t\t\tconsole.log(\"โœ… XMTP reconnection successful\")\n\t\t} catch (error) {\n\t\t\tconsole.error(\"โŒ XMTP reconnection failed:\", error)\n\t\t} finally {\n\t\t\tthis.isReconnecting = false\n\t\t}\n\t}\n\n\tprivate sleep(ms: number): Promise<void> {\n\t\treturn new Promise((resolve) => setTimeout(resolve, ms))\n\t}\n\n\tgetHealth(): XMTPConnectionHealth {\n\t\treturn { ...this.health }\n\t}\n\n\tgetClient(): XmtpClient | null {\n\t\treturn this.client\n\t}\n\n\tasync disconnect(): Promise<void> {\n\t\tif (this.healthCheckTimer) {\n\t\t\tclearInterval(this.healthCheckTimer)\n\t\t\tthis.healthCheckTimer = null\n\t\t}\n\n\t\tthis.client = null\n\t\tthis.health.isConnected = false\n\t\tconsole.log(\"๐Ÿ”Œ XMTP client disconnected\")\n\t}\n}\n\n// Enhanced client creation with connection management\nexport async function createXMTPConnectionManager(\n\tprivateKey: string,\n\tconfig?: XMTPConnectionConfig\n): Promise<XMTPConnectionManager> {\n\tconst manager = new XMTPConnectionManager(privateKey, config)\n\tawait manager.connect()\n\treturn manager\n}\n\n// ===================================================================\n// User Address Resolution with Auto-Refresh\n// ===================================================================\n","import { Client, type Signer } from \"@xmtp/node-sdk\"\nimport { createSigner } from \"../src/client\"\n\n// Function to revoke old installations when hitting the limit\nexport async function revokeOldInstallations(signer: Signer, inboxId?: string) {\n\tconsole.log(\"๐Ÿ”ง Attempting to revoke old installations...\")\n\n\ttry {\n\t\t// If we don't have the inboxId, we need to extract it from a temporary client attempt\n\t\tif (!inboxId) {\n\t\t\tconsole.log(\"โ„น๏ธ No inboxId provided, cannot revoke installations\")\n\t\t\treturn false\n\t\t}\n\n\t\tconst inboxStates = await Client.inboxStateFromInboxIds(\n\t\t\t[inboxId],\n\t\t\tprocess.env.XMTP_ENV as \"dev\" | \"production\"\n\t\t)\n\n\t\tif (!inboxStates[0]) {\n\t\t\tconsole.log(\"โŒ No inbox state found for the provided inboxId\")\n\t\t\treturn false\n\t\t}\n\n\t\tconst toRevokeInstallationBytes = inboxStates[0].installations.map(\n\t\t\t(i: { bytes: Uint8Array }) => i.bytes\n\t\t)\n\n\t\tawait Client.revokeInstallations(\n\t\t\tsigner,\n\t\t\tinboxId,\n\t\t\ttoRevokeInstallationBytes,\n\t\t\tprocess.env.XMTP_ENV as \"dev\" | \"production\"\n\t\t)\n\n\t\tconst resultingStates = await Client.inboxStateFromInboxIds(\n\t\t\t[inboxId],\n\t\t\tprocess.env.XMTP_ENV as \"dev\" | \"production\"\n\t\t)\n\n\t\tconsole.log(\n\t\t\t`๐Ÿ“‹ Revoked installations: ${toRevokeInstallationBytes.length} installations`\n\t\t)\n\t\tconsole.log(\n\t\t\t`๐Ÿ“‹ Resulting state: ${resultingStates[0]?.installations.length || 0} installations`\n\t\t)\n\n\t\treturn true\n\t} catch (error) {\n\t\tconsole.error(\"โŒ Error during installation revocation:\", error)\n\t\treturn false\n\t}\n}\n\n// CLI script to revoke installations\nasync function main() {\n\tconst { XMTP_WALLET_KEY } = process.env\n\tconst inboxId = process.argv[2]\n\n\tif (!XMTP_WALLET_KEY) {\n\t\tconsole.error(\"โŒ XMTP_WALLET_KEY is required\")\n\t\tprocess.exit(1)\n\t}\n\n\tif (!inboxId) {\n\t\tconsole.error(\"โŒ InboxID is required as CLI argument\")\n\t\tconsole.error(\"Usage: tsx revoke-installations.ts <inboxId>\")\n\t\tprocess.exit(1)\n\t}\n\n\tconst signer = createSigner(XMTP_WALLET_KEY)\n\tconst identifier = await signer.getIdentifier()\n\tconst address = identifier.identifier\n\n\tconsole.log(`๐Ÿ”‘ Wallet Address: ${address}`)\n\tconsole.log(`๐Ÿ“‹ Inbox ID: ${inboxId}`)\n\n\t// Try to revoke installations\n\tconst success = await revokeOldInstallations(signer, inboxId)\n\n\tif (success) {\n\t\tconsole.log(\"โœ… Successfully revoked installations\")\n\t} else {\n\t\tconsole.log(\"โŒ Failed to revoke installations\")\n\t\tprocess.exit(1)\n\t}\n}\n\n// Run if called directly\nif (import.meta.url === `file://${process.argv[1]}`) {\n\tmain().catch((error) => {\n\t\tconsole.error(\"๐Ÿ’ฅ Fatal error:\", error)\n\t\tprocess.exit(1)\n\t})\n}\n","import {\n\tAgent as XmtpAgent,\n\tXmtpEnv,\n\tcreateSigner,\n\tcreateUser\n} from \"@xmtp/agent-sdk\"\n\nimport type {\n\tAgentMessage,\n\tAgentRuntime,\n\tBehaviorContext,\n\tBehaviorRegistry,\n\tPlugin,\n\tPluginContext,\n\tXmtpClient,\n\tXmtpConversation,\n\tXmtpMessage\n} from \"@hybrd/types\"\nimport { logger } from \"@hybrd/utils\"\nimport { randomUUID } from \"node:crypto\"\nimport { createXMTPClient, getDbPath } from \"./client\"\nimport { ContentTypeReply, ContentTypeText, type Reply } from \"./index\"\n\n// Re-export types from @hybrd/types for backward compatibility\nexport type { Plugin }\n\n/**\n * Send a response with threading support\n */\nasync function sendResponse(\n\tconversation: XmtpConversation,\n\ttext: string,\n\toriginalMessageId: string,\n\tbehaviorContext?: BehaviorContext\n) {\n\tconst shouldThread = behaviorContext?.sendOptions?.threaded ?? false\n\n\tif (shouldThread) {\n\t\t// Send as a reply to the original message\n\t\ttry {\n\t\t\tconst reply: Reply = {\n\t\t\t\treference: originalMessageId,\n\t\t\t\tcontentType: ContentTypeText,\n\t\t\t\tcontent: text\n\t\t\t}\n\t\t\tawait conversation.send(reply, ContentTypeReply)\n\t\t\tlogger.debug(\n\t\t\t\t`โœ… [sendResponse] Threaded reply sent successfully to message ${originalMessageId}`\n\t\t\t)\n\t\t} catch (error) {\n\t\t\tlogger.error(\n\t\t\t\t`โŒ [sendResponse] Failed to send threaded reply to message ${originalMessageId}:`,\n\t\t\t\terror\n\t\t\t)\n\t\t\t// Fall back to regular message if threaded reply fails\n\t\t\tlogger.debug(`๐Ÿ”„ [sendResponse] Falling back to regular message`)\n\t\t\tawait conversation.send(text)\n\t\t}\n\t} else {\n\t\t// Send as a regular message\n\t\tawait conversation.send(text)\n\t}\n}\n\n/**\n * XMTP Plugin that provides XMTP functionality to the agent\n *\n * @description\n * This plugin integrates XMTP messaging capabilities into the agent's\n * HTTP server. It mounts the XMTP endpoints for handling XMTP tools requests.\n */\nexport function XMTPPlugin(): Plugin<PluginContext> {\n\treturn {\n\t\tname: \"xmtp\",\n\t\tdescription: \"Provides XMTP messaging functionality\",\n\t\tapply: async (app, context): Promise<void> => {\n\t\t\tconst {\n\t\t\t\tXMTP_WALLET_KEY,\n\t\t\t\tXMTP_DB_ENCRYPTION_KEY,\n\t\t\t\tXMTP_ENV = \"production\"\n\t\t\t} = process.env\n\n\t\t\tconst { agent } = context\n\t\t\tconst pluginContext = context as PluginContext & {\n\t\t\t\tbehaviors?: BehaviorRegistry\n\t\t\t}\n\n\t\t\tif (!XMTP_WALLET_KEY) {\n\t\t\t\tthrow new Error(\"XMTP_WALLET_KEY must be set\")\n\t\t\t}\n\n\t\t\tif (!XMTP_DB_ENCRYPTION_KEY) {\n\t\t\t\tthrow new Error(\"XMTP_DB_ENCRYPTION_KEY must be set\")\n\t\t\t}\n\n\t\t\tconst user = createUser(XMTP_WALLET_KEY as `0x${string}`)\n\t\t\tconst signer = createSigner(user)\n\n\t\t\tconst xmtpClient = await createXMTPClient(\n\t\t\t\tXMTP_WALLET_KEY as `0x${string}`\n\t\t\t)\n\n\t\t\t// Start a reliable node client stream to process incoming messages\n\t\t\tasync function startNodeStream() {\n\t\t\t\ttry {\n\t\t\t\t\tlogger.debug(\"๐ŸŽง XMTP node client stream initializing\")\n\t\t\t\t\tconst stream = await xmtpClient.conversations.streamAllMessages()\n\t\t\t\t\tlogger.debug(\"๐ŸŽง XMTP node client stream started\")\n\t\t\t\t\tfor await (const msg of stream) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tif (msg.senderInboxId === xmtpClient.inboxId) continue\n\n\t\t\t\t\t\t\tconst content =\n\t\t\t\t\t\t\t\ttypeof msg.content === \"string\"\n\t\t\t\t\t\t\t\t\t? msg.content\n\t\t\t\t\t\t\t\t\t: (() => {\n\t\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\t\treturn JSON.stringify(msg.content)\n\t\t\t\t\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t\t\t\t\treturn String(msg.content)\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t})()\n\n\t\t\t\t\t\t\tconst conversation =\n\t\t\t\t\t\t\t\tawait xmtpClient.conversations.getConversationById(\n\t\t\t\t\t\t\t\t\tmsg.conversationId\n\t\t\t\t\t\t\t\t)\n\n\t\t\t\t\t\t\tif (!conversation) {\n\t\t\t\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t\t\t\t`โš ๏ธ XMTP conversation not found: ${msg.conversationId}`\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst messages: AgentMessage[] = [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tid: randomUUID(),\n\t\t\t\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\t\t\t\tparts: [{ type: \"text\", text: content }]\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t]\n\n\t\t\t\t\t\t\tconst baseRuntime: AgentRuntime = {\n\t\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\t\tmessage: msg,\n\t\t\t\t\t\t\t\txmtpClient\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst runtime = await agent.createRuntimeContext(baseRuntime)\n\n\t\t\t\t\t\t\t// Execute pre-response behaviors\n\t\t\t\t\t\t\tif (pluginContext.behaviors) {\n\t\t\t\t\t\t\t\tconst behaviorContext: BehaviorContext = {\n\t\t\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\t\t\tmessage: msg as XmtpMessage\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tawait pluginContext.behaviors.executeBefore(behaviorContext)\n\n\t\t\t\t\t\t\t\t// Check if message was filtered out by any behavior\n\t\t\t\t\t\t\t\tif (behaviorContext.sendOptions?.filtered) {\n\t\t\t\t\t\t\t\t\tcontinue // Skip processing this message\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst { text } = await agent.generate(messages, { runtime })\n\n\t\t\t\t\t\t\t// Create behavior context for send options\n\t\t\t\t\t\t\tconst behaviorContext: BehaviorContext = {\n\t\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\t\tmessage: msg as XmtpMessage,\n\t\t\t\t\t\t\t\tresponse: text\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Execute post-response behaviors\n\t\t\t\t\t\t\tif (pluginContext.behaviors) {\n\t\t\t\t\t\t\t\tawait pluginContext.behaviors.executeAfter(behaviorContext)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Check if message was filtered out by filterMessages behavior\n\t\t\t\t\t\t\tif (behaviorContext?.sendOptions?.filtered) {\n\t\t\t\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t\t\t\t`๐Ÿ”‡ [XMTP Plugin] Skipping response due to message being filtered`\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Send the response with threading support\n\t\t\t\t\t\t\tawait sendResponse(conversation, text, msg.id, behaviorContext)\n\t\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t\tlogger.error(\"โŒ Error processing XMTP message:\", err)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tlogger.error(\"โŒ XMTP node client stream failed:\", err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst enabledFromEnv = process.env.XMTP_ENABLE_NODE_STREAM\n\t\t\tconst isNodeStreamEnabled =\n\t\t\t\tenabledFromEnv === undefined\n\t\t\t\t\t? true\n\t\t\t\t\t: !/^(false|0|off|no)$/i.test(String(enabledFromEnv))\n\n\t\t\tif (isNodeStreamEnabled) void startNodeStream()\n\n\t\t\tconst address = user.account.address.toLowerCase()\n\t\t\tconst agentDbPath = await getDbPath(\n\t\t\t\t`agent-${XMTP_ENV || \"dev\"}-${address}`\n\t\t\t)\n\t\t\tlogger.debug(`๐Ÿ“ Using agent listener database path: ${agentDbPath}`)\n\n\t\t\tconst xmtp = await XmtpAgent.create(signer, {\n\t\t\t\tenv: XMTP_ENV as XmtpEnv,\n\t\t\t\tdbPath: agentDbPath\n\t\t\t})\n\n\t\t\txmtp.on(\"reaction\", async ({ conversation, message }) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst text = message.content.content\n\t\t\t\t\tconst messages: AgentMessage[] = [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tid: randomUUID(),\n\t\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\t\tparts: [{ type: \"text\", text }]\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\n\t\t\t\t\tconst baseRuntime: AgentRuntime = {\n\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\txmtpClient\n\t\t\t\t\t}\n\n\t\t\t\t\tconst runtime = await agent.createRuntimeContext(baseRuntime)\n\n\t\t\t\t\t// Execute pre-response behaviors\n\t\t\t\t\tif (context.behaviors) {\n\t\t\t\t\t\tconst behaviorContext: BehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait context.behaviors.executeBefore(behaviorContext)\n\t\t\t\t\t}\n\n\t\t\t\t\tconst { text: reply } = await agent.generate(messages, { runtime })\n\n\t\t\t\t\t// Execute post-response behaviors\n\t\t\t\t\tlet behaviorContext: BehaviorContext | undefined\n\t\t\t\t\tif (context.behaviors) {\n\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\t\tresponse: reply\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait context.behaviors.executeAfter(behaviorContext)\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Create minimal context for send options\n\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\t\tresponse: reply\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check if message was filtered out by filterMessages behavior\n\t\t\t\t\tif (behaviorContext?.sendOptions?.filtered) {\n\t\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t\t`๐Ÿ”‡ [XMTP Plugin] Skipping reaction response due to message being filtered`\n\t\t\t\t\t\t)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\tawait sendResponse(\n\t\t\t\t\t\tconversation as unknown as XmtpConversation,\n\t\t\t\t\t\treply,\n\t\t\t\t\t\tmessage.id,\n\t\t\t\t\t\tbehaviorContext\n\t\t\t\t\t)\n\t\t\t\t} catch (err) {\n\t\t\t\t\tlogger.error(\"โŒ Error handling reaction:\", err)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\txmtp.on(\"reply\", async ({ conversation, message }) => {\n\t\t\t\ttry {\n\t\t\t\t\t// TODO - why isn't this typed better?\n\t\t\t\t\tconst text = message.content.content as string\n\t\t\t\t\tconst messages: AgentMessage[] = [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tid: randomUUID(),\n\t\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\t\tparts: [{ type: \"text\", text }]\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\n\t\t\t\t\tconst baseRuntime: AgentRuntime = {\n\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\txmtpClient\n\t\t\t\t\t}\n\n\t\t\t\t\tconst runtime = await agent.createRuntimeContext(baseRuntime)\n\n\t\t\t\t\t// Execute pre-response behaviors\n\t\t\t\t\tlet behaviorContext: BehaviorContext | undefined\n\t\t\t\t\tif (context.behaviors) {\n\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait context.behaviors.executeBefore(behaviorContext)\n\n\t\t\t\t\t\t// Check if behaviors were stopped early (e.g., due to filtering)\n\t\t\t\t\t\tif (behaviorContext.stopped) {\n\t\t\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t\t\t`๐Ÿ”‡ [XMTP Plugin] Skipping reply response due to behavior chain being stopped`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst { text: reply } = await agent.generate(messages, { runtime })\n\n\t\t\t\t\t// Execute post-response behaviors\n\t\t\t\t\tif (context.behaviors) {\n\t\t\t\t\t\tif (!behaviorContext) {\n\t\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\t\t\tresponse: reply\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tbehaviorContext.response = reply\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait context.behaviors.executeAfter(behaviorContext)\n\n\t\t\t\t\t\t// Check if post behaviors were stopped early\n\t\t\t\t\t\tif (behaviorContext.stopped) {\n\t\t\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t\t\t`๐Ÿ”‡ [XMTP Plugin] Skipping reply response due to post-behavior chain being stopped`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Create minimal context for send options\n\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\t\tresponse: reply\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tawait sendResponse(\n\t\t\t\t\t\tconversation as unknown as XmtpConversation,\n\t\t\t\t\t\treply,\n\t\t\t\t\t\tmessage.id,\n\t\t\t\t\t\tbehaviorContext\n\t\t\t\t\t)\n\t\t\t\t} catch (err) {\n\t\t\t\t\tlogger.error(\"โŒ Error handling reply:\", err)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\txmtp.on(\"text\", async ({ conversation, message }) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst text = message.content\n\t\t\t\t\tconst messages: AgentMessage[] = [\n\t\t\t\t\t\t{ id: randomUUID(), role: \"user\", parts: [{ type: \"text\", text }] }\n\t\t\t\t\t]\n\n\t\t\t\t\tconst baseRuntime: AgentRuntime = {\n\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\txmtpClient\n\t\t\t\t\t}\n\n\t\t\t\t\tconst runtime = await agent.createRuntimeContext(baseRuntime)\n\n\t\t\t\t\t// Execute pre-response behaviors\n\t\t\t\t\tlet behaviorContext: BehaviorContext | undefined\n\t\t\t\t\tif (context.behaviors) {\n\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait context.behaviors.executeBefore(behaviorContext)\n\n\t\t\t\t\t\t// Check if behaviors were stopped early (e.g., due to filtering)\n\t\t\t\t\t\tif (behaviorContext.stopped) {\n\t\t\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t\t\t`๐Ÿ”‡ [XMTP Plugin] Skipping text response due to behavior chain being stopped`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst { text: reply } = await agent.generate(messages, { runtime })\n\n\t\t\t\t\t// Execute post-response behaviors\n\t\t\t\t\tif (context.behaviors) {\n\t\t\t\t\t\tif (!behaviorContext) {\n\t\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\t\t\tresponse: reply\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tbehaviorContext.response = reply\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait context.behaviors.executeAfter(behaviorContext)\n\n\t\t\t\t\t\t// Check if post behaviors were stopped early\n\t\t\t\t\t\tif (behaviorContext.stopped) {\n\t\t\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t\t\t`๐Ÿ”‡ [XMTP Plugin] Skipping text response due to post-behavior chain being stopped`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Create minimal context for send options\n\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\t\tresponse: reply\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tawait sendResponse(\n\t\t\t\t\t\tconversation as unknown as XmtpConversation,\n\t\t\t\t\t\treply,\n\t\t\t\t\t\tmessage.id,\n\t\t\t\t\t\tbehaviorContext\n\t\t\t\t\t)\n\t\t\t\t} catch (err) {\n\t\t\t\t\tlogger.error(\"โŒ Error handling text:\", err)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\t// Event handlers removed due to incompatibility with current XMTP agent SDK\n\n\t\t\tvoid xmtp\n\t\t\t\t.start()\n\t\t\t\t.then(() => logger.debug(\"โœ… XMTP agent listener started\"))\n\t\t\t\t.catch((err) =>\n\t\t\t\t\tconsole.error(\"โŒ XMTP agent listener failed to start:\", err)\n\t\t\t\t)\n\t\t}\n\t}\n}\n","import { Context } from \"hono\"\nimport jwt from \"jsonwebtoken\"\nimport { logger } from \"@hybrd/utils\"\n\nexport interface XMTPToolsPayload {\n\taction: \"send\" | \"reply\" | \"react\" | \"transaction\" | \"blockchain-event\"\n\tconversationId: string\n\t// Action-specific data\n\tcontent?: string\n\treferenceMessageId?: string\n\temoji?: string\n\tactionType?: \"added\" | \"removed\"\n\tfromAddress?: string\n\tchainId?: string\n\tcalls?: Array<{\n\t\tto: string\n\t\tdata: string\n\t\tmetadata?: {\n\t\t\tdescription: string\n\t\t\ttransactionType: string\n\t\t}\n\t}>\n\t// Metadata\n\tissued: number\n\texpires: number\n}\n\n/**\n * Validates token and returns payload for both GET and POST endpoints\n *\n * @param {Context} c - Hono context object containing request information\n * @returns {XMTPToolsPayload | null} The validated payload or null if invalid\n *\n * @description\n * Supports two authentication methods:\n * - Authorization header with Bearer token (for POST endpoints)\n * - Query parameter token (for GET endpoints)\n *\n * @example\n * ```typescript\n * app.post(\"/api/endpoint\", async (c) => {\n * const payload = getValidatedPayload(c);\n * if (!payload) {\n * return c.json({ error: \"Invalid token\" }, 401);\n * }\n * // Use payload data\n * });\n * ```\n */\nexport function getValidatedPayload(c: Context): XMTPToolsPayload | null {\n\t// Try Authorization header first (for POST endpoints)\n\tconst authHeader = c.req.header(\"Authorization\")\n\tif (authHeader?.startsWith(\"Bearer \")) {\n\t\tconst token = authHeader.substring(7) // Remove \"Bearer \" prefix\n\t\treturn validateXMTPToolsToken(token)\n\t}\n\n\t// Fall back to query parameter (for GET endpoints)\n\tconst token = c.req.query(\"token\")\n\tif (!token) {\n\t\treturn null\n\t}\n\n\treturn validateXMTPToolsToken(token)\n}\n\n/**\n * Gets the JWT secret for token signing, with lazy initialization\n * Uses XMTP_DB_ENCRYPTION_KEY environment variable for consistency\n * Only falls back to development secret in development/test environments\n */\nfunction getJwtSecret(): string {\n\tconst secret = process.env.XMTP_DB_ENCRYPTION_KEY\n\tconst nodeEnv = process.env.NODE_ENV || \"development\"\n\n\t// In production, require a real JWT secret\n\tif (nodeEnv === \"production\" && !secret) {\n\t\tthrow new Error(\n\t\t\t\"XMTP_DB_ENCRYPTION_KEY environment variable is required in production. \" +\n\t\t\t\t\"Generate a secure random secret for JWT token signing.\"\n\t\t)\n\t}\n\n\t// In development/test, allow fallback but warn only when actually used\n\tif (!secret) {\n\t\tlogger.warn(\n\t\t\t\"โš ๏ธ [SECURITY] Using fallback JWT secret for development. \" +\n\t\t\t\t\"Set XMTP_DB_ENCRYPTION_KEY environment variable for production.\"\n\t\t)\n\t\treturn \"fallback-secret-for-dev-only\"\n\t}\n\n\treturn secret\n}\n\n/**\n * Gets the API key for authentication, with lazy initialization\n * Requires XMTP_API_KEY environment variable in production\n * Only falls back to development key in development/test environments\n */\nfunction getApiKey(): string {\n\tconst apiKey = process.env.XMTP_API_KEY\n\tconst nodeEnv = process.env.NODE_ENV || \"development\"\n\n\t// In production, require a real API key\n\tif (nodeEnv === \"production\" && !apiKey) {\n\t\tthrow new Error(\n\t\t\t\"XMTP_API_KEY environment variable is required in production. \" +\n\t\t\t\t\"Generate a secure random API key for authentication.\"\n\t\t)\n\t}\n\n\t// In development/test, allow fallback but warn only when actually used\n\tif (!apiKey) {\n\t\tlogger.warn(\n\t\t\t\"โš ๏ธ [SECURITY] Using fallback API key for development. \" +\n\t\t\t\t\"Set XMTP_API_KEY environment variable for production.\"\n\t\t)\n\t\treturn \"fallback-api-key-for-dev-only\"\n\t}\n\n\treturn apiKey\n}\n\n/**\n * JWT token expiry time in seconds (5 minutes)\n */\nconst JWT_EXPIRY = 5 * 60 // 5 minutes in seconds\n\n/**\n * Generates a signed JWT token for XMTP tools authentication\n *\n * @param {Omit<XMTPToolsPayload, \"issued\" | \"expires\">} payload - Token payload without timestamp fields\n * @returns {string} Signed JWT token\n *\n * @description\n * Creates a JWT token with automatic timestamp fields:\n * - issued: Current timestamp\n * - expires: Current timestamp + JWT_EXPIRY\n *\n * @example\n * ```typescript\n * const token = generateXMTPToolsToken({\n * action: \"send\",\n * conversationId: \"0x123...\"\n * });\n * ```\n */\nexport function generateXMTPToolsToken(\n\tpayload: Omit<XMTPToolsPayload, \"issued\" | \"expires\">\n): string {\n\tconst startTime = performance.now()\n\tlogger.debug(\"๐Ÿ” [JWT] Starting token generation...\")\n\n\tconst now = Math.floor(Date.now() / 1000)\n\tconst fullPayload: XMTPToolsPayload = {\n\t\t...payload,\n\t\tissued: now,\n\t\texpires: now + JWT_EXPIRY\n\t}\n\n\tconst token = jwt.sign(fullPayload, getJwtSecret(), {\n\t\texpiresIn: JWT_EXPIRY\n\t})\n\n\tconst endTime = performance.now()\n\tlogger.debug(\n\t\t`๐Ÿ” [JWT] Token generation completed in ${(endTime - startTime).toFixed(2)}ms`\n\t)\n\n\treturn token\n}\n\n/**\n * Validates an XMTP tools token using either API key or JWT verification\n *\n * @param {string} token - Token to validate (either API key or JWT)\n * @returns {XMTPToolsPayload | null} Validated payload or null if invalid\n *\n * @description\n * Supports two authentication methods in order of precedence:\n * 1. API key authentication - Direct comparison with XMTP_API_KEY\n * 2. JWT token authentication - Signature verification and expiry check\n *\n * For API key authentication, returns a default payload with 1-hour expiry.\n * For JWT authentication, validates signature and checks expiry timestamp.\n *\n * @example\n * ```typescript\n * const payload = validateXMTPToolsToken(userToken);\n * if (payload) {\n * console.log(`Action: ${payload.action}`);\n * console.log(`Conversation: ${payload.conversationId}`);\n * }\n * ```\n */\nexport function validateXMTPToolsToken(token: string): XMTPToolsPayload | null {\n\tconst startTime = performance.now()\n\tlogger.debug(\"๐Ÿ” [JWT] Starting token validation...\")\n\n\t// First try API key authentication\n\tif (token === getApiKey()) {\n\t\tlogger.debug(\"๐Ÿ”‘ [Auth] Using API key authentication\")\n\t\t// Return a valid payload for API key auth\n\t\tconst now = Math.floor(Date.now() / 1000)\n\t\tconst result = {\n\t\t\taction: \"send\" as const, // Default action\n\t\t\tconversationId: \"\", // Will be filled by endpoint\n\t\t\tissued: now,\n\t\t\texpires: now + 3600 // API keys are valid for 1 hour\n\t\t}\n\n\t\tconst endTime = performance.now()\n\t\tlogger.debug(\n\t\t\t`๐Ÿ” [JWT] API key validation completed in ${(endTime - startTime).toFixed(2)}ms`\n\t\t)\n\t\treturn result\n\t}\n\n\t// Then try JWT token authentication\n\ttry {\n\t\tconst decoded = jwt.verify(token, getJwtSecret()) as XMTPToolsPayload\n\t\tlogger.debug(\"๐Ÿ”‘ [Auth] Using JWT token authentication\")\n\n\t\t// Additional expiry check\n\t\tconst now = Math.floor(Date.now() / 1000)\n\t\tif (decoded.expires < now) {\n\t\t\tconsole.log(\"๐Ÿ”’ XMTP tools token has expired\")\n\t\t\tconst endTime = performance.now()\n\t\t\tlogger.debug(\n\t\t\t\t`๐Ÿ” [JWT] Token validation failed (expired) in ${(endTime - startTime).toFixed(2)}ms`\n\t\t\t)\n\t\t\treturn null\n\t\t}\n\n\t\tconst endTime = performance.now()\n\t\tlogger.debug(\n\t\t\t`๐Ÿ” [JWT] JWT validation completed in ${(endTime - startTime).toFixed(2)}ms`\n\t\t)\n\t\treturn decoded\n\t} catch (error) {\n\t\tlogger.error(\n\t\t\t\"๐Ÿ”’ Invalid XMTP tools token and not matching API key:\",\n\t\t\terror\n\t\t)\n\t\tconst endTime = performance.now()\n\t\tlogger.debug(\n\t\t\t`๐Ÿ” [JWT] Token validation failed in ${(endTime - startTime).toFixed(2)}ms`\n\t\t)\n\t\treturn null\n\t}\n}\n"],"mappings":";;;;;AAAA;AAAA,EACC;AAAA,EACA,gBAAAA;AAAA,EACA,cAAAC;AAAA,EACA;AAAA,EACA;AAAA,OACM;;;ACHA,IAAM,kBAAkB,CAAC,OAAO,IAAI;AACpC,IAAM,iBAAiB;AACvB,IAAM,kBAAkB;;;ACL/B,SAAS,cAAc;AACvB,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB;AAC3B,SAAS,iCAAiC;AAC1C,SAAS,4BAA4B;AACrC,SAAS,UAAAC,eAAoD;AAC7D,SAAS,uBAAuB;AAChC,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAC9B,SAAS,YAAY,YAAY,2BAA2B;AAC5D,SAAS,oBAAoB,MAAM,eAAe;AAClD,SAAS,2BAA2B;AACpC,SAAS,eAAe;;;ACbxB,SAAS,cAA2B;AAIpC,eAAsB,uBAAuB,QAAgB,SAAkB;AAC9E,UAAQ,IAAI,qDAA8C;AAE1D,MAAI;AAEH,QAAI,CAAC,SAAS;AACb,cAAQ,IAAI,+DAAqD;AACjE,aAAO;AAAA,IACR;AAEA,UAAM,cAAc,MAAM,OAAO;AAAA,MAChC,CAAC,OAAO;AAAA,MACR,QAAQ,IAAI;AAAA,IACb;AAEA,QAAI,CAAC,YAAY,CAAC,GAAG;AACpB,cAAQ,IAAI,sDAAiD;AAC7D,aAAO;AAAA,IACR;AAEA,UAAM,4BAA4B,YAAY,CAAC,EAAE,cAAc;AAAA,MAC9D,CAAC,MAA6B,EAAE;AAAA,IACjC;AAEA,UAAM,OAAO;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAQ,IAAI;AAAA,IACb;AAEA,UAAM,kBAAkB,MAAM,OAAO;AAAA,MACpC,CAAC,OAAO;AAAA,MACR,QAAQ,IAAI;AAAA,IACb;AAEA,YAAQ;AAAA,MACP,oCAA6B,0BAA0B,MAAM;AAAA,IAC9D;AACA,YAAQ;AAAA,MACP,8BAAuB,gBAAgB,CAAC,GAAG,cAAc,UAAU,CAAC;AAAA,IACrE;AAEA,WAAO;AAAA,EACR,SAAS,OAAO;AACf,YAAQ,MAAM,gDAA2C,KAAK;AAC9D,WAAO;AAAA,EACR;AACD;AAGA,eAAe,OAAO;AACrB,QAAM,EAAE,gBAAgB,IAAI,QAAQ;AACpC,QAAM,UAAU,QAAQ,KAAK,CAAC;AAE9B,MAAI,CAAC,iBAAiB;AACrB,YAAQ,MAAM,oCAA+B;AAC7C,YAAQ,KAAK,CAAC;AAAA,EACf;AAEA,MAAI,CAAC,SAAS;AACb,YAAQ,MAAM,4CAAuC;AACrD,YAAQ,MAAM,8CAA8C;AAC5D,YAAQ,KAAK,CAAC;AAAA,EACf;AAEA,QAAM,SAAS,aAAa,eAAe;AAC3C,QAAM,aAAa,MAAM,OAAO,cAAc;AAC9C,QAAM,UAAU,WAAW;AAE3B,UAAQ,IAAI,6BAAsB,OAAO,EAAE;AAC3C,UAAQ,IAAI,uBAAgB,OAAO,EAAE;AAGrC,QAAM,UAAU,MAAM,uBAAuB,QAAQ,OAAO;AAE5D,MAAI,SAAS;AACZ,YAAQ,IAAI,2CAAsC;AAAA,EACnD,OAAO;AACN,YAAQ,IAAI,uCAAkC;AAC9C,YAAQ,KAAK,CAAC;AAAA,EACf;AACD;AAGA,IAAI,YAAY,QAAQ,UAAU,QAAQ,KAAK,CAAC,CAAC,IAAI;AACpD,OAAK,EAAE,MAAM,CAAC,UAAU;AACvB,YAAQ,MAAM,0BAAmB,KAAK;AACtC,YAAQ,KAAK,CAAC;AAAA,EACf,CAAC;AACF;;;ADzEA,IAAM,aAAa,cAAc,YAAY,GAAG;AAChD,IAAM,YAAY,KAAK,QAAQ,UAAU;AAclC,IAAM,aAAa,CAAC,QAAsB;AAChD,QAAM,UAAU,oBAAoB,GAAoB;AACxD,SAAO;AAAA,IACN;AAAA,IACA;AAAA,IACA,QAAQ,mBAAmB;AAAA,MAC1B;AAAA,MACA,OAAO;AAAA,MACP,WAAW,KAAK;AAAA,IACjB,CAAC;AAAA,EACF;AACD;AAEO,IAAM,eAAe,CAAC,QAAwB;AACpD,MAAI,CAAC,OAAO,OAAO,QAAQ,UAAU;AACpC,UAAM,IAAI,MAAM,4CAA4C;AAAA,EAC7D;AACA,QAAM,eAAe,IAAI,WAAW,IAAI,IAAI,MAAM,KAAK,GAAG;AAC1D,QAAM,OAAO,WAAW,YAAY;AACpC,SAAO;AAAA,IACN,MAAM;AAAA,IACN,eAAe,OAAO;AAAA,MACrB,gBAAgB;AAAA;AAAA,MAChB,YAAY,KAAK,QAAQ,QAAQ,YAAY;AAAA,IAC9C;AAAA,IACA,aAAa,OAAO,YAAoB;AACvC,YAAM,YAAY,MAAM,KAAK,OAAO,YAAY;AAAA,QAC/C;AAAA,QACA,SAAS,KAAK;AAAA,MACf,CAAC;AACD,aAAO,QAAQ,SAAS;AAAA,IACzB;AAAA,EACD;AACD;AAMA,eAAe,kBAAkB,SAAiB,KAAa;AAC9D,SAAO,MAAM,mEAA4D;AAGzE,QAAM,sBAAsB,MAAM;AACjC,UAAM,oBAAoB,QAAQ,IAAI;AAEtC,QAAI,mBAAmB;AACtB,aAAO,KAAK,WAAW,iBAAiB,IACrC,oBACA,KAAK,QAAQ,QAAQ,IAAI,GAAG,iBAAiB;AAAA,IACjD;AAGA,UAAM,cACL,QAAQ,IAAI,gBAAgB,KAAK,QAAQ,WAAW,UAAU;AAE/D,WAAO,KAAK,KAAK,aAAa,YAAY;AAAA,EAC3C;AAGA,QAAM,YAAY,GAAG,GAAG,IAAI,OAAO;AACnC,QAAM,aAAa,oBAAoB;AAGvC,QAAM,gBAAgB;AAAA,IACrB;AAAA;AAAA,IAEA,KAAK,KAAK,QAAQ,IAAI,GAAG,SAAS,MAAM;AAAA,IACxC,KAAK,KAAK,QAAQ,IAAI,GAAG,MAAM,SAAS,MAAM;AAAA,IAC9C,KAAK,KAAK,QAAQ,IAAI,GAAG,MAAM,MAAM,SAAS,MAAM;AAAA,EACrD;AAEA,aAAW,OAAO,eAAe;AAChC,QAAI;AACH,UAAI,GAAG,WAAW,GAAG,GAAG;AACvB,cAAM,QAAQ,GAAG,YAAY,GAAG;AAChC,cAAM,gBAAgB,MAAM;AAAA,UAC3B,CAAC,SACA,KAAK,SAAS,SAAS,KACvB,KAAK,SAAS,OAAO,KACrB,KAAK,SAAS,QAAQ,GAAG,IAAI,OAAO,EAAE;AAAA,QACxC;AAEA,mBAAW,QAAQ,eAAe;AACjC,gBAAM,WAAW,KAAK,KAAK,KAAK,IAAI;AACpC,cAAI;AACH,eAAG,WAAW,QAAQ;AACtB,mBAAO,MAAM,mBAAc,QAAQ,EAAE;AAAA,UACtC,SAAS,KAAK;AACb,mBAAO,MAAM,iCAAuB,QAAQ,KAAK,GAAG;AAAA,UACrD;AAAA,QACD;AAAA,MACD;AAAA,IACD,SAAS,KAAK;AAAA,IAEd;AAAA,EACD;AACD;AAEA,eAAsB,iBACrB,YACA,MAKsB;AACtB,QAAM,EAAE,UAAU,MAAM,aAAa,GAAG,YAAY,IAAI,QAAQ,CAAC;AACjE,MAAI,UAAU;AAId,QAAM,SAAS,aAAa,UAAU;AAEtC,MAAI,CAAC,QAAQ;AACZ,UAAM,IAAI;AAAA,MACT;AAAA,IACD;AAAA,EACD;AAEA,QAAM,EAAE,wBAAwB,SAAS,IAAI,QAAQ;AAGrD,QAAM,aAAa,MAAM,OAAO,cAAc;AAC9C,QAAM,UAAU,WAAW;AAE3B,SAAO,UAAU,YAAY;AAC5B,QAAI;AACH,aAAO;AAAA,QACN,qBAAc,UAAU,CAAC,IAAI,UAAU;AAAA,MACxC;AAGA,UAAI,CAAC,SAAS;AACb,cAAM,IAAI;AAAA,UACT;AAAA,QAGD;AAAA,MACD;AAEA,UAAI,CAAC,wBAAwB;AAC5B,cAAM,IAAI;AAAA,UACT;AAAA,QACD;AAAA,MACD;AAEA,YAAM,kBAAkB,wBAAwB,sBAAsB;AACtE,YAAM,SAAS,MAAM;AAAA,QACpB,GAAG,YAAY,KAAK,IAAI,OAAO;AAAA,QAC/B;AAAA,MACD;AACA,aAAO,MAAM,kCAA2B,MAAM,EAAE;AAGhD,YAAM,SAAS,MAAMC,QAAO,OAAO,QAAQ;AAAA,QAC1C;AAAA,QACA,KAAK;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,UACP,IAAI,WAAW;AAAA,UACf,IAAI,cAAc;AAAA,UAClB,IAAI,qBAAqB;AAAA,UACzB,IAAI,0BAA0B;AAAA,QAC/B;AAAA,MACD,CAAC;AAGD,aAAO,MAAM,2DAAoD;AACjE,YAAM,OAAO,cAAc,KAAK;AAEhC,YAAM;AAAA,QACL;AAAA,QACA,GAAG,YAAY,KAAK,IAAI,OAAO;AAAA,MAChC;AAEA,cAAQ,IAAI,WAAW,OAAO,EAAE;AAChC,cAAQ,IAAI,QAAQ,YAAY,KAAK,EAAE;AACvC,cAAQ,IAAI,qBAAqB;AAEjC,aAAO;AAAA,IACR,SAAS,OAAO;AACf;AAEA,UACC,iBAAiB,SACjB,MAAM,QAAQ,SAAS,mBAAmB,GACzC;AACD,gBAAQ;AAAA,UACP,iDAA0C,OAAO,IAAI,UAAU;AAAA,QAChE;AAEA,YAAI,UAAU,YAAY;AAEzB,gBAAMC,cAAa,MAAM,OAAO,cAAc;AAC9C,gBAAMC,WAAUD,YAAW;AAG3B,gBAAM,eAAe,MAAM,QAAQ,MAAM,qBAAqB;AAC9D,gBAAM,UAAU,eAAe,aAAa,CAAC,IAAI;AAGjD,gBAAM,oBAAoB,MAAM;AAAA,YAC/B;AAAA,YACA;AAAA,UACD;AAEA,cAAI,mBAAmB;AACtB,oBAAQ,IAAI,yDAAkD;AAAA,UAC/D,OAAO;AACN,oBAAQ;AAAA,cACP;AAAA,YACD;AAEA,kBAAM,kBAAkBC,UAAS,QAAQ,IAAI,YAAY,KAAK;AAAA,UAC/D;AAGA,gBAAM,QAAQ,KAAK,IAAI,GAAG,OAAO,IAAI;AACrC,kBAAQ,IAAI,kBAAa,KAAK,oBAAoB;AAClD,gBAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,KAAK,CAAC;AAAA,QAC1D,OAAO;AACN,kBAAQ;AAAA,YACP;AAAA,UACD;AACA,kBAAQ,MAAM,+BAAwB;AACtC,kBAAQ,MAAM,kDAAkD;AAChE,kBAAQ,MAAM,qDAAqD;AACnE,kBAAQ,MAAM,gCAAgC;AAC9C,kBAAQ,MAAM,oDAAoD;AAClE,gBAAM;AAAA,QACP;AAAA,MACD,WACC,iBAAiB,SACjB,MAAM,QAAQ,SAAS,4CAA4C,GAClE;AACD,gBAAQ;AAAA,UACP,0DAAmD,OAAO,IAAI,UAAU;AAAA,QACzE;AAEA,YAAI,UAAU,YAAY;AACzB,kBAAQ,IAAI,oDAA6C;AAGzD,cAAI;AACH,oBAAQ,IAAI,6DAAsD;AAClE,kBAAM,oBAAoB,yBACvB,wBAAwB,sBAAsB,IAC9C,wBAAwB,yBAAyB,CAAC;AACrD,kBAAM,aAAa,MAAMF,QAAO,OAAO,QAAQ;AAAA,cAC9C,iBAAiB;AAAA,cACjB,KAAK;AAAA,cACL,QAAQ,MAAM;AAAA,gBACb,GAAG,YAAY,KAAK,IAAI,OAAO;AAAA,gBAC/B;AAAA,cACD;AAAA,cACA,QAAQ;AAAA,gBACP,IAAI,WAAW;AAAA,gBACf,IAAI,cAAc;AAAA,gBAClB,IAAI,qBAAqB;AAAA,gBACzB,IAAI,0BAA0B;AAAA,cAC/B;AAAA,YACD,CAAC;AAED,oBAAQ,IAAI,iDAA0C;AACtD,kBAAM,WAAW,cAAc,KAAK;AAEpC,oBAAQ;AAAA,cACP;AAAA,YACD;AAGA,kBAAM,QAAQ,KAAK,IAAI,GAAG,OAAO,IAAI;AACrC,oBAAQ,IAAI,kBAAa,KAAK,oBAAoB;AAClD,kBAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,KAAK,CAAC;AAAA,UAC1D,SAAS,cAAc;AACtB,oBAAQ,IAAI,mCAA8B,YAAY;AAAA,UAEvD;AAAA,QACD,OAAO;AACN,kBAAQ;AAAA,YACP;AAAA,UACD;AACA,kBAAQ;AAAA,YACP;AAAA,UACD;AACA,gBAAM;AAAA,QACP;AAAA,MACD,OAAO;AAEN,cAAM;AAAA,MACP;AAAA,IACD;AAAA,EACD;AAEA,QAAM,IAAI,MAAM,sBAAsB;AACvC;AASO,IAAM,2BAA2B,MAAM;AAC7C,QAAM,aAAa,gBAAgB,IAAI,WAAW,EAAE,CAAC;AACrD,SAAO,oBAAoB,YAAY,KAAK;AAC7C;AAOA,IAAM,0BAA0B,CAAC,QAA4B;AAC5D,SAAO,WAAW,KAAK,KAAK;AAC7B;AAKO,IAAM,YAAY,OAAO,cAAc,QAAQ,gBAAyB;AAE9E,QAAM,oBAAoB,QAAQ,IAAI;AAEtC,MAAI;AAEJ,MAAI,mBAAmB;AAEtB,iBAAa,KAAK,WAAW,iBAAiB,IAC3C,oBACA,KAAK,QAAQ,QAAQ,IAAI,GAAG,iBAAiB;AAAA,EACjD,WAAW,aAAa;AACvB,iBAAa,KAAK,WAAW,WAAW,IACrC,cACA,KAAK,QAAQ,QAAQ,IAAI,GAAG,WAAW;AAAA,EAC3C,OAAO;AAEN,UAAM,cACL,QAAQ,IAAI,gBAAgB,KAAK,QAAQ,WAAW,UAAU;AAG/D,iBAAa,KAAK,KAAK,aAAa,YAAY;AAAA,EACjD;AAEA,QAAM,SAAS,GAAG,UAAU,IAAI,WAAW;AAE3C,MAAI,OAAO,eAAe,eAAe,kBAAkB,YAAY;AACtE,QAAI;AACH,cAAQ,IAAI,8CAAuC,MAAM,EAAE;AAE3D,YAAM,WAAY,WAAmB;AACrC,YAAM,aAAa,kBAAkB,WAAW;AAEhD,UAAI;AACH,cAAM,iBAAiB,MAAM,SAAS,KAAK,UAAU;AACrD,YAAI,gBAAgB;AACnB,kBAAQ,IAAI,4DAAqD;AAEjE,cAAI,CAAC,GAAG,WAAW,UAAU,GAAG;AAC/B,eAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;AAAA,UAC7C;AAEA,gBAAM,SAAS,MAAM,SAAS,IAAI,UAAU;AAC5C,cAAI,QAAQ;AACX,kBAAM,WAAW,MAAM,OAAO,YAAY;AAC1C,eAAG,cAAc,QAAQ,IAAI,WAAW,QAAQ,CAAC;AACjD,oBAAQ,IAAI,4CAAuC;AAAA,UACpD;AAAA,QACD,OAAO;AACN,kBAAQ,IAAI,oDAA6C;AAAA,QAC1D;AAAA,MACD,SAAS,OAAO;AACf,gBAAQ,IAAI,6DAAmD,KAAK;AAAA,MACrE;AAAA,IACD,SAAS,OAAO;AACf,cAAQ,IAAI,0CAAgC,KAAK;AAAA,IAClD;AAAA,EACD;AAEA,MAAI,CAAC,GAAG,WAAW,UAAU,GAAG;AAC/B,OAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;AAAA,EAC7C;AAEA,SAAO;AACR;AAEA,IAAM,8BAA8B,OACnC,QACA,gBACI;AACJ,MACC,OAAO,eAAe,eACtB,kBAAkB,cAClB,GAAG,WAAW,MAAM,GACnB;AACD,QAAI;AACH,cAAQ,IAAI,gDAAyC,MAAM,EAAE;AAE7D,YAAM,WAAY,WAAmB;AACrC,YAAM,aAAa,kBAAkB,WAAW;AAEhD,YAAM,WAAW,GAAG,aAAa,MAAM;AACvC,YAAM,SAAS,IAAI,YAAY,QAAQ;AACvC,cAAQ,IAAI,4CAAuC,UAAU,EAAE;AAAA,IAChE,SAAS,OAAO;AACf,cAAQ,IAAI,yDAA+C,KAAK;AAAA,IACjE;AAAA,EACD;AACD;AAKO,IAAM,kBAAkB,OAC9B,YACmB;AACnB,QAAM,mBAAmB,MAAM,QAAQ,OAAO,IAC3C,QAAQ,OAAqC,CAAC,KAAK,eAAe;AAClE,UAAM,UAAU,WAAW,mBAAmB,cAAc;AAC5D,QAAI,OAAO,IAAI,IAAI,OAAO,KAAK,CAAC;AAChC,QAAI,OAAO,EAAE,KAAK,UAAU;AAC5B,WAAO;AAAA,EACR,GAAG,CAAC,CAAC,IACJ;AAAA,IACA,CAAC,QAAQ,mBAAmB,cAAc,EAAE,GAAG,CAAC,OAAO;AAAA,EACxD;AAEF,aAAW,CAAC,SAAS,WAAW,KAAK,OAAO,QAAQ,gBAAgB,GAAG;AACtE,UAAM,cAAc,YAAY,CAAC;AACjC,UAAM,UAAU,aAAa;AAC7B,UAAM,eAAe,YACnB,IAAI,CAAC,MAAM,EAAE,SAAS,OAAO,KAAK,EAClC,KAAK,IAAI;AACX,YAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAOA;AAEZ,UAAM,OAAO,CAAC,uBAAuB,OAAO,EAAE;AAE9C,UAAM,gBAAgB,MAAM,aAAa,cAAc,KAAK;AAE5D,YAAQ,IAAI;AAAA;AAAA,sBAEG,OAAO;AAAA,4BACD,eAAe,MAAM;AAAA,sBAC3B,OAAO;AAAA,uBACN,YAAY;AAAA,MACxB,KAAK,IAAI,CAAC,QAAQ,eAAU,GAAG,EAAE,EAAE,KAAK,IAAI,CAAC,EAAE;AAAA,EACpD;AACD;AAKO,SAAS,oBAAoB,MAAwC;AAC3E,QAAM,UAAU,KAAK,OAAO,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;AAElD,MAAI,QAAQ,QAAQ;AACnB,QAAI;AACH,YAAM,UAAU,KAAK,QAAQ,QAAQ,IAAI,GAAG,MAAM;AAClD,UAAI,GAAG,WAAW,OAAO,GAAG;AAC3B,cAAM,UAAU,GACd,aAAa,SAAS,OAAO,EAC7B,MAAM,IAAI,EACV,OAAO,CAAC,SAAS,KAAK,KAAK,KAAK,CAAC,KAAK,WAAW,GAAG,CAAC,EACrD,OAA+B,CAAC,KAAK,SAAS;AAC9C,gBAAM,CAAC,KAAK,GAAG,GAAG,IAAI,KAAK,MAAM,GAAG;AACpC,cAAI,OAAO,IAAI,OAAQ,KAAI,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,GAAG,EAAE,KAAK;AAC5D,iBAAO;AAAA,QACR,GAAG,CAAC,CAAC;AAEN,gBAAQ,QAAQ,CAAC,MAAM;AACtB,cAAI,QAAQ,CAAC,EAAG,SAAQ,IAAI,CAAC,IAAI,QAAQ,CAAC;AAAA,QAC3C,CAAC;AAAA,MACF;AAAA,IACD,SAAS,GAAG;AACX,cAAQ,MAAM,CAAC;AAAA,IAEhB;AAEA,UAAM,eAAe,KAAK,OAAO,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;AACvD,QAAI,aAAa,QAAQ;AACxB,cAAQ,MAAM,qBAAqB,aAAa,KAAK,IAAI,CAAC;AAC1D,cAAQ,KAAK,CAAC;AAAA,IACf;AAAA,EACD;AAEA,SAAO,KAAK,OAA+B,CAAC,KAAK,QAAQ;AACxD,QAAI,GAAG,IAAI,QAAQ,IAAI,GAAG;AAC1B,WAAO;AAAA,EACR,GAAG,CAAC,CAAC;AACN;AAsHO,IAAM,wBAAN,MAA4B;AAAA,EAQlC,YAAY,YAAoB,SAA+B,CAAC,GAAG;AAPnE,wBAAQ,UAA4B;AACpC,wBAAQ;AACR,wBAAQ;AACR,wBAAQ;AACR,wBAAQ,oBAA0C;AAClD,wBAAQ,kBAAiB;AAGxB,SAAK,aAAa;AAClB,SAAK,SAAS;AAAA,MACb,YAAY,OAAO,cAAc;AAAA,MACjC,cAAc,OAAO,gBAAgB;AAAA,MACrC,uBAAuB,OAAO,yBAAyB;AAAA,MACvD,qBAAqB,OAAO,uBAAuB;AAAA,MACnD,oBAAoB,OAAO,sBAAsB;AAAA,IAClD;AAEA,SAAK,SAAS;AAAA,MACb,aAAa;AAAA,MACb,iBAAiB,oBAAI,KAAK;AAAA,MAC1B,qBAAqB;AAAA,MACrB,iBAAiB;AAAA,MACjB,iBAAiB;AAAA,IAClB;AAAA,EACD;AAAA,EAEA,MAAM,QAAQ,UAAU,OAA4B;AACnD,QAAI,KAAK,UAAU,KAAK,OAAO,aAAa;AAC3C,aAAO,KAAK;AAAA,IACb;AAEA,QAAI,UAAU;AACd,WAAO,UAAU,KAAK,OAAO,YAAY;AACxC,UAAI;AACH,gBAAQ;AAAA,UACP,qCAA8B,UAAU,CAAC,IAAI,KAAK,OAAO,UAAU;AAAA,QACpE;AAEA,aAAK,SAAS,MAAM,iBAAiB,KAAK,YAAY,EAAE,QAAQ,CAAC;AACjE,aAAK,OAAO,cAAc;AAC1B,aAAK,OAAO,sBAAsB;AAGlC,aAAK,sBAAsB;AAE3B,gBAAQ,IAAI,2CAAsC;AAClD,eAAO,KAAK;AAAA,MACb,SAAS,OAAO;AACf;AACA,aAAK,OAAO;AAEZ,gBAAQ,MAAM,kCAA6B,OAAO,YAAY,KAAK;AAEnE,YAAI,UAAU,KAAK,OAAO,YAAY;AACrC,gBAAM,QAAQ,KAAK,OAAO,eAAe,KAAK,IAAI,GAAG,UAAU,CAAC;AAChE,kBAAQ,IAAI,sBAAiB,KAAK,OAAO;AACzC,gBAAM,KAAK,MAAM,KAAK;AAAA,QACvB;AAAA,MACD;AAAA,IACD;AAEA,UAAM,IAAI;AAAA,MACT,mCAAmC,KAAK,OAAO,UAAU;AAAA,IAC1D;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeQ,wBAA8B;AACrC,QAAI,KAAK,kBAAkB;AAC1B,oBAAc,KAAK,gBAAgB;AAAA,IACpC;AAEA,SAAK,mBAAmB,YAAY,MAAM;AACzC,WAAK,mBAAmB;AAAA,IACzB,GAAG,KAAK,OAAO,qBAAqB;AAAA,EACrC;AAAA,EAEA,MAAc,qBAAoC;AACjD,QAAI,CAAC,KAAK,OAAQ;AAElB,UAAM,YAAY,KAAK,IAAI;AAE3B,QAAI;AAEH,YAAM,KAAK,OAAO,cAAc,KAAK;AAErC,YAAM,eAAe,KAAK,IAAI,IAAI;AAClC,WAAK,OAAO,mBACV,KAAK,OAAO,kBAAkB,gBAAgB;AAChD,WAAK,OAAO,kBAAkB,oBAAI,KAAK;AACvC,WAAK,OAAO,sBAAsB;AAClC,WAAK,OAAO,cAAc;AAE1B,cAAQ,IAAI,uCAAgC,YAAY,KAAK;AAAA,IAC9D,SAAS,OAAO;AACf,WAAK,OAAO;AACZ,WAAK,OAAO,cAAc;AAE1B,cAAQ,MAAM,uCAAgC,KAAK;AAGnD,UAAI,KAAK,OAAO,sBAAsB,CAAC,KAAK,gBAAgB;AAC3D,aAAK,wBAAwB;AAAA,MAC9B;AAAA,IACD;AAAA,EACD;AAAA,EAEA,MAAc,0BAAyC;AACtD,QAAI,KAAK,eAAgB;AAEzB,SAAK,iBAAiB;AACtB,SAAK,OAAO;AAEZ,YAAQ,IAAI,4DAAqD;AAEjE,QAAI;AACH,WAAK,SAAS;AACd,YAAM,KAAK,QAAQ;AACnB,cAAQ,IAAI,qCAAgC;AAAA,IAC7C,SAAS,OAAO;AACf,cAAQ,MAAM,oCAA+B,KAAK;AAAA,IACnD,UAAE;AACD,WAAK,iBAAiB;AAAA,IACvB;AAAA,EACD;AAAA,EAEQ,MAAM,IAA2B;AACxC,WAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAAA,EACxD;AAAA,EAEA,YAAkC;AACjC,WAAO,EAAE,GAAG,KAAK,OAAO;AAAA,EACzB;AAAA,EAEA,YAA+B;AAC9B,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,MAAM,aAA4B;AACjC,QAAI,KAAK,kBAAkB;AAC1B,oBAAc,KAAK,gBAAgB;AACnC,WAAK,mBAAmB;AAAA,IACzB;AAEA,SAAK,SAAS;AACd,SAAK,OAAO,cAAc;AAC1B,YAAQ,IAAI,oCAA6B;AAAA,EAC1C;AACD;;;AE7yBA;AAAA,EACC,SAAS;AAAA,EAET,gBAAAG;AAAA,EACA,cAAAC;AAAA,OACM;AAaP,SAAS,UAAAC,eAAc;AACvB,SAAS,kBAAkB;AAU3B,eAAe,aACd,cACA,MACA,mBACA,iBACC;AACD,QAAM,eAAe,iBAAiB,aAAa,YAAY;AAE/D,MAAI,cAAc;AAEjB,QAAI;AACH,YAAM,QAAe;AAAA,QACpB,WAAW;AAAA,QACX,aAAa;AAAA,QACb,SAAS;AAAA,MACV;AACA,YAAM,aAAa,KAAK,OAAO,gBAAgB;AAC/C,MAAAC,QAAO;AAAA,QACN,qEAAgE,iBAAiB;AAAA,MAClF;AAAA,IACD,SAAS,OAAO;AACf,MAAAA,QAAO;AAAA,QACN,kEAA6D,iBAAiB;AAAA,QAC9E;AAAA,MACD;AAEA,MAAAA,QAAO,MAAM,0DAAmD;AAChE,YAAM,aAAa,KAAK,IAAI;AAAA,IAC7B;AAAA,EACD,OAAO;AAEN,UAAM,aAAa,KAAK,IAAI;AAAA,EAC7B;AACD;AASO,SAAS,aAAoC;AACnD,SAAO;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA,IACb,OAAO,OAAO,KAAK,YAA2B;AAC7C,YAAM;AAAA,QACL;AAAA,QACA;AAAA,QACA,WAAW;AAAA,MACZ,IAAI,QAAQ;AAEZ,YAAM,EAAE,MAAM,IAAI;AAClB,YAAM,gBAAgB;AAItB,UAAI,CAAC,iBAAiB;AACrB,cAAM,IAAI,MAAM,6BAA6B;AAAA,MAC9C;AAEA,UAAI,CAAC,wBAAwB;AAC5B,cAAM,IAAI,MAAM,oCAAoC;AAAA,MACrD;AAEA,YAAM,OAAOC,YAAW,eAAgC;AACxD,YAAM,SAASC,cAAa,IAAI;AAEhC,YAAM,aAAa,MAAM;AAAA,QACxB;AAAA,MACD;AAGA,qBAAe,kBAAkB;AAChC,YAAI;AACH,UAAAF,QAAO,MAAM,gDAAyC;AACtD,gBAAM,SAAS,MAAM,WAAW,cAAc,kBAAkB;AAChE,UAAAA,QAAO,MAAM,2CAAoC;AACjD,2BAAiB,OAAO,QAAQ;AAC/B,gBAAI;AACH,kBAAI,IAAI,kBAAkB,WAAW,QAAS;AAE9C,oBAAM,UACL,OAAO,IAAI,YAAY,WACpB,IAAI,WACH,MAAM;AACP,oBAAI;AACH,yBAAO,KAAK,UAAU,IAAI,OAAO;AAAA,gBAClC,QAAQ;AACP,yBAAO,OAAO,IAAI,OAAO;AAAA,gBAC1B;AAAA,cACD,GAAG;AAEN,oBAAM,eACL,MAAM,WAAW,cAAc;AAAA,gBAC9B,IAAI;AAAA,cACL;AAED,kBAAI,CAAC,cAAc;AAClB,gBAAAA,QAAO;AAAA,kBACN,6CAAmC,IAAI,cAAc;AAAA,gBACtD;AACA;AAAA,cACD;AAEA,oBAAM,WAA2B;AAAA,gBAChC;AAAA,kBACC,IAAI,WAAW;AAAA,kBACf,MAAM;AAAA,kBACN,OAAO,CAAC,EAAE,MAAM,QAAQ,MAAM,QAAQ,CAAC;AAAA,gBACxC;AAAA,cACD;AAEA,oBAAM,cAA4B;AAAA,gBACjC;AAAA,gBACA,SAAS;AAAA,gBACT;AAAA,cACD;AAEA,oBAAM,UAAU,MAAM,MAAM,qBAAqB,WAAW;AAG5D,kBAAI,cAAc,WAAW;AAC5B,sBAAMG,mBAAmC;AAAA,kBACxC;AAAA,kBACA,QAAQ;AAAA,kBACR;AAAA,kBACA,SAAS;AAAA,gBACV;AACA,sBAAM,cAAc,UAAU,cAAcA,gBAAe;AAG3D,oBAAIA,iBAAgB,aAAa,UAAU;AAC1C;AAAA,gBACD;AAAA,cACD;AAEA,oBAAM,EAAE,KAAK,IAAI,MAAM,MAAM,SAAS,UAAU,EAAE,QAAQ,CAAC;AAG3D,oBAAM,kBAAmC;AAAA,gBACxC;AAAA,gBACA,QAAQ;AAAA,gBACR;AAAA,gBACA,SAAS;AAAA,gBACT,UAAU;AAAA,cACX;AAGA,kBAAI,cAAc,WAAW;AAC5B,sBAAM,cAAc,UAAU,aAAa,eAAe;AAAA,cAC3D;AAGA,kBAAI,iBAAiB,aAAa,UAAU;AAC3C,gBAAAH,QAAO;AAAA,kBACN;AAAA,gBACD;AACA;AAAA,cACD;AAGA,oBAAM,aAAa,cAAc,MAAM,IAAI,IAAI,eAAe;AAAA,YAC/D,SAAS,KAAK;AACb,cAAAA,QAAO,MAAM,yCAAoC,GAAG;AAAA,YACrD;AAAA,UACD;AAAA,QACD,SAAS,KAAK;AACb,UAAAA,QAAO,MAAM,0CAAqC,GAAG;AAAA,QACtD;AAAA,MACD;AAEA,YAAM,iBAAiB,QAAQ,IAAI;AACnC,YAAM,sBACL,mBAAmB,SAChB,OACA,CAAC,sBAAsB,KAAK,OAAO,cAAc,CAAC;AAEtD,UAAI,oBAAqB,MAAK,gBAAgB;AAE9C,YAAM,UAAU,KAAK,QAAQ,QAAQ,YAAY;AACjD,YAAM,cAAc,MAAM;AAAA,QACzB,SAAS,YAAY,KAAK,IAAI,OAAO;AAAA,MACtC;AACA,MAAAA,QAAO,MAAM,iDAA0C,WAAW,EAAE;AAEpE,YAAM,OAAO,MAAM,UAAU,OAAO,QAAQ;AAAA,QAC3C,KAAK;AAAA,QACL,QAAQ;AAAA,MACT,CAAC;AAED,WAAK,GAAG,YAAY,OAAO,EAAE,cAAc,QAAQ,MAAM;AACxD,YAAI;AACH,gBAAM,OAAO,QAAQ,QAAQ;AAC7B,gBAAM,WAA2B;AAAA,YAChC;AAAA,cACC,IAAI,WAAW;AAAA,cACf,MAAM;AAAA,cACN,OAAO,CAAC,EAAE,MAAM,QAAQ,KAAK,CAAC;AAAA,YAC/B;AAAA,UACD;AAEA,gBAAM,cAA4B;AAAA,YACjC;AAAA,YACA;AAAA,YACA;AAAA,UACD;AAEA,gBAAM,UAAU,MAAM,MAAM,qBAAqB,WAAW;AAG5D,cAAI,QAAQ,WAAW;AACtB,kBAAMG,mBAAmC;AAAA,cACxC;AAAA,cACA,QAAQ;AAAA,cACR;AAAA,cACA;AAAA,YACD;AACA,kBAAM,QAAQ,UAAU,cAAcA,gBAAe;AAAA,UACtD;AAEA,gBAAM,EAAE,MAAM,MAAM,IAAI,MAAM,MAAM,SAAS,UAAU,EAAE,QAAQ,CAAC;AAGlE,cAAI;AACJ,cAAI,QAAQ,WAAW;AACtB,8BAAkB;AAAA,cACjB;AAAA,cACA,QAAQ;AAAA,cACR;AAAA,cACA;AAAA,cACA,UAAU;AAAA,YACX;AACA,kBAAM,QAAQ,UAAU,aAAa,eAAe;AAAA,UACrD,OAAO;AAEN,8BAAkB;AAAA,cACjB;AAAA,cACA,QAAQ;AAAA,cACR;AAAA,cACA;AAAA,cACA,UAAU;AAAA,YACX;AAAA,UACD;AAGA,cAAI,iBAAiB,aAAa,UAAU;AAC3C,YAAAH,QAAO;AAAA,cACN;AAAA,YACD;AACA;AAAA,UACD;AAEA,gBAAM;AAAA,YACL;AAAA,YACA;AAAA,YACA,QAAQ;AAAA,YACR;AAAA,UACD;AAAA,QACD,SAAS,KAAK;AACb,UAAAA,QAAO,MAAM,mCAA8B,GAAG;AAAA,QAC/C;AAAA,MACD,CAAC;AAED,WAAK,GAAG,SAAS,OAAO,EAAE,cAAc,QAAQ,MAAM;AACrD,YAAI;AAEH,gBAAM,OAAO,QAAQ,QAAQ;AAC7B,gBAAM,WAA2B;AAAA,YAChC;AAAA,cACC,IAAI,WAAW;AAAA,cACf,MAAM;AAAA,cACN,OAAO,CAAC,EAAE,MAAM,QAAQ,KAAK,CAAC;AAAA,YAC/B;AAAA,UACD;AAEA,gBAAM,cAA4B;AAAA,YACjC;AAAA,YACA;AAAA,YACA;AAAA,UACD;AAEA,gBAAM,UAAU,MAAM,MAAM,qBAAqB,WAAW;AAG5D,cAAI;AACJ,cAAI,QAAQ,WAAW;AACtB,8BAAkB;AAAA,cACjB;AAAA,cACA,QAAQ;AAAA,cACR;AAAA,cACA;AAAA,YACD;AACA,kBAAM,QAAQ,UAAU,cAAc,eAAe;AAGrD,gBAAI,gBAAgB,SAAS;AAC5B,cAAAA,QAAO;AAAA,gBACN;AAAA,cACD;AACA;AAAA,YACD;AAAA,UACD;AAEA,gBAAM,EAAE,MAAM,MAAM,IAAI,MAAM,MAAM,SAAS,UAAU,EAAE,QAAQ,CAAC;AAGlE,cAAI,QAAQ,WAAW;AACtB,gBAAI,CAAC,iBAAiB;AACrB,gCAAkB;AAAA,gBACjB;AAAA,gBACA,QAAQ;AAAA,gBACR;AAAA,gBACA;AAAA,gBACA,UAAU;AAAA,cACX;AAAA,YACD,OAAO;AACN,8BAAgB,WAAW;AAAA,YAC5B;AACA,kBAAM,QAAQ,UAAU,aAAa,eAAe;AAGpD,gBAAI,gBAAgB,SAAS;AAC5B,cAAAA,QAAO;AAAA,gBACN;AAAA,cACD;AACA;AAAA,YACD;AAAA,UACD,OAAO;AAEN,8BAAkB;AAAA,cACjB;AAAA,cACA,QAAQ;AAAA,cACR;AAAA,cACA;AAAA,cACA,UAAU;AAAA,YACX;AAAA,UACD;AAEA,gBAAM;AAAA,YACL;AAAA,YACA;AAAA,YACA,QAAQ;AAAA,YACR;AAAA,UACD;AAAA,QACD,SAAS,KAAK;AACb,UAAAA,QAAO,MAAM,gCAA2B,GAAG;AAAA,QAC5C;AAAA,MACD,CAAC;AAED,WAAK,GAAG,QAAQ,OAAO,EAAE,cAAc,QAAQ,MAAM;AACpD,YAAI;AACH,gBAAM,OAAO,QAAQ;AACrB,gBAAM,WAA2B;AAAA,YAChC,EAAE,IAAI,WAAW,GAAG,MAAM,QAAQ,OAAO,CAAC,EAAE,MAAM,QAAQ,KAAK,CAAC,EAAE;AAAA,UACnE;AAEA,gBAAM,cAA4B;AAAA,YACjC;AAAA,YACA;AAAA,YACA;AAAA,UACD;AAEA,gBAAM,UAAU,MAAM,MAAM,qBAAqB,WAAW;AAG5D,cAAI;AACJ,cAAI,QAAQ,WAAW;AACtB,8BAAkB;AAAA,cACjB;AAAA,cACA,QAAQ;AAAA,cACR;AAAA,cACA;AAAA,YACD;AACA,kBAAM,QAAQ,UAAU,cAAc,eAAe;AAGrD,gBAAI,gBAAgB,SAAS;AAC5B,cAAAA,QAAO;AAAA,gBACN;AAAA,cACD;AACA;AAAA,YACD;AAAA,UACD;AAEA,gBAAM,EAAE,MAAM,MAAM,IAAI,MAAM,MAAM,SAAS,UAAU,EAAE,QAAQ,CAAC;AAGlE,cAAI,QAAQ,WAAW;AACtB,gBAAI,CAAC,iBAAiB;AACrB,gCAAkB;AAAA,gBACjB;AAAA,gBACA,QAAQ;AAAA,gBACR;AAAA,gBACA;AAAA,gBACA,UAAU;AAAA,cACX;AAAA,YACD,OAAO;AACN,8BAAgB,WAAW;AAAA,YAC5B;AACA,kBAAM,QAAQ,UAAU,aAAa,eAAe;AAGpD,gBAAI,gBAAgB,SAAS;AAC5B,cAAAA,QAAO;AAAA,gBACN;AAAA,cACD;AACA;AAAA,YACD;AAAA,UACD,OAAO;AAEN,8BAAkB;AAAA,cACjB;AAAA,cACA,QAAQ;AAAA,cACR;AAAA,cACA;AAAA,cACA,UAAU;AAAA,YACX;AAAA,UACD;AAEA,gBAAM;AAAA,YACL;AAAA,YACA;AAAA,YACA,QAAQ;AAAA,YACR;AAAA,UACD;AAAA,QACD,SAAS,KAAK;AACb,UAAAA,QAAO,MAAM,+BAA0B,GAAG;AAAA,QAC3C;AAAA,MACD,CAAC;AAID,WAAK,KACH,MAAM,EACN,KAAK,MAAMA,QAAO,MAAM,oCAA+B,CAAC,EACxD;AAAA,QAAM,CAAC,QACP,QAAQ,MAAM,+CAA0C,GAAG;AAAA,MAC5D;AAAA,IACF;AAAA,EACD;AACD;;;ACtdA,OAAO,SAAS;AAChB,SAAS,UAAAI,eAAc;AAqEvB,SAAS,eAAuB;AAC/B,QAAM,SAAS,QAAQ,IAAI;AAC3B,QAAM,UAAU,QAAQ,IAAI,YAAY;AAGxC,MAAI,YAAY,gBAAgB,CAAC,QAAQ;AACxC,UAAM,IAAI;AAAA,MACT;AAAA,IAED;AAAA,EACD;AAGA,MAAI,CAAC,QAAQ;AACZ,IAAAC,QAAO;AAAA,MACN;AAAA,IAED;AACA,WAAO;AAAA,EACR;AAEA,SAAO;AACR;AAkCA,IAAM,aAAa,IAAI;AAqBhB,SAAS,uBACf,SACS;AACT,QAAM,YAAY,YAAY,IAAI;AAClC,EAAAC,QAAO,MAAM,8CAAuC;AAEpD,QAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,QAAM,cAAgC;AAAA,IACrC,GAAG;AAAA,IACH,QAAQ;AAAA,IACR,SAAS,MAAM;AAAA,EAChB;AAEA,QAAM,QAAQ,IAAI,KAAK,aAAa,aAAa,GAAG;AAAA,IACnD,WAAW;AAAA,EACZ,CAAC;AAED,QAAM,UAAU,YAAY,IAAI;AAChC,EAAAA,QAAO;AAAA,IACN,kDAA2C,UAAU,WAAW,QAAQ,CAAC,CAAC;AAAA,EAC3E;AAEA,SAAO;AACR;;;AL9HA;AAAA,EACC,UAAAC;AAAA,EACA,kBAAAC;AAAA,OAQM;AAKP;AAAA,EACC;AAAA,OAEM;AAEP,SAAS,uBAA4C;AAErD;AAAA,EACC;AAAA,OAEM;AAEP;AAAA,EACC;AAAA,EACA,cAAAC;AAAA,OAEM;AAEP;AAAA,EACC;AAAA,EACA;AAAA,OAEM;AAEP;AAAA,EACC;AAAA,OAEM;","names":["createSigner","createUser","Client","Client","identifier","address","createSigner","createUser","logger","logger","createUser","createSigner","behaviorContext","logger","logger","logger","Client","IdentifierKind","ReplyCodec"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/client.ts","../scripts/revoke-installations.ts","../src/plugin.ts","../src/lib/jwt.ts"],"sourcesContent":["export {\n\tAgent,\n\tcreateSigner,\n\tcreateUser,\n\tfilter,\n\tgetTestUrl\n} from \"@xmtp/agent-sdk\"\n\nexport type * from \"./types\"\n\nexport {\n\tDEFAULT_AMOUNT,\n\tDEFAULT_OPTIONS,\n\tMAX_USDC_AMOUNT\n} from \"./constants\"\n// NodeNext/Node16 requires explicit extensions for relative imports\n// NodeNext/Node16 requires explicit extensions for relative imports\n\n// ===================================================================\n// XMTP Client and Connection Management\n// ===================================================================\nexport {\n createXMTPClient,\n createSigner as createXMTPSigner,\n logAgentDetails,\n validateEnvironment,\n XMTPConnectionManager\n} from \"./client\"\nexport type { XMTPConnectionConfig } from \"./client\"\n\n// ===================================================================\n// XMTP Plugin for Agent Integration\n// ===================================================================\nexport { XMTPPlugin } from \"./plugin\"\nexport type { Plugin } from \"./plugin\"\n\n// ===================================================================\n// JWT Utilities for XMTP Tools\n// ===================================================================\nexport { generateXMTPToolsToken } from \"./lib/jwt\"\nexport type { XMTPToolsPayload } from \"./lib/jwt\"\n\n// ===================================================================\n// XMTP Core SDK Exports\n// ===================================================================\nexport {\n\tClient,\n\tIdentifierKind,\n\t// type Conversation,\n\ttype DecodedMessage,\n\ttype Dm,\n\t// type Group,\n\ttype LogLevel,\n\ttype Signer,\n\ttype XmtpEnv\n} from \"@xmtp/node-sdk\"\n\n// ===================================================================\n// XMTP Content Types\n// ===================================================================\nexport {\n\tContentTypeTransactionReference,\n\ttype TransactionReference\n} from \"@xmtp/content-type-transaction-reference\"\n\nexport { ContentTypeText, type TextParameters } from \"@xmtp/content-type-text\"\n\nexport {\n\tContentTypeReaction,\n\ttype Reaction\n} from \"@xmtp/content-type-reaction\"\n\nexport {\n\tContentTypeReply,\n\tReplyCodec,\n\ttype Reply\n} from \"@xmtp/content-type-reply\"\n\nexport {\n\tContentTypeGroupUpdated,\n\tGroupUpdatedCodec,\n\ttype GroupUpdated\n} from \"@xmtp/content-type-group-updated\"\n\nexport {\n\tContentTypeWalletSendCalls,\n\ttype WalletSendCallsParams\n} from \"@xmtp/content-type-wallet-send-calls\"\n","// ===================================================================\n// Betting Configuration\n// ===================================================================\nexport const DEFAULT_OPTIONS = [\"yes\", \"no\"]\nexport const DEFAULT_AMOUNT = \"0.1\"\nexport const MAX_USDC_AMOUNT = 10 // Maximum allowed USDC transaction amount\n","import { logger } from \"@hybrd/utils\"\nimport { ReactionCodec } from \"@xmtp/content-type-reaction\"\nimport { ReplyCodec } from \"@xmtp/content-type-reply\"\nimport { TransactionReferenceCodec } from \"@xmtp/content-type-transaction-reference\"\nimport { WalletSendCallsCodec } from \"@xmtp/content-type-wallet-send-calls\"\nimport { Client, IdentifierKind, type Signer, XmtpEnv } from \"@xmtp/node-sdk\"\nimport { getRandomValues } from \"node:crypto\"\nimport fs from \"node:fs\"\nimport path from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport { fromString, toString as uint8arraysToString } from \"uint8arrays\"\nimport { createWalletClient, http, toBytes } from \"viem\"\nimport { privateKeyToAccount } from \"viem/accounts\"\nimport { sepolia } from \"viem/chains\"\nimport { revokeOldInstallations } from \"../scripts/revoke-installations\"\nimport { XmtpClient } from \"./types\"\n\n// ===================================================================\n// Module Setup\n// ===================================================================\n// ES module equivalent of __dirname\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = path.dirname(__filename)\n\n// ===================================================================\n// Type Definitions\n// ===================================================================\ninterface User {\n\tkey: `0x${string}`\n\taccount: ReturnType<typeof privateKeyToAccount>\n\twallet: any // Simplified to avoid deep type instantiation\n}\n\n// ===================================================================\n// User and Signer Creation\n// ===================================================================\nexport const createUser = (key: string): User => {\n\tconst account = privateKeyToAccount(key as `0x${string}`)\n\treturn {\n\t\tkey: key as `0x${string}`,\n\t\taccount,\n\t\twallet: createWalletClient({\n\t\t\taccount,\n\t\t\tchain: sepolia,\n\t\t\ttransport: http()\n\t\t})\n\t}\n}\n\nexport const createSigner = (key: string): Signer => {\n\tif (!key || typeof key !== \"string\") {\n\t\tthrow new Error(\"XMTP wallet key must be a non-empty string\")\n\t}\n\tconst sanitizedKey = key.startsWith(\"0x\") ? key : `0x${key}`\n\tconst user = createUser(sanitizedKey)\n\treturn {\n\t\ttype: \"EOA\",\n\t\tgetIdentifier: () => ({\n\t\t\tidentifierKind: 0 as IdentifierKind.Ethereum, // Use numeric value to avoid ambient const enum issue\n\t\t\tidentifier: user.account.address.toLowerCase()\n\t\t}),\n\t\tsignMessage: async (message: string) => {\n\t\t\tconst signature = await user.wallet.signMessage({\n\t\t\t\tmessage,\n\t\t\t\taccount: user.account\n\t\t\t})\n\t\t\treturn toBytes(signature)\n\t\t}\n\t}\n}\n\n// XMTP XmtpClient setup\n// const xmtpClient: XmtpClient | null = null\n\n// Function to clear XMTP database when hitting installation limits\nasync function clearXMTPDatabase(address: string, env: string) {\n\tlogger.debug(\"๐Ÿงน Clearing XMTP database to resolve installation limit...\")\n\n\t// Get the storage directory using the same logic as getDbPath\n\tconst getStorageDirectory = () => {\n\t\tconst customStoragePath = process.env.XMTP_STORAGE_PATH\n\n\t\tif (customStoragePath) {\n\t\t\treturn path.isAbsolute(customStoragePath)\n\t\t\t\t? customStoragePath\n\t\t\t\t: path.resolve(process.cwd(), customStoragePath)\n\t\t}\n\n\t\t// Use existing logic as fallback\n\t\tconst projectRoot =\n\t\t\tprocess.env.PROJECT_ROOT || path.resolve(__dirname, \"../../..\")\n\n\t\treturn path.join(projectRoot, \".data/xmtp\") // Local development\n\t}\n\n\t// Clear local database files\n\tconst dbPattern = `${env}-${address}.db3`\n\tconst storageDir = getStorageDirectory()\n\n\t// Primary storage directory\n\tconst possiblePaths = [\n\t\tstorageDir,\n\t\t// Legacy fallback paths for backward compatibility\n\t\tpath.join(process.cwd(), \".data\", \"xmtp\"),\n\t\tpath.join(process.cwd(), \"..\", \".data\", \"xmtp\"),\n\t\tpath.join(process.cwd(), \"..\", \"..\", \".data\", \"xmtp\")\n\t]\n\n\tfor (const dir of possiblePaths) {\n\t\ttry {\n\t\t\tif (fs.existsSync(dir)) {\n\t\t\t\tconst files = fs.readdirSync(dir)\n\t\t\t\tconst matchingFiles = files.filter(\n\t\t\t\t\t(file) =>\n\t\t\t\t\t\tfile.includes(dbPattern) ||\n\t\t\t\t\t\tfile.includes(address) ||\n\t\t\t\t\t\tfile.includes(`xmtp-${env}-${address}`)\n\t\t\t\t)\n\n\t\t\t\tfor (const file of matchingFiles) {\n\t\t\t\t\tconst fullPath = path.join(dir, file)\n\t\t\t\t\ttry {\n\t\t\t\t\t\tfs.unlinkSync(fullPath)\n\t\t\t\t\t\tlogger.debug(`โœ… Removed: ${fullPath}`)\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tlogger.debug(`โš ๏ธ Could not remove ${fullPath}:`, err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (err) {\n\t\t\t// Ignore errors when checking directories\n\t\t}\n\t}\n}\n\nexport async function createXMTPClient(\n\tprivateKey: string,\n\topts?: {\n\t\tpersist?: boolean\n\t\tmaxRetries?: number\n\t\tstoragePath?: string\n\t}\n): Promise<XmtpClient> {\n\tconst { persist = true, maxRetries = 3, storagePath } = opts ?? {}\n\tlet attempt = 0\n\n\t// Extract common variables for error handling\n\t// const actualSigner = signer\n\tconst signer = createSigner(privateKey)\n\n\tif (!signer) {\n\t\tthrow new Error(\n\t\t\t\"No signer provided and XMTP_WALLET_KEY environment variable is not set\"\n\t\t)\n\t}\n\n\tconst { XMTP_DB_ENCRYPTION_KEY, XMTP_ENV } = process.env\n\n\t// Get the wallet address to use the correct database\n\tconst identifier = await signer.getIdentifier()\n\tconst address = identifier.identifier\n\n\twhile (attempt < maxRetries) {\n\t\ttry {\n\t\t\tlogger.debug(\n\t\t\t\t`๐Ÿ”„ Attempt ${attempt + 1}/${maxRetries} to create XMTP client...`\n\t\t\t)\n\n\t\t\t// Always require encryption key and persistence - no stateless mode\n\t\t\tif (!persist) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Stateless mode is not supported. XMTP client must run in persistent mode \" +\n\t\t\t\t\t\t\"to properly receive and process messages. Set persist: true or remove the persist option \" +\n\t\t\t\t\t\t\"to use the default persistent mode.\"\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tif (!XMTP_DB_ENCRYPTION_KEY) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"XMTP_DB_ENCRYPTION_KEY must be set for persistent mode\"\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tconst dbEncryptionKey = getEncryptionKeyFromHex(XMTP_DB_ENCRYPTION_KEY)\n\t\t\tconst dbPath = await getDbPath(\n\t\t\t\t`${XMTP_ENV || \"dev\"}-${address}`,\n\t\t\t\tstoragePath\n\t\t\t)\n\t\t\tlogger.debug(`๐Ÿ“ Using database path: ${dbPath}`)\n\n\t\t\t// Always create a fresh client and sync it\n\t\t\tconst client = await Client.create(signer, {\n\t\t\t\tdbEncryptionKey,\n\t\t\t\tenv: XMTP_ENV as XmtpEnv,\n\t\t\t\tdbPath,\n\t\t\t\tcodecs: [\n\t\t\t\t\tnew ReplyCodec(),\n\t\t\t\t\tnew ReactionCodec(),\n\t\t\t\t\tnew WalletSendCallsCodec(),\n\t\t\t\t\tnew TransactionReferenceCodec()\n\t\t\t\t]\n\t\t\t})\n\n\t\t\t// Force sync conversations to ensure we have the latest data\n\t\t\tlogger.debug(\"๐Ÿ“ก Syncing conversations to ensure latest state...\")\n\t\t\tawait client.conversations.sync()\n\n\t\t\tawait backupDbToPersistentStorage(\n\t\t\t\tdbPath,\n\t\t\t\t`${XMTP_ENV || \"dev\"}-${address}`\n\t\t\t)\n\n\t\t\tconsole.log(`Wallet: ${address}`)\n\t\t\tconsole.log(`Env: ${XMTP_ENV || \"dev\"}`)\n\t\t\tconsole.log(`Storage: persistent`)\n\n\t\t\treturn client as unknown as XmtpClient\n\t\t} catch (error) {\n\t\t\tattempt++\n\n\t\t\tif (\n\t\t\t\terror instanceof Error &&\n\t\t\t\terror.message.includes(\"5/5 installations\")\n\t\t\t) {\n\t\t\t\tconsole.log(\n\t\t\t\t\t`๐Ÿ’ฅ Installation limit reached (attempt ${attempt}/${maxRetries})`\n\t\t\t\t)\n\n\t\t\t\tif (attempt < maxRetries) {\n\t\t\t\t\t// Get wallet address for database clearing\n\t\t\t\t\tconst identifier = await signer.getIdentifier()\n\t\t\t\t\tconst address = identifier.identifier\n\n\t\t\t\t\t// Extract inboxId from the error message\n\t\t\t\t\tconst inboxIdMatch = error.message.match(/InboxID ([a-f0-9]+)/)\n\t\t\t\t\tconst inboxId = inboxIdMatch ? inboxIdMatch[1] : undefined\n\n\t\t\t\t\t// First try to revoke old installations\n\t\t\t\t\tconst revocationSuccess = await revokeOldInstallations(\n\t\t\t\t\t\tsigner,\n\t\t\t\t\t\tinboxId\n\t\t\t\t\t)\n\n\t\t\t\t\tif (revocationSuccess) {\n\t\t\t\t\t\tconsole.log(\"๐ŸŽฏ Installations revoked, retrying connection...\")\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\"โš ๏ธ Installation revocation failed or not needed, clearing database...\"\n\t\t\t\t\t\t)\n\t\t\t\t\t\t// Clear database as fallback\n\t\t\t\t\t\tawait clearXMTPDatabase(address, process.env.XMTP_ENV || \"dev\")\n\t\t\t\t\t}\n\n\t\t\t\t\t// Wait a bit before retrying\n\t\t\t\t\tconst delay = Math.pow(2, attempt) * 1000 // Exponential backoff\n\t\t\t\t\tconsole.log(`โณ Waiting ${delay}ms before retry...`)\n\t\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, delay))\n\t\t\t\t} else {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\"โŒ Failed to resolve installation limit after all retries\"\n\t\t\t\t\t)\n\t\t\t\t\tconsole.error(\"๐Ÿ’ก Possible solutions:\")\n\t\t\t\t\tconsole.error(\" 1. Use a different wallet (generate new keys)\")\n\t\t\t\t\tconsole.error(\" 2. Switch XMTP environments (dev <-> production)\")\n\t\t\t\t\tconsole.error(\" 3. Wait and try again later\")\n\t\t\t\t\tconsole.error(\" 4. Contact XMTP support for manual intervention\")\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t} else if (\n\t\t\t\terror instanceof Error &&\n\t\t\t\terror.message.includes(\"Association error: Missing identity update\")\n\t\t\t) {\n\t\t\t\tconsole.log(\n\t\t\t\t\t`๐Ÿ”„ Identity association error detected (attempt ${attempt}/${maxRetries})`\n\t\t\t\t)\n\n\t\t\t\tif (attempt < maxRetries) {\n\t\t\t\t\tconsole.log(\"๐Ÿ”ง Attempting automatic identity refresh...\")\n\n\t\t\t\t\t// Try to refresh identity by creating a persistent client first\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconsole.log(\"๐Ÿ“ Creating persistent client to refresh identity...\")\n\t\t\t\t\t\tconst tempEncryptionKey = XMTP_DB_ENCRYPTION_KEY\n\t\t\t\t\t\t\t? getEncryptionKeyFromHex(XMTP_DB_ENCRYPTION_KEY)\n\t\t\t\t\t\t\t: getEncryptionKeyFromHex(generateEncryptionKeyHex())\n\t\t\t\t\t\tconst tempClient = await Client.create(signer, {\n\t\t\t\t\t\t\tdbEncryptionKey: tempEncryptionKey,\n\t\t\t\t\t\t\tenv: XMTP_ENV as XmtpEnv,\n\t\t\t\t\t\t\tdbPath: await getDbPath(\n\t\t\t\t\t\t\t\t`${XMTP_ENV || \"dev\"}-${address}`,\n\t\t\t\t\t\t\t\tstoragePath\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tcodecs: [\n\t\t\t\t\t\t\t\tnew ReplyCodec(),\n\t\t\t\t\t\t\t\tnew ReactionCodec(),\n\t\t\t\t\t\t\t\tnew WalletSendCallsCodec(),\n\t\t\t\t\t\t\t\tnew TransactionReferenceCodec()\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\tconsole.log(\"๐Ÿ“ก Syncing identity and conversations...\")\n\t\t\t\t\t\tawait tempClient.conversations.sync()\n\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\"โœ… Identity refresh successful, retrying original request...\"\n\t\t\t\t\t\t)\n\n\t\t\t\t\t\t// Wait a bit before retrying\n\t\t\t\t\t\tconst delay = Math.pow(2, attempt) * 1000 // Exponential backoff\n\t\t\t\t\t\tconsole.log(`โณ Waiting ${delay}ms before retry...`)\n\t\t\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, delay))\n\t\t\t\t\t} catch (refreshError) {\n\t\t\t\t\t\tconsole.log(`โŒ Identity refresh failed:`, refreshError)\n\t\t\t\t\t\t// Continue to the retry logic\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\"โŒ Failed to resolve identity association error after all retries\"\n\t\t\t\t\t)\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\"๐Ÿ’ก Try running: pnpm with-env pnpm --filter @hybrd/xmtp refresh:identity\"\n\t\t\t\t\t)\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// For other errors, don't retry\n\t\t\t\tthrow error\n\t\t\t}\n\t\t}\n\t}\n\n\tthrow new Error(\"Max retries exceeded\")\n}\n\n// ===================================================================\n// Encryption Key Management\n// ===================================================================\n/**\n * Generate a random encryption key\n * @returns The encryption key as a hex string\n */\nexport const generateEncryptionKeyHex = () => {\n\tconst uint8Array = getRandomValues(new Uint8Array(32))\n\treturn uint8arraysToString(uint8Array, \"hex\")\n}\n\n/**\n * Get the encryption key from a hex string\n * @param hex - The hex string\n * @returns The encryption key as Uint8Array\n */\nconst getEncryptionKeyFromHex = (hex: string): Uint8Array => {\n\treturn fromString(hex, \"hex\")\n}\n\n// ===================================================================\n// Database Path Management\n// ===================================================================\nexport const getDbPath = async (description = \"xmtp\", storagePath?: string) => {\n\t// Allow custom storage path via environment variable\n\tconst customStoragePath = process.env.XMTP_STORAGE_PATH\n\n\tlet volumePath: string\n\n\tif (customStoragePath) {\n\t\t// Use custom storage path if provided\n\t\tvolumePath = path.isAbsolute(customStoragePath)\n\t\t\t? customStoragePath\n\t\t\t: path.resolve(process.cwd(), customStoragePath)\n\t} else if (storagePath) {\n\t\tvolumePath = path.isAbsolute(storagePath)\n\t\t\t? storagePath\n\t\t\t: path.resolve(process.cwd(), storagePath)\n\t} else {\n\t\t// Use existing logic as fallback\n\t\tconst projectRoot =\n\t\t\tprocess.env.PROJECT_ROOT || path.resolve(__dirname, \"../../..\")\n\n\t\t// Default storage path for local development\n\t\tvolumePath = path.join(projectRoot, \".data/xmtp\")\n\t}\n\n\tconst dbPath = `${volumePath}/${description}.db3`\n\n\tif (typeof globalThis !== \"undefined\" && \"XMTP_STORAGE\" in globalThis) {\n\t\ttry {\n\t\t\tconsole.log(`๐Ÿ“ฆ Using Cloudflare R2 storage for: ${dbPath}`)\n\n\t\t\tconst r2Bucket = (globalThis as any).XMTP_STORAGE\n\t\t\tconst remotePath = `xmtp-databases/${description}.db3`\n\n\t\t\ttry {\n\t\t\t\tconst existingObject = await r2Bucket.head(remotePath)\n\t\t\t\tif (existingObject) {\n\t\t\t\t\tconsole.log(`๐Ÿ“ฅ Downloading existing database from R2 storage...`)\n\n\t\t\t\t\tif (!fs.existsSync(volumePath)) {\n\t\t\t\t\t\tfs.mkdirSync(volumePath, { recursive: true })\n\t\t\t\t\t}\n\n\t\t\t\t\tconst object = await r2Bucket.get(remotePath)\n\t\t\t\t\tif (object) {\n\t\t\t\t\t\tconst fileData = await object.arrayBuffer()\n\t\t\t\t\t\tfs.writeFileSync(dbPath, new Uint8Array(fileData))\n\t\t\t\t\t\tconsole.log(`โœ… Database downloaded from R2 storage`)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconsole.log(`๐Ÿ“ No existing database found in R2 storage`)\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.log(`โš ๏ธ Failed to download database from R2 storage:`, error)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.log(`โš ๏ธ R2 storage not available:`, error)\n\t\t}\n\t}\n\n\tif (!fs.existsSync(volumePath)) {\n\t\tfs.mkdirSync(volumePath, { recursive: true })\n\t}\n\n\treturn dbPath\n}\n\nconst backupDbToPersistentStorage = async (\n\tdbPath: string,\n\tdescription: string\n) => {\n\tif (\n\t\ttypeof globalThis !== \"undefined\" &&\n\t\t\"XMTP_STORAGE\" in globalThis &&\n\t\tfs.existsSync(dbPath)\n\t) {\n\t\ttry {\n\t\t\tconsole.log(`๐Ÿ“ฆ Backing up database to R2 storage: ${dbPath}`)\n\n\t\t\tconst r2Bucket = (globalThis as any).XMTP_STORAGE\n\t\t\tconst remotePath = `xmtp-databases/${description}.db3`\n\n\t\t\tconst fileData = fs.readFileSync(dbPath)\n\t\t\tawait r2Bucket.put(remotePath, fileData)\n\t\t\tconsole.log(`โœ… Database backed up to R2 storage: ${remotePath}`)\n\t\t} catch (error) {\n\t\t\tconsole.log(`โš ๏ธ Failed to backup database to R2 storage:`, error)\n\t\t}\n\t}\n}\n\n// ===================================================================\n// Logging and Debugging\n// ===================================================================\nexport const logAgentDetails = async (\n\tclients: XmtpClient | XmtpClient[]\n): Promise<void> => {\n\tconst clientsByAddress = Array.isArray(clients)\n\t\t? clients.reduce<Record<string, XmtpClient[]>>((acc, XmtpClient) => {\n\t\t\t\tconst address = XmtpClient.accountIdentifier?.identifier ?? \"\"\n\t\t\t\tacc[address] = acc[address] ?? []\n\t\t\t\tacc[address].push(XmtpClient)\n\t\t\t\treturn acc\n\t\t\t}, {})\n\t\t: {\n\t\t\t\t[clients.accountIdentifier?.identifier ?? \"\"]: [clients]\n\t\t\t}\n\n\tfor (const [address, clientGroup] of Object.entries(clientsByAddress)) {\n\t\tconst firstClient = clientGroup[0]\n\t\tconst inboxId = firstClient?.inboxId\n\t\tconst environments = clientGroup\n\t\t\t.map((c) => c.options?.env ?? \"dev\")\n\t\t\t.join(\", \")\n\t\tconsole.log(`\\x1b[38;2;252;76;52m\n โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— \n โ•šโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ•‘โ•šโ•โ•โ–ˆโ–ˆโ•”โ•โ•โ•โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—\n โ•šโ–ˆโ–ˆโ–ˆโ•”โ• โ–ˆโ–ˆโ•”โ–ˆโ–ˆโ–ˆโ–ˆโ•”โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•\n โ–ˆโ–ˆโ•”โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•‘โ•šโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•”โ•โ•โ•โ• \n โ–ˆโ–ˆโ•”โ• โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•‘ โ•šโ•โ• โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ \n โ•šโ•โ• โ•šโ•โ•โ•šโ•โ• โ•šโ•โ• โ•šโ•โ• โ•šโ•โ• \n \\x1b[0m`)\n\n\t\tconst urls = [`http://xmtp.chat/dm/${address}`]\n\n\t\tconst conversations = await firstClient?.conversations.list()\n\n\t\tconsole.log(`\n โœ“ XMTP XmtpClient:\n โ€ข Address: ${address}\n โ€ข Conversations: ${conversations?.length}\n โ€ข InboxId: ${inboxId}\n โ€ข Networks: ${environments}\n ${urls.map((url) => `โ€ข URL: ${url}`).join(\"\\n\")}`)\n\t}\n}\n\n// ===================================================================\n// Environment Validation\n// ===================================================================\nexport function validateEnvironment(vars: string[]): Record<string, string> {\n\tconst missing = vars.filter((v) => !process.env[v])\n\n\tif (missing.length) {\n\t\ttry {\n\t\t\tconst envPath = path.resolve(process.cwd(), \".env\")\n\t\t\tif (fs.existsSync(envPath)) {\n\t\t\t\tconst envVars = fs\n\t\t\t\t\t.readFileSync(envPath, \"utf-8\")\n\t\t\t\t\t.split(\"\\n\")\n\t\t\t\t\t.filter((line) => line.trim() && !line.startsWith(\"#\"))\n\t\t\t\t\t.reduce<Record<string, string>>((acc, line) => {\n\t\t\t\t\t\tconst [key, ...val] = line.split(\"=\")\n\t\t\t\t\t\tif (key && val.length) acc[key.trim()] = val.join(\"=\").trim()\n\t\t\t\t\t\treturn acc\n\t\t\t\t\t}, {})\n\n\t\t\t\tmissing.forEach((v) => {\n\t\t\t\t\tif (envVars[v]) process.env[v] = envVars[v]\n\t\t\t\t})\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.error(e)\n\t\t\t/* ignore errors */\n\t\t}\n\n\t\tconst stillMissing = vars.filter((v) => !process.env[v])\n\t\tif (stillMissing.length) {\n\t\t\tconsole.error(\"Missing env vars:\", stillMissing.join(\", \"))\n\t\t\tprocess.exit(1)\n\t\t}\n\t}\n\n\treturn vars.reduce<Record<string, string>>((acc, key) => {\n\t\tacc[key] = process.env[key] as string\n\t\treturn acc\n\t}, {})\n}\n\n/**\n * Diagnose XMTP environment and identity issues (internal use only)\n */\nasync function diagnoseXMTPIdentityIssue(\n\tclient: XmtpClient,\n\tinboxId: string,\n\tenvironment: string\n): Promise<{\n\tcanResolve: boolean\n\tsuggestions: string[]\n\tdetails: Record<string, any>\n}> {\n\tconst suggestions: string[] = []\n\tconst details: Record<string, any> = {\n\t\tenvironment,\n\t\tinboxId,\n\t\ttimestamp: new Date().toISOString()\n\t}\n\n\ttry {\n\t\t// Try to resolve the inbox state\n\t\tconst inboxState = await client.preferences.inboxStateFromInboxIds([\n\t\t\tinboxId\n\t\t])\n\n\t\tif (inboxState.length === 0) {\n\t\t\tsuggestions.push(\n\t\t\t\t`Inbox ID ${inboxId} not found in ${environment} environment`\n\t\t\t)\n\t\t\tsuggestions.push(\n\t\t\t\t\"Try switching XMTP_ENV to 'dev' if currently 'production' or vice versa\"\n\t\t\t)\n\t\t\tsuggestions.push(\n\t\t\t\t\"Verify the user has created an identity on this XMTP network\"\n\t\t\t)\n\t\t\tdetails.inboxStateFound = false\n\t\t\treturn { canResolve: false, suggestions, details }\n\t\t}\n\n\t\tconst inbox = inboxState[0]\n\t\tif (!inbox) {\n\t\t\tsuggestions.push(\"Inbox state returned empty data\")\n\t\t\tdetails.inboxStateFound = false\n\t\t\treturn { canResolve: false, suggestions, details }\n\t\t}\n\n\t\tdetails.inboxStateFound = true\n\t\tdetails.identifierCount = inbox.identifiers?.length || 0\n\n\t\tif (!inbox.identifiers || inbox.identifiers.length === 0) {\n\t\t\tsuggestions.push(\"Inbox found but has no identifiers\")\n\t\t\tsuggestions.push(\"This indicates incomplete identity registration\")\n\t\t\tsuggestions.push(\"User may need to re-register their identity on XMTP\")\n\t\t\tdetails.hasIdentifiers = false\n\t\t\treturn { canResolve: false, suggestions, details }\n\t\t}\n\n\t\t// Successfully resolved\n\t\tdetails.hasIdentifiers = true\n\t\tdetails.resolvedAddress = inbox.identifiers[0]?.identifier\n\t\treturn {\n\t\t\tcanResolve: true,\n\t\t\tsuggestions: [\"Identity resolved successfully\"],\n\t\t\tdetails\n\t\t}\n\t} catch (error) {\n\t\tconst errorMessage = error instanceof Error ? error.message : String(error)\n\t\tdetails.error = errorMessage\n\n\t\tif (errorMessage.includes(\"Association error\")) {\n\t\t\tsuggestions.push(\"XMTP identity association error detected\")\n\t\t\tsuggestions.push(\n\t\t\t\t\"Check if user exists on the correct XMTP environment (dev vs production)\"\n\t\t\t)\n\t\t\tsuggestions.push(\n\t\t\t\t\"Identity may need to be recreated on the current environment\"\n\t\t\t)\n\t\t}\n\n\t\tif (errorMessage.includes(\"Missing identity update\")) {\n\t\t\tsuggestions.push(\"Missing identity updates in XMTP network\")\n\t\t\tsuggestions.push(\"This can indicate network sync issues\")\n\t\t\tsuggestions.push(\"Wait a few minutes and retry, or recreate identity\")\n\t\t}\n\n\t\tif (errorMessage.includes(\"database\") || errorMessage.includes(\"storage\")) {\n\t\t\tsuggestions.push(\"XMTP local database/storage issue\")\n\t\t\tsuggestions.push(\"Try clearing XMTP database and resyncing\")\n\t\t\tsuggestions.push(\"Check .data/xmtp directory permissions\")\n\t\t}\n\n\t\tsuggestions.push(\"Consider testing with a fresh XMTP identity\")\n\t\treturn { canResolve: false, suggestions, details }\n\t}\n}\n\n// ===================================================================\n// Enhanced Connection Management & Health Monitoring\n// ===================================================================\n\nexport interface XMTPConnectionConfig {\n\tmaxRetries?: number\n\tretryDelayMs?: number\n\thealthCheckIntervalMs?: number\n\tconnectionTimeoutMs?: number\n\treconnectOnFailure?: boolean\n}\n\nexport interface XMTPConnectionHealth {\n\tisConnected: boolean\n\tlastHealthCheck: Date\n\tconsecutiveFailures: number\n\ttotalReconnects: number\n\tavgResponseTime: number\n}\n\nexport class XMTPConnectionManager {\n\tprivate client: XmtpClient | null = null\n\tprivate privateKey: string\n\tprivate config: Required<XMTPConnectionConfig>\n\tprivate health: XMTPConnectionHealth\n\tprivate healthCheckTimer: NodeJS.Timeout | null = null\n\tprivate isReconnecting = false\n\n\tconstructor(privateKey: string, config: XMTPConnectionConfig = {}) {\n\t\tthis.privateKey = privateKey\n\t\tthis.config = {\n\t\t\tmaxRetries: config.maxRetries ?? 5,\n\t\t\tretryDelayMs: config.retryDelayMs ?? 1000,\n\t\t\thealthCheckIntervalMs: config.healthCheckIntervalMs ?? 30000,\n\t\t\tconnectionTimeoutMs: config.connectionTimeoutMs ?? 10000,\n\t\t\treconnectOnFailure: config.reconnectOnFailure ?? true\n\t\t}\n\n\t\tthis.health = {\n\t\t\tisConnected: false,\n\t\t\tlastHealthCheck: new Date(),\n\t\t\tconsecutiveFailures: 0,\n\t\t\ttotalReconnects: 0,\n\t\t\tavgResponseTime: 0\n\t\t}\n\t}\n\n\tasync connect(persist = false): Promise<XmtpClient> {\n\t\tif (this.client && this.health.isConnected) {\n\t\t\treturn this.client\n\t\t}\n\n\t\tlet attempt = 0\n\t\twhile (attempt < this.config.maxRetries) {\n\t\t\ttry {\n\t\t\t\tconsole.log(\n\t\t\t\t\t`๐Ÿ”„ XMTP connection attempt ${attempt + 1}/${this.config.maxRetries}`\n\t\t\t\t)\n\n\t\t\t\tthis.client = await createXMTPClient(this.privateKey, { persist })\n\t\t\t\tthis.health.isConnected = true\n\t\t\t\tthis.health.consecutiveFailures = 0\n\n\t\t\t\t// Start health monitoring\n\t\t\t\tthis.startHealthMonitoring()\n\n\t\t\t\tconsole.log(\"โœ… XMTP client connected successfully\")\n\t\t\t\treturn this.client\n\t\t\t} catch (error) {\n\t\t\t\tattempt++\n\t\t\t\tthis.health.consecutiveFailures++\n\n\t\t\t\tconsole.error(`โŒ XMTP connection attempt ${attempt} failed:`, error)\n\n\t\t\t\tif (attempt < this.config.maxRetries) {\n\t\t\t\t\tconst delay = this.config.retryDelayMs * Math.pow(2, attempt - 1)\n\t\t\t\t\tconsole.log(`โณ Retrying in ${delay}ms...`)\n\t\t\t\t\tawait this.sleep(delay)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t`Failed to connect to XMTP after ${this.config.maxRetries} attempts`\n\t\t)\n\t}\n\n\t// private async createClientWithTimeout(persist: boolean): Promise<XmtpClient> {\n\t// const timeoutPromise = new Promise<never>((_, reject) => {\n\t// setTimeout(\n\t// () => reject(new Error(\"Connection timeout\")),\n\t// this.config.connectionTimeoutMs\n\t// )\n\t// })\n\n\t// const clientPromise = createXMTPClient(this.signer, { persist })\n\n\t// return Promise.race([clientPromise, timeoutPromise])\n\t// }\n\n\tprivate startHealthMonitoring(): void {\n\t\tif (this.healthCheckTimer) {\n\t\t\tclearInterval(this.healthCheckTimer)\n\t\t}\n\n\t\tthis.healthCheckTimer = setInterval(() => {\n\t\t\tthis.performHealthCheck()\n\t\t}, this.config.healthCheckIntervalMs)\n\t}\n\n\tprivate async performHealthCheck(): Promise<void> {\n\t\tif (!this.client) return\n\n\t\tconst startTime = Date.now()\n\n\t\ttry {\n\t\t\t// Simple health check: try to list conversations\n\t\t\tawait this.client.conversations.list()\n\n\t\t\tconst responseTime = Date.now() - startTime\n\t\t\tthis.health.avgResponseTime =\n\t\t\t\t(this.health.avgResponseTime + responseTime) / 2\n\t\t\tthis.health.lastHealthCheck = new Date()\n\t\t\tthis.health.consecutiveFailures = 0\n\t\t\tthis.health.isConnected = true\n\n\t\t\tconsole.log(`๐Ÿ’“ XMTP health check passed (${responseTime}ms)`)\n\t\t} catch (error) {\n\t\t\tthis.health.consecutiveFailures++\n\t\t\tthis.health.isConnected = false\n\n\t\t\tconsole.error(`๐Ÿ’” XMTP health check failed:`, error)\n\n\t\t\t// Trigger reconnection if enabled\n\t\t\tif (this.config.reconnectOnFailure && !this.isReconnecting) {\n\t\t\t\tthis.handleConnectionFailure()\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async handleConnectionFailure(): Promise<void> {\n\t\tif (this.isReconnecting) return\n\n\t\tthis.isReconnecting = true\n\t\tthis.health.totalReconnects++\n\n\t\tconsole.log(\"๐Ÿ”„ XMTP connection lost, attempting to reconnect...\")\n\n\t\ttry {\n\t\t\tthis.client = null\n\t\t\tawait this.connect()\n\t\t\tconsole.log(\"โœ… XMTP reconnection successful\")\n\t\t} catch (error) {\n\t\t\tconsole.error(\"โŒ XMTP reconnection failed:\", error)\n\t\t} finally {\n\t\t\tthis.isReconnecting = false\n\t\t}\n\t}\n\n\tprivate sleep(ms: number): Promise<void> {\n\t\treturn new Promise((resolve) => setTimeout(resolve, ms))\n\t}\n\n\tgetHealth(): XMTPConnectionHealth {\n\t\treturn { ...this.health }\n\t}\n\n\tgetClient(): XmtpClient | null {\n\t\treturn this.client\n\t}\n\n\tasync disconnect(): Promise<void> {\n\t\tif (this.healthCheckTimer) {\n\t\t\tclearInterval(this.healthCheckTimer)\n\t\t\tthis.healthCheckTimer = null\n\t\t}\n\n\t\tthis.client = null\n\t\tthis.health.isConnected = false\n\t\tconsole.log(\"๐Ÿ”Œ XMTP client disconnected\")\n\t}\n}\n\n// Enhanced client creation with connection management\nexport async function createXMTPConnectionManager(\n\tprivateKey: string,\n\tconfig?: XMTPConnectionConfig\n): Promise<XMTPConnectionManager> {\n\tconst manager = new XMTPConnectionManager(privateKey, config)\n\tawait manager.connect()\n\treturn manager\n}\n\n// ===================================================================\n// User Address Resolution with Auto-Refresh\n// ===================================================================\n","import { Client, type Signer } from \"@xmtp/node-sdk\"\nimport { createSigner } from \"../src/client\"\n\n// Function to revoke old installations when hitting the limit\nexport async function revokeOldInstallations(signer: Signer, inboxId?: string) {\n\tconsole.log(\"๐Ÿ”ง Attempting to revoke old installations...\")\n\n\ttry {\n\t\t// If we don't have the inboxId, we need to extract it from a temporary client attempt\n\t\tif (!inboxId) {\n\t\t\tconsole.log(\"โ„น๏ธ No inboxId provided, cannot revoke installations\")\n\t\t\treturn false\n\t\t}\n\n\t\tconst inboxStates = await Client.inboxStateFromInboxIds(\n\t\t\t[inboxId],\n\t\t\tprocess.env.XMTP_ENV as \"dev\" | \"production\"\n\t\t)\n\n\t\tif (!inboxStates[0]) {\n\t\t\tconsole.log(\"โŒ No inbox state found for the provided inboxId\")\n\t\t\treturn false\n\t\t}\n\n\t\tconst toRevokeInstallationBytes = inboxStates[0].installations.map(\n\t\t\t(i: { bytes: Uint8Array }) => i.bytes\n\t\t)\n\n\t\tawait Client.revokeInstallations(\n\t\t\tsigner,\n\t\t\tinboxId,\n\t\t\ttoRevokeInstallationBytes,\n\t\t\tprocess.env.XMTP_ENV as \"dev\" | \"production\"\n\t\t)\n\n\t\tconst resultingStates = await Client.inboxStateFromInboxIds(\n\t\t\t[inboxId],\n\t\t\tprocess.env.XMTP_ENV as \"dev\" | \"production\"\n\t\t)\n\n\t\tconsole.log(\n\t\t\t`๐Ÿ“‹ Revoked installations: ${toRevokeInstallationBytes.length} installations`\n\t\t)\n\t\tconsole.log(\n\t\t\t`๐Ÿ“‹ Resulting state: ${resultingStates[0]?.installations.length || 0} installations`\n\t\t)\n\n\t\treturn true\n\t} catch (error) {\n\t\tconsole.error(\"โŒ Error during installation revocation:\", error)\n\t\treturn false\n\t}\n}\n\n// CLI script to revoke installations\nasync function main() {\n\tconst { XMTP_WALLET_KEY } = process.env\n\tconst inboxId = process.argv[2]\n\n\tif (!XMTP_WALLET_KEY) {\n\t\tconsole.error(\"โŒ XMTP_WALLET_KEY is required\")\n\t\tprocess.exit(1)\n\t}\n\n\tif (!inboxId) {\n\t\tconsole.error(\"โŒ InboxID is required as CLI argument\")\n\t\tconsole.error(\"Usage: tsx revoke-installations.ts <inboxId>\")\n\t\tprocess.exit(1)\n\t}\n\n\tconst signer = createSigner(XMTP_WALLET_KEY)\n\tconst identifier = await signer.getIdentifier()\n\tconst address = identifier.identifier\n\n\tconsole.log(`๐Ÿ”‘ Wallet Address: ${address}`)\n\tconsole.log(`๐Ÿ“‹ Inbox ID: ${inboxId}`)\n\n\t// Try to revoke installations\n\tconst success = await revokeOldInstallations(signer, inboxId)\n\n\tif (success) {\n\t\tconsole.log(\"โœ… Successfully revoked installations\")\n\t} else {\n\t\tconsole.log(\"โŒ Failed to revoke installations\")\n\t\tprocess.exit(1)\n\t}\n}\n\n// Run if called directly\nif (import.meta.url === `file://${process.argv[1]}`) {\n\tmain().catch((error) => {\n\t\tconsole.error(\"๐Ÿ’ฅ Fatal error:\", error)\n\t\tprocess.exit(1)\n\t})\n}\n","import {\n\tAgent as XmtpAgent,\n\tXmtpEnv,\n\tcreateSigner,\n\tcreateUser\n} from \"@xmtp/agent-sdk\"\n\nimport type {\n\tAgentMessage,\n\tAgentRuntime,\n\tBehaviorContext,\n\tBehaviorRegistry,\n\tPlugin,\n\tPluginContext,\n\tXmtpClient,\n\tXmtpConversation,\n\tXmtpMessage\n} from \"@hybrd/types\"\nimport { logger } from \"@hybrd/utils\"\nimport { randomUUID } from \"node:crypto\"\nimport { createXMTPClient, getDbPath } from \"./client\"\nimport { ContentTypeReply, ContentTypeText, type Reply } from \"./index\"\n\n// Re-export types from @hybrd/types for backward compatibility\nexport type { Plugin }\n\n/**\n * Send a response with threading support\n */\nasync function sendResponse(\n\tconversation: XmtpConversation,\n\ttext: string,\n\toriginalMessageId: string,\n\tbehaviorContext?: BehaviorContext\n) {\n\tconst shouldThread = behaviorContext?.sendOptions?.threaded ?? false\n\n\tif (shouldThread) {\n\t\t// Send as a reply to the original message\n\t\ttry {\n\t\t\tconst reply: Reply = {\n\t\t\t\treference: originalMessageId,\n\t\t\t\tcontentType: ContentTypeText,\n\t\t\t\tcontent: text\n\t\t\t}\n\t\t\tawait conversation.send(reply, ContentTypeReply)\n\t\t\tlogger.debug(\n\t\t\t\t`โœ… [sendResponse] Threaded reply sent successfully to message ${originalMessageId}`\n\t\t\t)\n\t\t} catch (error) {\n\t\t\tlogger.error(\n\t\t\t\t`โŒ [sendResponse] Failed to send threaded reply to message ${originalMessageId}:`,\n\t\t\t\terror\n\t\t\t)\n\t\t\t// Fall back to regular message if threaded reply fails\n\t\t\tlogger.debug(`๐Ÿ”„ [sendResponse] Falling back to regular message`)\n\t\t\tawait conversation.send(text)\n\t\t}\n\t} else {\n\t\t// Send as a regular message\n\t\tawait conversation.send(text)\n\t}\n}\n\n/**\n * XMTP Plugin that provides XMTP functionality to the agent\n *\n * @description\n * This plugin integrates XMTP messaging capabilities into the agent's\n * HTTP server. It mounts the XMTP endpoints for handling XMTP tools requests.\n */\nexport function XMTPPlugin(): Plugin<PluginContext> {\n\treturn {\n\t\tname: \"xmtp\",\n\t\tdescription: \"Provides XMTP messaging functionality\",\n\t\tapply: async (app, context): Promise<void> => {\n\t\t\tconst {\n\t\t\t\tXMTP_WALLET_KEY,\n\t\t\t\tXMTP_DB_ENCRYPTION_KEY,\n\t\t\t\tXMTP_ENV = \"production\"\n\t\t\t} = process.env\n\n\t\t\tconst { agent } = context\n\t\t\tconst pluginContext = context as PluginContext & {\n\t\t\t\tbehaviors?: BehaviorRegistry\n\t\t\t}\n\n\t\t\tif (!XMTP_WALLET_KEY) {\n\t\t\t\tthrow new Error(\"XMTP_WALLET_KEY must be set\")\n\t\t\t}\n\n\t\t\tif (!XMTP_DB_ENCRYPTION_KEY) {\n\t\t\t\tthrow new Error(\"XMTP_DB_ENCRYPTION_KEY must be set\")\n\t\t\t}\n\n\t\t\tconst user = createUser(XMTP_WALLET_KEY as `0x${string}`)\n\t\t\tconst signer = createSigner(user)\n\n\t\t\tconst xmtpClient = await createXMTPClient(\n\t\t\t\tXMTP_WALLET_KEY as `0x${string}`\n\t\t\t)\n\n\t\t\tconst address = user.account.address.toLowerCase()\n\t\t\tconst agentDbPath = await getDbPath(\n\t\t\t\t`agent-${XMTP_ENV || \"dev\"}-${address}`\n\t\t\t)\n\t\t\tlogger.debug(`๐Ÿ“ Using database path: ${agentDbPath}`)\n\n\t\t\tconst xmtp = await XmtpAgent.create(signer, {\n\t\t\t\tenv: XMTP_ENV as XmtpEnv,\n\t\t\t\tdbPath: agentDbPath\n\t\t\t})\n\n\t\t\txmtp.on(\"reaction\", async ({ conversation, message }) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst text = message.content.content\n\t\t\t\t\tconst messages: AgentMessage[] = [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tid: randomUUID(),\n\t\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\t\tparts: [{ type: \"text\", text }]\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\n\t\t\t\t\tconst baseRuntime: AgentRuntime = {\n\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\txmtpClient\n\t\t\t\t\t}\n\n\t\t\t\t\tconst runtime = await agent.createRuntimeContext(baseRuntime)\n\n\t\t\t\t\t// Execute pre-response behaviors\n\t\t\t\t\tif (context.behaviors) {\n\t\t\t\t\t\tconst behaviorContext: BehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait context.behaviors.executeBefore(behaviorContext)\n\t\t\t\t\t}\n\n\t\t\t\t\tconst { text: reply } = await agent.generate(messages, { runtime })\n\n\t\t\t\t\t// Execute post-response behaviors\n\t\t\t\t\tlet behaviorContext: BehaviorContext | undefined\n\t\t\t\t\tif (context.behaviors) {\n\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\t\tresponse: reply\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait context.behaviors.executeAfter(behaviorContext)\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Create minimal context for send options\n\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\t\tresponse: reply\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check if message was filtered out by filterMessages behavior\n\t\t\t\t\tif (behaviorContext?.sendOptions?.filtered) {\n\t\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t\t`๐Ÿ”‡ [XMTP Plugin] Skipping reaction response due to message being filtered`\n\t\t\t\t\t\t)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\tawait sendResponse(\n\t\t\t\t\t\tconversation as unknown as XmtpConversation,\n\t\t\t\t\t\treply,\n\t\t\t\t\t\tmessage.id,\n\t\t\t\t\t\tbehaviorContext\n\t\t\t\t\t)\n\t\t\t\t} catch (err) {\n\t\t\t\t\tlogger.error(\"โŒ Error handling reaction:\", err)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\txmtp.on(\"reply\", async ({ conversation, message }) => {\n\t\t\t\ttry {\n\t\t\t\t\t// TODO - why isn't this typed better?\n\t\t\t\t\tconst text = message.content.content as string\n\t\t\t\t\tconst messages: AgentMessage[] = [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tid: randomUUID(),\n\t\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\t\tparts: [{ type: \"text\", text }]\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\n\t\t\t\t\tconst baseRuntime: AgentRuntime = {\n\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\txmtpClient\n\t\t\t\t\t}\n\n\t\t\t\t\tconst runtime = await agent.createRuntimeContext(baseRuntime)\n\n\t\t\t\t\t// Execute pre-response behaviors\n\t\t\t\t\tlet behaviorContext: BehaviorContext | undefined\n\t\t\t\t\tif (context.behaviors) {\n\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait context.behaviors.executeBefore(behaviorContext)\n\n\t\t\t\t\t\t// Check if behaviors were stopped early (e.g., due to filtering)\n\t\t\t\t\t\tif (behaviorContext.stopped) {\n\t\t\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t\t\t`๐Ÿ”‡ [XMTP Plugin] Skipping reply response due to behavior chain being stopped`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst { text: reply } = await agent.generate(messages, { runtime })\n\n\t\t\t\t\t// Execute post-response behaviors\n\t\t\t\t\tif (context.behaviors) {\n\t\t\t\t\t\tif (!behaviorContext) {\n\t\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\t\t\tresponse: reply\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tbehaviorContext.response = reply\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait context.behaviors.executeAfter(behaviorContext)\n\n\t\t\t\t\t\t// Check if post behaviors were stopped early\n\t\t\t\t\t\tif (behaviorContext.stopped) {\n\t\t\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t\t\t`๐Ÿ”‡ [XMTP Plugin] Skipping reply response due to post-behavior chain being stopped`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Create minimal context for send options\n\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\t\tresponse: reply\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tawait sendResponse(\n\t\t\t\t\t\tconversation as unknown as XmtpConversation,\n\t\t\t\t\t\treply,\n\t\t\t\t\t\tmessage.id,\n\t\t\t\t\t\tbehaviorContext\n\t\t\t\t\t)\n\t\t\t\t} catch (err) {\n\t\t\t\t\tlogger.error(\"โŒ Error handling reply:\", err)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\txmtp.on(\"text\", async ({ conversation, message }) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst text = message.content\n\t\t\t\t\tconst messages: AgentMessage[] = [\n\t\t\t\t\t\t{ id: randomUUID(), role: \"user\", parts: [{ type: \"text\", text }] }\n\t\t\t\t\t]\n\n\t\t\t\t\tconst baseRuntime: AgentRuntime = {\n\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\txmtpClient\n\t\t\t\t\t}\n\n\t\t\t\t\tconst runtime = await agent.createRuntimeContext(baseRuntime)\n\n\t\t\t\t\t// Execute pre-response behaviors\n\t\t\t\t\tlet behaviorContext: BehaviorContext | undefined\n\t\t\t\t\tif (context.behaviors) {\n\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait context.behaviors.executeBefore(behaviorContext)\n\n\t\t\t\t\t\t// Check if behaviors were stopped early (e.g., due to filtering)\n\t\t\t\t\t\tif (behaviorContext.stopped) {\n\t\t\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t\t\t`๐Ÿ”‡ [XMTP Plugin] Skipping text response due to behavior chain being stopped`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst { text: reply } = await agent.generate(messages, { runtime })\n\n\t\t\t\t\t// Execute post-response behaviors\n\t\t\t\t\tif (context.behaviors) {\n\t\t\t\t\t\tif (!behaviorContext) {\n\t\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\t\t\tresponse: reply\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tbehaviorContext.response = reply\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait context.behaviors.executeAfter(behaviorContext)\n\n\t\t\t\t\t\t// Check if post behaviors were stopped early\n\t\t\t\t\t\tif (behaviorContext.stopped) {\n\t\t\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t\t\t`๐Ÿ”‡ [XMTP Plugin] Skipping text response due to post-behavior chain being stopped`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Create minimal context for send options\n\t\t\t\t\t\tbehaviorContext = {\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tclient: xmtpClient as unknown as XmtpClient,\n\t\t\t\t\t\t\tconversation: conversation as unknown as XmtpConversation,\n\t\t\t\t\t\t\tmessage: message as unknown as XmtpMessage,\n\t\t\t\t\t\t\tresponse: reply\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tawait sendResponse(\n\t\t\t\t\t\tconversation as unknown as XmtpConversation,\n\t\t\t\t\t\treply,\n\t\t\t\t\t\tmessage.id,\n\t\t\t\t\t\tbehaviorContext\n\t\t\t\t\t)\n\t\t\t\t} catch (err) {\n\t\t\t\t\tlogger.error(\"โŒ Error handling text:\", err)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\t// Event handlers removed due to incompatibility with current XMTP agent SDK\n\n\t\t\tvoid xmtp\n\t\t\t\t.start()\n\t\t\t\t.then(() => logger.debug(\"โœ… XMTP agent listener started\"))\n\t\t\t\t.catch((err) =>\n\t\t\t\t\tconsole.error(\"โŒ XMTP agent listener failed to start:\", err)\n\t\t\t\t)\n\t\t}\n\t}\n}\n","import { Context } from \"hono\"\nimport jwt from \"jsonwebtoken\"\nimport { logger } from \"@hybrd/utils\"\n\nexport interface XMTPToolsPayload {\n\taction: \"send\" | \"reply\" | \"react\" | \"transaction\" | \"blockchain-event\"\n\tconversationId: string\n\t// Action-specific data\n\tcontent?: string\n\treferenceMessageId?: string\n\temoji?: string\n\tactionType?: \"added\" | \"removed\"\n\tfromAddress?: string\n\tchainId?: string\n\tcalls?: Array<{\n\t\tto: string\n\t\tdata: string\n\t\tmetadata?: {\n\t\t\tdescription: string\n\t\t\ttransactionType: string\n\t\t}\n\t}>\n\t// Metadata\n\tissued: number\n\texpires: number\n}\n\n/**\n * Validates token and returns payload for both GET and POST endpoints\n *\n * @param {Context} c - Hono context object containing request information\n * @returns {XMTPToolsPayload | null} The validated payload or null if invalid\n *\n * @description\n * Supports two authentication methods:\n * - Authorization header with Bearer token (for POST endpoints)\n * - Query parameter token (for GET endpoints)\n *\n * @example\n * ```typescript\n * app.post(\"/api/endpoint\", async (c) => {\n * const payload = getValidatedPayload(c);\n * if (!payload) {\n * return c.json({ error: \"Invalid token\" }, 401);\n * }\n * // Use payload data\n * });\n * ```\n */\nexport function getValidatedPayload(c: Context): XMTPToolsPayload | null {\n\t// Try Authorization header first (for POST endpoints)\n\tconst authHeader = c.req.header(\"Authorization\")\n\tif (authHeader?.startsWith(\"Bearer \")) {\n\t\tconst token = authHeader.substring(7) // Remove \"Bearer \" prefix\n\t\treturn validateXMTPToolsToken(token)\n\t}\n\n\t// Fall back to query parameter (for GET endpoints)\n\tconst token = c.req.query(\"token\")\n\tif (!token) {\n\t\treturn null\n\t}\n\n\treturn validateXMTPToolsToken(token)\n}\n\n/**\n * Gets the JWT secret for token signing, with lazy initialization\n * Uses XMTP_DB_ENCRYPTION_KEY environment variable for consistency\n * Only falls back to development secret in development/test environments\n */\nfunction getJwtSecret(): string {\n\tconst secret = process.env.XMTP_DB_ENCRYPTION_KEY\n\tconst nodeEnv = process.env.NODE_ENV || \"development\"\n\n\t// In production, require a real JWT secret\n\tif (nodeEnv === \"production\" && !secret) {\n\t\tthrow new Error(\n\t\t\t\"XMTP_DB_ENCRYPTION_KEY environment variable is required in production. \" +\n\t\t\t\t\"Generate a secure random secret for JWT token signing.\"\n\t\t)\n\t}\n\n\t// In development/test, allow fallback but warn only when actually used\n\tif (!secret) {\n\t\tlogger.warn(\n\t\t\t\"โš ๏ธ [SECURITY] Using fallback JWT secret for development. \" +\n\t\t\t\t\"Set XMTP_DB_ENCRYPTION_KEY environment variable for production.\"\n\t\t)\n\t\treturn \"fallback-secret-for-dev-only\"\n\t}\n\n\treturn secret\n}\n\n/**\n * Gets the API key for authentication, with lazy initialization\n * Requires XMTP_API_KEY environment variable in production\n * Only falls back to development key in development/test environments\n */\nfunction getApiKey(): string {\n\tconst apiKey = process.env.XMTP_API_KEY\n\tconst nodeEnv = process.env.NODE_ENV || \"development\"\n\n\t// In production, require a real API key\n\tif (nodeEnv === \"production\" && !apiKey) {\n\t\tthrow new Error(\n\t\t\t\"XMTP_API_KEY environment variable is required in production. \" +\n\t\t\t\t\"Generate a secure random API key for authentication.\"\n\t\t)\n\t}\n\n\t// In development/test, allow fallback but warn only when actually used\n\tif (!apiKey) {\n\t\tlogger.warn(\n\t\t\t\"โš ๏ธ [SECURITY] Using fallback API key for development. \" +\n\t\t\t\t\"Set XMTP_API_KEY environment variable for production.\"\n\t\t)\n\t\treturn \"fallback-api-key-for-dev-only\"\n\t}\n\n\treturn apiKey\n}\n\n/**\n * JWT token expiry time in seconds (5 minutes)\n */\nconst JWT_EXPIRY = 5 * 60 // 5 minutes in seconds\n\n/**\n * Generates a signed JWT token for XMTP tools authentication\n *\n * @param {Omit<XMTPToolsPayload, \"issued\" | \"expires\">} payload - Token payload without timestamp fields\n * @returns {string} Signed JWT token\n *\n * @description\n * Creates a JWT token with automatic timestamp fields:\n * - issued: Current timestamp\n * - expires: Current timestamp + JWT_EXPIRY\n *\n * @example\n * ```typescript\n * const token = generateXMTPToolsToken({\n * action: \"send\",\n * conversationId: \"0x123...\"\n * });\n * ```\n */\nexport function generateXMTPToolsToken(\n\tpayload: Omit<XMTPToolsPayload, \"issued\" | \"expires\">\n): string {\n\tconst startTime = performance.now()\n\tlogger.debug(\"๐Ÿ” [JWT] Starting token generation...\")\n\n\tconst now = Math.floor(Date.now() / 1000)\n\tconst fullPayload: XMTPToolsPayload = {\n\t\t...payload,\n\t\tissued: now,\n\t\texpires: now + JWT_EXPIRY\n\t}\n\n\tconst token = jwt.sign(fullPayload, getJwtSecret(), {\n\t\texpiresIn: JWT_EXPIRY\n\t})\n\n\tconst endTime = performance.now()\n\tlogger.debug(\n\t\t`๐Ÿ” [JWT] Token generation completed in ${(endTime - startTime).toFixed(2)}ms`\n\t)\n\n\treturn token\n}\n\n/**\n * Validates an XMTP tools token using either API key or JWT verification\n *\n * @param {string} token - Token to validate (either API key or JWT)\n * @returns {XMTPToolsPayload | null} Validated payload or null if invalid\n *\n * @description\n * Supports two authentication methods in order of precedence:\n * 1. API key authentication - Direct comparison with XMTP_API_KEY\n * 2. JWT token authentication - Signature verification and expiry check\n *\n * For API key authentication, returns a default payload with 1-hour expiry.\n * For JWT authentication, validates signature and checks expiry timestamp.\n *\n * @example\n * ```typescript\n * const payload = validateXMTPToolsToken(userToken);\n * if (payload) {\n * console.log(`Action: ${payload.action}`);\n * console.log(`Conversation: ${payload.conversationId}`);\n * }\n * ```\n */\nexport function validateXMTPToolsToken(token: string): XMTPToolsPayload | null {\n\tconst startTime = performance.now()\n\tlogger.debug(\"๐Ÿ” [JWT] Starting token validation...\")\n\n\t// First try API key authentication\n\tif (token === getApiKey()) {\n\t\tlogger.debug(\"๐Ÿ”‘ [Auth] Using API key authentication\")\n\t\t// Return a valid payload for API key auth\n\t\tconst now = Math.floor(Date.now() / 1000)\n\t\tconst result = {\n\t\t\taction: \"send\" as const, // Default action\n\t\t\tconversationId: \"\", // Will be filled by endpoint\n\t\t\tissued: now,\n\t\t\texpires: now + 3600 // API keys are valid for 1 hour\n\t\t}\n\n\t\tconst endTime = performance.now()\n\t\tlogger.debug(\n\t\t\t`๐Ÿ” [JWT] API key validation completed in ${(endTime - startTime).toFixed(2)}ms`\n\t\t)\n\t\treturn result\n\t}\n\n\t// Then try JWT token authentication\n\ttry {\n\t\tconst decoded = jwt.verify(token, getJwtSecret()) as XMTPToolsPayload\n\t\tlogger.debug(\"๐Ÿ”‘ [Auth] Using JWT token authentication\")\n\n\t\t// Additional expiry check\n\t\tconst now = Math.floor(Date.now() / 1000)\n\t\tif (decoded.expires < now) {\n\t\t\tconsole.log(\"๐Ÿ”’ XMTP tools token has expired\")\n\t\t\tconst endTime = performance.now()\n\t\t\tlogger.debug(\n\t\t\t\t`๐Ÿ” [JWT] Token validation failed (expired) in ${(endTime - startTime).toFixed(2)}ms`\n\t\t\t)\n\t\t\treturn null\n\t\t}\n\n\t\tconst endTime = performance.now()\n\t\tlogger.debug(\n\t\t\t`๐Ÿ” [JWT] JWT validation completed in ${(endTime - startTime).toFixed(2)}ms`\n\t\t)\n\t\treturn decoded\n\t} catch (error) {\n\t\tlogger.error(\n\t\t\t\"๐Ÿ”’ Invalid XMTP tools token and not matching API key:\",\n\t\t\terror\n\t\t)\n\t\tconst endTime = performance.now()\n\t\tlogger.debug(\n\t\t\t`๐Ÿ” [JWT] Token validation failed in ${(endTime - startTime).toFixed(2)}ms`\n\t\t)\n\t\treturn null\n\t}\n}\n"],"mappings":"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","names":["createSigner","createUser","Client","Client","identifier","address","createSigner","createUser","logger","logger","createUser","createSigner","behaviorContext","logger","logger","logger","Client","IdentifierKind","ReplyCodec"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hybrd/xmtp",
3
- "version": "1.4.3",
3
+ "version": "1.4.4",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -29,8 +29,8 @@
29
29
  "jsonwebtoken": "^9.0.2",
30
30
  "uint8arrays": "^5.1.0",
31
31
  "viem": "^2.22.17",
32
- "@hybrd/utils": "1.4.3",
33
- "@hybrd/types": "1.4.3"
32
+ "@hybrd/types": "1.4.4",
33
+ "@hybrd/utils": "1.4.4"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/jsonwebtoken": "^9.0.7",
package/src/plugin.ts CHANGED
@@ -100,119 +100,11 @@ export function XMTPPlugin(): Plugin<PluginContext> {
100
100
  XMTP_WALLET_KEY as `0x${string}`
101
101
  )
102
102
 
103
- // Start a reliable node client stream to process incoming messages
104
- async function startNodeStream() {
105
- try {
106
- logger.debug("๐ŸŽง XMTP node client stream initializing")
107
- const stream = await xmtpClient.conversations.streamAllMessages()
108
- logger.debug("๐ŸŽง XMTP node client stream started")
109
- for await (const msg of stream) {
110
- try {
111
- if (msg.senderInboxId === xmtpClient.inboxId) continue
112
-
113
- const content =
114
- typeof msg.content === "string"
115
- ? msg.content
116
- : (() => {
117
- try {
118
- return JSON.stringify(msg.content)
119
- } catch {
120
- return String(msg.content)
121
- }
122
- })()
123
-
124
- const conversation =
125
- await xmtpClient.conversations.getConversationById(
126
- msg.conversationId
127
- )
128
-
129
- if (!conversation) {
130
- logger.warn(
131
- `โš ๏ธ XMTP conversation not found: ${msg.conversationId}`
132
- )
133
- continue
134
- }
135
-
136
- const messages: AgentMessage[] = [
137
- {
138
- id: randomUUID(),
139
- role: "user",
140
- parts: [{ type: "text", text: content }]
141
- }
142
- ]
143
-
144
- const baseRuntime: AgentRuntime = {
145
- conversation: conversation as unknown as XmtpConversation,
146
- message: msg,
147
- xmtpClient
148
- }
149
-
150
- const runtime = await agent.createRuntimeContext(baseRuntime)
151
-
152
- // Execute pre-response behaviors
153
- if (pluginContext.behaviors) {
154
- const behaviorContext: BehaviorContext = {
155
- runtime,
156
- client: xmtpClient as unknown as XmtpClient,
157
- conversation: conversation as unknown as XmtpConversation,
158
- message: msg as XmtpMessage
159
- }
160
- await pluginContext.behaviors.executeBefore(behaviorContext)
161
-
162
- // Check if message was filtered out by any behavior
163
- if (behaviorContext.sendOptions?.filtered) {
164
- continue // Skip processing this message
165
- }
166
- }
167
-
168
- const { text } = await agent.generate(messages, { runtime })
169
-
170
- // Create behavior context for send options
171
- const behaviorContext: BehaviorContext = {
172
- runtime,
173
- client: xmtpClient as unknown as XmtpClient,
174
- conversation: conversation as unknown as XmtpConversation,
175
- message: msg as XmtpMessage,
176
- response: text
177
- }
178
-
179
- // Execute post-response behaviors
180
- if (pluginContext.behaviors) {
181
- await pluginContext.behaviors.executeAfter(behaviorContext)
182
- }
183
-
184
- // Check if message was filtered out by filterMessages behavior
185
- if (behaviorContext?.sendOptions?.filtered) {
186
- logger.debug(
187
- `๐Ÿ”‡ [XMTP Plugin] Skipping response due to message being filtered`
188
- )
189
- return
190
- }
191
-
192
- // Send the response with threading support
193
- await sendResponse(conversation, text, msg.id, behaviorContext)
194
- } catch (err) {
195
- logger.error("โŒ Error processing XMTP message:", err)
196
- }
197
- }
198
- } catch (err) {
199
- logger.error("โŒ XMTP node client stream failed:", err)
200
- }
201
- }
202
-
203
- const enabledFromEnv = process.env.XMTP_ENABLE_NODE_STREAM
204
- const isNodeStreamEnabled =
205
- enabledFromEnv === undefined
206
- ? true
207
- : !/^(false|0|off|no)$/i.test(String(enabledFromEnv))
208
-
209
- if (isNodeStreamEnabled) void startNodeStream()
210
-
211
103
  const address = user.account.address.toLowerCase()
212
104
  const agentDbPath = await getDbPath(
213
105
  `agent-${XMTP_ENV || "dev"}-${address}`
214
106
  )
215
- logger.debug(`๐Ÿ“ Using agent listener database path: ${agentDbPath}`)
107
+ logger.debug(`๐Ÿ“ Using database path: ${agentDbPath}`)
216
108
 
217
109
  const xmtp = await XmtpAgent.create(signer, {
218
110
  env: XMTP_ENV as XmtpEnv,