@hybrd/xmtp 1.4.4 → 2.0.0

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
@@ -52,10 +52,13 @@ __export(src_exports, {
52
52
  createUser: () => import_agent_sdk2.createUser,
53
53
  createXMTPClient: () => createXMTPClient,
54
54
  createXMTPSigner: () => createSigner,
55
+ deriveAgentSecret: () => deriveAgentSecret,
55
56
  filter: () => import_agent_sdk2.filter,
56
57
  generateXMTPToolsToken: () => generateXMTPToolsToken,
58
+ getDbPath: () => getDbPath,
57
59
  getTestUrl: () => import_agent_sdk2.getTestUrl,
58
60
  logAgentDetails: () => logAgentDetails,
61
+ resolveAgentSecret: () => resolveAgentSecret,
59
62
  validateEnvironment: () => validateEnvironment
60
63
  });
61
64
  module.exports = __toCommonJS(src_exports);
@@ -67,19 +70,18 @@ var DEFAULT_AMOUNT = "0.1";
67
70
  var MAX_USDC_AMOUNT = 10;
68
71
 
69
72
  // src/client.ts
73
+ var import_node_crypto = require("crypto");
74
+ var import_node_fs = __toESM(require("fs"), 1);
75
+ var import_node_path = __toESM(require("path"), 1);
70
76
  var import_utils = require("@hybrd/utils");
71
77
  var import_content_type_reaction = require("@xmtp/content-type-reaction");
72
78
  var import_content_type_reply = require("@xmtp/content-type-reply");
73
79
  var import_content_type_transaction_reference = require("@xmtp/content-type-transaction-reference");
74
80
  var import_content_type_wallet_send_calls = require("@xmtp/content-type-wallet-send-calls");
75
81
  var import_node_sdk2 = require("@xmtp/node-sdk");
76
- var import_node_crypto = require("crypto");
77
- var import_node_fs = __toESM(require("fs"), 1);
78
- var import_node_path = __toESM(require("path"), 1);
79
- var import_node_url = require("url");
80
82
  var import_uint8arrays = require("uint8arrays");
81
- var import_viem = require("viem");
82
- var import_accounts = require("viem/accounts");
83
+ var import_viem2 = require("viem");
84
+ var import_accounts2 = require("viem/accounts");
83
85
  var import_chains = require("viem/chains");
84
86
 
85
87
  // scripts/revoke-installations.ts
@@ -126,10 +128,10 @@ async function revokeOldInstallations(signer, inboxId) {
126
128
  }
127
129
  }
128
130
  async function main() {
129
- const { XMTP_WALLET_KEY } = process.env;
131
+ const { AGENT_WALLET_KEY } = process.env;
130
132
  const inboxId = process.argv[2];
131
- if (!XMTP_WALLET_KEY) {
132
- console.error("\u274C XMTP_WALLET_KEY is required");
133
+ if (!AGENT_WALLET_KEY) {
134
+ console.error("\u274C AGENT_WALLET_KEY is required");
133
135
  process.exit(1);
134
136
  }
135
137
  if (!inboxId) {
@@ -137,7 +139,7 @@ async function main() {
137
139
  console.error("Usage: tsx revoke-installations.ts <inboxId>");
138
140
  process.exit(1);
139
141
  }
140
- const signer = createSigner(XMTP_WALLET_KEY);
142
+ const signer = createSigner(AGENT_WALLET_KEY);
141
143
  const identifier = await signer.getIdentifier();
142
144
  const address = identifier.identifier;
143
145
  console.log(`\u{1F511} Wallet Address: ${address}`);
@@ -150,26 +152,45 @@ async function main() {
150
152
  process.exit(1);
151
153
  }
152
154
  }
153
- if (import_meta.url === `file://${process.argv[1]}`) {
154
- main().catch((error) => {
155
- console.error("\u{1F4A5} Fatal error:", error);
156
- process.exit(1);
157
- });
155
+ try {
156
+ if (import_meta.url === `file://${process.argv[1]}`) {
157
+ main().catch((error) => {
158
+ console.error("\u{1F4A5} Fatal error:", error);
159
+ process.exit(1);
160
+ });
161
+ }
162
+ } catch {
163
+ }
164
+
165
+ // src/lib/secret.ts
166
+ var import_viem = require("viem");
167
+ var import_accounts = require("viem/accounts");
168
+ function deriveAgentSecret(walletKey) {
169
+ const keyBytes = (0, import_viem.toBytes)(walletKey);
170
+ const hdKey = import_accounts.HDKey.fromMasterSeed(keyBytes);
171
+ const child = hdKey.derive("m/44'/60'/0'/0/41");
172
+ if (!child.privateKey) {
173
+ throw new Error("Failed to derive child key from wallet key");
174
+ }
175
+ return Buffer.from(child.privateKey).toString("hex");
176
+ }
177
+ function resolveAgentSecret(walletKey) {
178
+ if (!walletKey) {
179
+ throw new Error("walletKey is required to derive the encryption secret");
180
+ }
181
+ return deriveAgentSecret(walletKey);
158
182
  }
159
183
 
160
184
  // src/client.ts
161
- var import_meta2 = {};
162
- var __filename = (0, import_node_url.fileURLToPath)(import_meta2.url);
163
- var __dirname = import_node_path.default.dirname(__filename);
164
185
  var createUser = (key) => {
165
- const account = (0, import_accounts.privateKeyToAccount)(key);
186
+ const account = (0, import_accounts2.privateKeyToAccount)(key);
166
187
  return {
167
188
  key,
168
189
  account,
169
- wallet: (0, import_viem.createWalletClient)({
190
+ wallet: (0, import_viem2.createWalletClient)({
170
191
  account,
171
192
  chain: import_chains.sepolia,
172
- transport: (0, import_viem.http)()
193
+ transport: (0, import_viem2.http)()
173
194
  })
174
195
  };
175
196
  };
@@ -191,7 +212,7 @@ var createSigner = (key) => {
191
212
  message,
192
213
  account: user.account
193
214
  });
194
- return (0, import_viem.toBytes)(signature);
215
+ return (0, import_viem2.toBytes)(signature);
195
216
  }
196
217
  };
197
218
  };
@@ -202,8 +223,7 @@ async function clearXMTPDatabase(address, env) {
202
223
  if (customStoragePath) {
203
224
  return import_node_path.default.isAbsolute(customStoragePath) ? customStoragePath : import_node_path.default.resolve(process.cwd(), customStoragePath);
204
225
  }
205
- const projectRoot = process.env.PROJECT_ROOT || import_node_path.default.resolve(__dirname, "../../..");
206
- return import_node_path.default.join(projectRoot, ".data/xmtp");
226
+ return import_node_path.default.join(process.cwd(), ".hybrid", ".xmtp");
207
227
  };
208
228
  const dbPattern = `${env}-${address}.db3`;
209
229
  const storageDir = getStorageDirectory();
@@ -212,6 +232,8 @@ async function clearXMTPDatabase(address, env) {
212
232
  // Legacy fallback paths for backward compatibility
213
233
  import_node_path.default.join(process.cwd(), ".data", "xmtp"),
214
234
  import_node_path.default.join(process.cwd(), "..", ".data", "xmtp"),
235
+ import_node_path.default.join(process.cwd(), "..", "..", ".data", "xmtp"),
236
+ // Monorepo root fallback
215
237
  import_node_path.default.join(process.cwd(), "..", "..", ".data", "xmtp")
216
238
  ];
217
239
  for (const dir of possiblePaths) {
@@ -241,10 +263,11 @@ async function createXMTPClient(privateKey, opts) {
241
263
  const signer = createSigner(privateKey);
242
264
  if (!signer) {
243
265
  throw new Error(
244
- "No signer provided and XMTP_WALLET_KEY environment variable is not set"
266
+ "No signer provided and AGENT_WALLET_KEY environment variable is not set"
245
267
  );
246
268
  }
247
- const { XMTP_DB_ENCRYPTION_KEY, XMTP_ENV } = process.env;
269
+ const { XMTP_ENV } = process.env;
270
+ const agentSecret = resolveAgentSecret(privateKey);
248
271
  const identifier = await signer.getIdentifier();
249
272
  const address = identifier.identifier;
250
273
  while (attempt < maxRetries) {
@@ -257,12 +280,7 @@ async function createXMTPClient(privateKey, opts) {
257
280
  "Stateless mode is not supported. XMTP client must run in persistent mode to properly receive and process messages. Set persist: true or remove the persist option to use the default persistent mode."
258
281
  );
259
282
  }
260
- if (!XMTP_DB_ENCRYPTION_KEY) {
261
- throw new Error(
262
- "XMTP_DB_ENCRYPTION_KEY must be set for persistent mode"
263
- );
264
- }
265
- const dbEncryptionKey = getEncryptionKeyFromHex(XMTP_DB_ENCRYPTION_KEY);
283
+ const dbEncryptionKey = getEncryptionKeyFromHex(agentSecret);
266
284
  const dbPath = await getDbPath(
267
285
  `${XMTP_ENV || "dev"}-${address}`,
268
286
  storagePath
@@ -291,7 +309,7 @@ async function createXMTPClient(privateKey, opts) {
291
309
  return client;
292
310
  } catch (error) {
293
311
  attempt++;
294
- if (error instanceof Error && error.message.includes("5/5 installations")) {
312
+ if (error instanceof Error && (error.message.includes("installations") || error.message.match(/\d+\/\d+\s+installations/))) {
295
313
  console.log(
296
314
  `\u{1F4A5} Installation limit reached (attempt ${attempt}/${maxRetries})`
297
315
  );
@@ -334,7 +352,7 @@ async function createXMTPClient(privateKey, opts) {
334
352
  console.log("\u{1F527} Attempting automatic identity refresh...");
335
353
  try {
336
354
  console.log("\u{1F4DD} Creating persistent client to refresh identity...");
337
- const tempEncryptionKey = XMTP_DB_ENCRYPTION_KEY ? getEncryptionKeyFromHex(XMTP_DB_ENCRYPTION_KEY) : getEncryptionKeyFromHex(generateEncryptionKeyHex());
355
+ const tempEncryptionKey = getEncryptionKeyFromHex(agentSecret);
338
356
  const tempClient = await import_node_sdk2.Client.create(signer, {
339
357
  dbEncryptionKey: tempEncryptionKey,
340
358
  env: XMTP_ENV,
@@ -376,10 +394,6 @@ async function createXMTPClient(privateKey, opts) {
376
394
  }
377
395
  throw new Error("Max retries exceeded");
378
396
  }
379
- var generateEncryptionKeyHex = () => {
380
- const uint8Array = (0, import_node_crypto.getRandomValues)(new Uint8Array(32));
381
- return (0, import_uint8arrays.toString)(uint8Array, "hex");
382
- };
383
397
  var getEncryptionKeyFromHex = (hex) => {
384
398
  return (0, import_uint8arrays.fromString)(hex, "hex");
385
399
  };
@@ -391,8 +405,7 @@ var getDbPath = async (description = "xmtp", storagePath) => {
391
405
  } else if (storagePath) {
392
406
  volumePath = import_node_path.default.isAbsolute(storagePath) ? storagePath : import_node_path.default.resolve(process.cwd(), storagePath);
393
407
  } else {
394
- const projectRoot = process.env.PROJECT_ROOT || import_node_path.default.resolve(__dirname, "../../..");
395
- volumePath = import_node_path.default.join(projectRoot, ".data/xmtp");
408
+ volumePath = import_node_path.default.join(process.cwd(), ".hybrid", ".xmtp");
396
409
  }
397
410
  const dbPath = `${volumePath}/${description}.db3`;
398
411
  if (typeof globalThis !== "undefined" && "XMTP_STORAGE" in globalThis) {
@@ -633,8 +646,8 @@ var XMTPConnectionManager = class {
633
646
 
634
647
  // src/plugin.ts
635
648
  var import_agent_sdk = require("@xmtp/agent-sdk");
636
- var import_utils2 = require("@hybrd/utils");
637
649
  var import_node_crypto2 = require("crypto");
650
+ var import_utils2 = require("@hybrd/utils");
638
651
  async function sendResponse(conversation, text, originalMessageId, behaviorContext) {
639
652
  const shouldThread = behaviorContext?.sendOptions?.threaded ?? false;
640
653
  if (shouldThread) {
@@ -665,23 +678,16 @@ function XMTPPlugin() {
665
678
  name: "xmtp",
666
679
  description: "Provides XMTP messaging functionality",
667
680
  apply: async (app, context) => {
668
- const {
669
- XMTP_WALLET_KEY,
670
- XMTP_DB_ENCRYPTION_KEY,
671
- XMTP_ENV = "production"
672
- } = process.env;
681
+ const { AGENT_WALLET_KEY, XMTP_ENV = "production" } = process.env;
673
682
  const { agent } = context;
674
683
  const pluginContext = context;
675
- if (!XMTP_WALLET_KEY) {
676
- throw new Error("XMTP_WALLET_KEY must be set");
677
- }
678
- if (!XMTP_DB_ENCRYPTION_KEY) {
679
- throw new Error("XMTP_DB_ENCRYPTION_KEY must be set");
684
+ if (!AGENT_WALLET_KEY) {
685
+ throw new Error("AGENT_WALLET_KEY must be set");
680
686
  }
681
- const user = (0, import_agent_sdk.createUser)(XMTP_WALLET_KEY);
687
+ const user = (0, import_agent_sdk.createUser)(AGENT_WALLET_KEY);
682
688
  const signer = (0, import_agent_sdk.createSigner)(user);
683
689
  const xmtpClient = await createXMTPClient(
684
- XMTP_WALLET_KEY
690
+ AGENT_WALLET_KEY
685
691
  );
686
692
  const address = user.account.address.toLowerCase();
687
693
  const agentDbPath = await getDbPath(
@@ -692,6 +698,8 @@ function XMTPPlugin() {
692
698
  env: XMTP_ENV,
693
699
  dbPath: agentDbPath
694
700
  });
701
+ const botInboxId = xmtp.client?.inboxId;
702
+ const MAX_HISTORY = 20;
695
703
  xmtp.on("reaction", async ({ conversation, message }) => {
696
704
  try {
697
705
  const text = message.content.content;
@@ -705,29 +713,52 @@ function XMTPPlugin() {
705
713
  const baseRuntime = {
706
714
  conversation,
707
715
  message,
708
- xmtpClient
716
+ xmtpClient,
717
+ ...context.scheduler ? { scheduler: context.scheduler } : {}
709
718
  };
710
719
  const runtime = await agent.createRuntimeContext(baseRuntime);
720
+ let behaviorContext;
711
721
  if (context.behaviors) {
712
- const behaviorContext2 = {
722
+ behaviorContext = {
713
723
  runtime,
714
724
  client: xmtpClient,
715
725
  conversation,
716
726
  message
717
727
  };
718
- await context.behaviors.executeBefore(behaviorContext2);
728
+ await context.behaviors.executeBefore(behaviorContext);
729
+ if (behaviorContext.stopped) {
730
+ import_utils2.logger.debug(
731
+ `\u{1F507} [XMTP Plugin] Skipping reaction response due to behavior chain being stopped`
732
+ );
733
+ return;
734
+ }
735
+ if (behaviorContext.sendOptions?.filtered) {
736
+ import_utils2.logger.debug(
737
+ `\u{1F507} [XMTP Plugin] Skipping reaction response due to message being filtered`
738
+ );
739
+ return;
740
+ }
719
741
  }
720
742
  const { text: reply } = await agent.generate(messages, { runtime });
721
- let behaviorContext;
722
743
  if (context.behaviors) {
723
- behaviorContext = {
724
- runtime,
725
- client: xmtpClient,
726
- conversation,
727
- message,
728
- response: reply
729
- };
744
+ if (behaviorContext) {
745
+ behaviorContext.response = reply;
746
+ } else {
747
+ behaviorContext = {
748
+ runtime,
749
+ client: xmtpClient,
750
+ conversation,
751
+ message,
752
+ response: reply
753
+ };
754
+ }
730
755
  await context.behaviors.executeAfter(behaviorContext);
756
+ if (behaviorContext.stopped) {
757
+ import_utils2.logger.debug(
758
+ `\u{1F507} [XMTP Plugin] Skipping reaction response due to post-behavior chain being stopped`
759
+ );
760
+ return;
761
+ }
731
762
  } else {
732
763
  behaviorContext = {
733
764
  runtime,
@@ -737,12 +768,6 @@ function XMTPPlugin() {
737
768
  response: reply
738
769
  };
739
770
  }
740
- if (behaviorContext?.sendOptions?.filtered) {
741
- import_utils2.logger.debug(
742
- `\u{1F507} [XMTP Plugin] Skipping reaction response due to message being filtered`
743
- );
744
- return;
745
- }
746
771
  await sendResponse(
747
772
  conversation,
748
773
  reply,
@@ -766,7 +791,8 @@ function XMTPPlugin() {
766
791
  const baseRuntime = {
767
792
  conversation,
768
793
  message,
769
- xmtpClient
794
+ xmtpClient,
795
+ ...context.scheduler ? { scheduler: context.scheduler } : {}
770
796
  };
771
797
  const runtime = await agent.createRuntimeContext(baseRuntime);
772
798
  let behaviorContext;
@@ -827,13 +853,38 @@ function XMTPPlugin() {
827
853
  xmtp.on("text", async ({ conversation, message }) => {
828
854
  try {
829
855
  const text = message.content;
856
+ let historyMessages = [];
857
+ try {
858
+ console.log("[xmtp] fetching conversation history...");
859
+ const history = await conversation.messages({
860
+ limit: MAX_HISTORY + 1,
861
+ direction: 1
862
+ });
863
+ console.log(
864
+ `[xmtp] got ${history.length} messages from conversation.messages()`
865
+ );
866
+ const filtered = history.filter((msg) => msg.id !== message.id).filter(
867
+ (msg) => msg.content && typeof msg.content === "string"
868
+ ).slice(0, MAX_HISTORY).reverse();
869
+ console.log(`[xmtp] after filter: ${filtered.length} messages`);
870
+ historyMessages = filtered.map((msg) => ({
871
+ id: msg.id,
872
+ role: msg.senderInboxId === botInboxId ? "assistant" : "user",
873
+ parts: [{ type: "text", text: msg.content }]
874
+ }));
875
+ } catch (historyErr) {
876
+ console.error(`[xmtp] history error:`, historyErr);
877
+ }
830
878
  const messages = [
879
+ ...historyMessages,
831
880
  { id: (0, import_node_crypto2.randomUUID)(), role: "user", parts: [{ type: "text", text }] }
832
881
  ];
882
+ console.log(`[xmtp] sending ${messages.length} messages to agent`);
833
883
  const baseRuntime = {
834
884
  conversation,
835
885
  message,
836
- xmtpClient
886
+ xmtpClient,
887
+ ...context.scheduler ? { scheduler: context.scheduler } : {}
837
888
  };
838
889
  const runtime = await agent.createRuntimeContext(baseRuntime);
839
890
  let behaviorContext;
@@ -891,6 +942,7 @@ function XMTPPlugin() {
891
942
  import_utils2.logger.error("\u274C Error handling text:", err);
892
943
  }
893
944
  });
945
+ context.xmtpClient = xmtpClient;
894
946
  void xmtp.start().then(() => import_utils2.logger.debug("\u2705 XMTP agent listener started")).catch(
895
947
  (err) => console.error("\u274C XMTP agent listener failed to start:", err)
896
948
  );
@@ -899,23 +951,23 @@ function XMTPPlugin() {
899
951
  }
900
952
 
901
953
  // src/lib/jwt.ts
902
- var import_jsonwebtoken = __toESM(require("jsonwebtoken"), 1);
903
954
  var import_utils3 = require("@hybrd/utils");
955
+ var import_jsonwebtoken = __toESM(require("jsonwebtoken"), 1);
904
956
  function getJwtSecret() {
905
- const secret = process.env.XMTP_DB_ENCRYPTION_KEY;
957
+ const walletKey = process.env.AGENT_WALLET_KEY;
958
+ if (walletKey) {
959
+ return resolveAgentSecret(walletKey);
960
+ }
906
961
  const nodeEnv = process.env.NODE_ENV || "development";
907
- if (nodeEnv === "production" && !secret) {
962
+ if (nodeEnv === "production") {
908
963
  throw new Error(
909
- "XMTP_DB_ENCRYPTION_KEY environment variable is required in production. Generate a secure random secret for JWT token signing."
910
- );
911
- }
912
- if (!secret) {
913
- import_utils3.logger.warn(
914
- "\u26A0\uFE0F [SECURITY] Using fallback JWT secret for development. Set XMTP_DB_ENCRYPTION_KEY environment variable for production."
964
+ "AGENT_WALLET_KEY must be set in production for JWT token signing."
915
965
  );
916
- return "fallback-secret-for-dev-only";
917
966
  }
918
- return secret;
967
+ import_utils3.logger.warn(
968
+ "\u26A0\uFE0F [SECURITY] Using fallback JWT secret for development. Set AGENT_WALLET_KEY for production."
969
+ );
970
+ return "fallback-secret-for-dev-only";
919
971
  }
920
972
  var JWT_EXPIRY = 5 * 60;
921
973
  function generateXMTPToolsToken(payload) {
@@ -967,10 +1019,13 @@ var import_content_type_wallet_send_calls2 = require("@xmtp/content-type-wallet-
967
1019
  createUser,
968
1020
  createXMTPClient,
969
1021
  createXMTPSigner,
1022
+ deriveAgentSecret,
970
1023
  filter,
971
1024
  generateXMTPToolsToken,
1025
+ getDbPath,
972
1026
  getTestUrl,
973
1027
  logAgentDetails,
1028
+ resolveAgentSecret,
974
1029
  validateEnvironment
975
1030
  });
976
1031
  //# sourceMappingURL=index.cjs.map