@hybrd/xmtp 1.4.5 → 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.
@@ -0,0 +1,33 @@
1
+ import { toBytes } from "viem"
2
+ import { HDKey } from "viem/accounts"
3
+
4
+ /**
5
+ * Derives a deterministic 32-byte secret from an agent wallet private key.
6
+ *
7
+ * Uses BIP-32 HD key derivation at path m/44'/60'/0'/0/41 (coin type 60 = ETH,
8
+ * index 41 is arbitrary but fixed — chosen to avoid collision with standard
9
+ * account derivation paths). The child private key is used directly as the
10
+ * 32-byte secret, returned as a 64-character hex string.
11
+ */
12
+ export function deriveAgentSecret(walletKey: string): string {
13
+ const keyBytes = toBytes(walletKey as `0x${string}`)
14
+ const hdKey = HDKey.fromMasterSeed(keyBytes)
15
+ const child = hdKey.derive("m/44'/60'/0'/0/41")
16
+ if (!child.privateKey) {
17
+ throw new Error("Failed to derive child key from wallet key")
18
+ }
19
+ return Buffer.from(child.privateKey).toString("hex")
20
+ }
21
+
22
+ /**
23
+ * Resolves the database encryption secret by deriving it from the provided wallet key.
24
+ *
25
+ * The wallet key must be passed explicitly — this function does not
26
+ * read from environment variables or external stores.
27
+ */
28
+ export function resolveAgentSecret(walletKey: string): string {
29
+ if (!walletKey) {
30
+ throw new Error("walletKey is required to derive the encryption secret")
31
+ }
32
+ return deriveAgentSecret(walletKey)
33
+ }
package/src/plugin.ts CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  createUser
6
6
  } from "@xmtp/agent-sdk"
7
7
 
8
+ import { randomUUID } from "node:crypto"
8
9
  import type {
9
10
  AgentMessage,
10
11
  AgentRuntime,
@@ -17,7 +18,6 @@ import type {
17
18
  XmtpMessage
18
19
  } from "@hybrd/types"
19
20
  import { logger } from "@hybrd/utils"
20
- import { randomUUID } from "node:crypto"
21
21
  import { createXMTPClient, getDbPath } from "./client"
22
22
  import { ContentTypeReply, ContentTypeText, type Reply } from "./index"
23
23
 
@@ -74,30 +74,22 @@ export function XMTPPlugin(): Plugin<PluginContext> {
74
74
  name: "xmtp",
75
75
  description: "Provides XMTP messaging functionality",
76
76
  apply: async (app, context): Promise<void> => {
77
- const {
78
- XMTP_WALLET_KEY,
79
- XMTP_DB_ENCRYPTION_KEY,
80
- XMTP_ENV = "production"
81
- } = process.env
77
+ const { AGENT_WALLET_KEY, XMTP_ENV = "production" } = process.env
82
78
 
83
79
  const { agent } = context
84
80
  const pluginContext = context as PluginContext & {
85
81
  behaviors?: BehaviorRegistry
86
82
  }
87
83
 
88
- if (!XMTP_WALLET_KEY) {
89
- throw new Error("XMTP_WALLET_KEY must be set")
90
- }
91
-
92
- if (!XMTP_DB_ENCRYPTION_KEY) {
93
- throw new Error("XMTP_DB_ENCRYPTION_KEY must be set")
84
+ if (!AGENT_WALLET_KEY) {
85
+ throw new Error("AGENT_WALLET_KEY must be set")
94
86
  }
95
87
 
96
- const user = createUser(XMTP_WALLET_KEY as `0x${string}`)
88
+ const user = createUser(AGENT_WALLET_KEY as `0x${string}`)
97
89
  const signer = createSigner(user)
98
90
 
99
91
  const xmtpClient = await createXMTPClient(
100
- XMTP_WALLET_KEY as `0x${string}`
92
+ AGENT_WALLET_KEY as `0x${string}`
101
93
  )
102
94
 
103
95
  const address = user.account.address.toLowerCase()
@@ -106,12 +98,15 @@ export function XMTPPlugin(): Plugin<PluginContext> {
106
98
  )
107
99
  logger.debug(`📁 Using database path: ${agentDbPath}`)
108
100
 
109
- const xmtp = await XmtpAgent.create(signer, {
101
+ const xmtp = (await XmtpAgent.create(signer, {
110
102
  env: XMTP_ENV as XmtpEnv,
111
103
  dbPath: agentDbPath
112
- })
104
+ })) as any
105
+
106
+ const botInboxId = xmtp.client?.inboxId
107
+ const MAX_HISTORY = 20
113
108
 
114
- xmtp.on("reaction", async ({ conversation, message }) => {
109
+ xmtp.on("reaction", async ({ conversation, message }: any) => {
115
110
  try {
116
111
  const text = message.content.content
117
112
  const messages: AgentMessage[] = [
@@ -125,35 +120,64 @@ export function XMTPPlugin(): Plugin<PluginContext> {
125
120
  const baseRuntime: AgentRuntime = {
126
121
  conversation: conversation as unknown as XmtpConversation,
127
122
  message: message as unknown as XmtpMessage,
128
- xmtpClient
123
+ xmtpClient,
124
+ ...(context.scheduler ? { scheduler: context.scheduler } : {})
129
125
  }
130
126
 
131
127
  const runtime = await agent.createRuntimeContext(baseRuntime)
132
128
 
133
129
  // Execute pre-response behaviors
130
+ let behaviorContext: BehaviorContext | undefined
134
131
  if (context.behaviors) {
135
- const behaviorContext: BehaviorContext = {
132
+ behaviorContext = {
136
133
  runtime,
137
134
  client: xmtpClient as unknown as XmtpClient,
138
135
  conversation: conversation as unknown as XmtpConversation,
139
136
  message: message as unknown as XmtpMessage
140
137
  }
141
138
  await context.behaviors.executeBefore(behaviorContext)
139
+
140
+ // Check if behaviors were stopped early (e.g., due to filtering)
141
+ if (behaviorContext.stopped) {
142
+ logger.debug(
143
+ `🔇 [XMTP Plugin] Skipping reaction response due to behavior chain being stopped`
144
+ )
145
+ return
146
+ }
147
+
148
+ // Check if message was filtered out by filterMessages behavior
149
+ if (behaviorContext.sendOptions?.filtered) {
150
+ logger.debug(
151
+ `🔇 [XMTP Plugin] Skipping reaction response due to message being filtered`
152
+ )
153
+ return
154
+ }
142
155
  }
143
156
 
144
157
  const { text: reply } = await agent.generate(messages, { runtime })
145
158
 
146
159
  // Execute post-response behaviors
147
- let behaviorContext: BehaviorContext | undefined
148
160
  if (context.behaviors) {
149
- behaviorContext = {
150
- runtime,
151
- client: xmtpClient as unknown as XmtpClient,
152
- conversation: conversation as unknown as XmtpConversation,
153
- message: message as unknown as XmtpMessage,
154
- response: reply
161
+ if (behaviorContext) {
162
+ behaviorContext.response = reply
163
+ } else {
164
+ behaviorContext = {
165
+ runtime,
166
+ client: xmtpClient as unknown as XmtpClient,
167
+ conversation: conversation as unknown as XmtpConversation,
168
+ message: message as unknown as XmtpMessage,
169
+ response: reply
170
+ }
155
171
  }
156
172
  await context.behaviors.executeAfter(behaviorContext)
173
+
174
+ // Check if post behaviors were stopped early
175
+ if (behaviorContext.stopped) {
176
+ logger.debug(
177
+ `🔇 [XMTP Plugin] Skipping reaction response due to post-behavior chain being stopped`
178
+ )
179
+ return
180
+ }
157
181
  } else {
158
182
  // Create minimal context for send options
159
183
  behaviorContext = {
@@ -165,14 +189,6 @@ export function XMTPPlugin(): Plugin<PluginContext> {
165
189
  }
166
190
  }
167
191
 
168
- // Check if message was filtered out by filterMessages behavior
169
- if (behaviorContext?.sendOptions?.filtered) {
170
- logger.debug(
171
- `🔇 [XMTP Plugin] Skipping reaction response due to message being filtered`
172
- )
173
- return
174
- }
175
-
176
192
  await sendResponse(
177
193
  conversation as unknown as XmtpConversation,
178
194
  reply,
@@ -184,7 +200,7 @@ export function XMTPPlugin(): Plugin<PluginContext> {
184
200
  }
185
201
  })
186
202
 
187
- xmtp.on("reply", async ({ conversation, message }) => {
203
+ xmtp.on("reply", async ({ conversation, message }: any) => {
188
204
  try {
189
205
  // TODO - why isn't this typed better?
190
206
  const text = message.content.content as string
@@ -199,7 +215,8 @@ export function XMTPPlugin(): Plugin<PluginContext> {
199
215
  const baseRuntime: AgentRuntime = {
200
216
  conversation: conversation as unknown as XmtpConversation,
201
217
  message: message as unknown as XmtpMessage,
202
- xmtpClient
218
+ xmtpClient,
219
+ ...(context.scheduler ? { scheduler: context.scheduler } : {})
203
220
  }
204
221
 
205
222
  const runtime = await agent.createRuntimeContext(baseRuntime)
@@ -270,17 +287,56 @@ export function XMTPPlugin(): Plugin<PluginContext> {
270
287
  }
271
288
  })
272
289
 
273
- xmtp.on("text", async ({ conversation, message }) => {
290
+ xmtp.on("text", async ({ conversation, message }: any) => {
274
291
  try {
275
292
  const text = message.content
293
+
294
+ let historyMessages: AgentMessage[] = []
295
+ try {
296
+ console.log("[xmtp] fetching conversation history...")
297
+ const history = await conversation.messages({
298
+ limit: MAX_HISTORY + 1,
299
+ direction: 1
300
+ })
301
+
302
+ console.log(
303
+ `[xmtp] got ${history.length} messages from conversation.messages()`
304
+ )
305
+
306
+ const filtered = history
307
+ .filter((msg: any) => msg.id !== message.id)
308
+ .filter(
309
+ (msg: any) => msg.content && typeof msg.content === "string"
310
+ )
311
+ .slice(0, MAX_HISTORY)
312
+ .reverse()
313
+
314
+ console.log(`[xmtp] after filter: ${filtered.length} messages`)
315
+
316
+ historyMessages = filtered.map((msg: any) => ({
317
+ id: msg.id,
318
+ role:
319
+ msg.senderInboxId === botInboxId
320
+ ? ("assistant" as const)
321
+ : ("user" as const),
322
+ parts: [{ type: "text" as const, text: msg.content as string }]
323
+ }))
324
+ } catch (historyErr) {
325
+ console.error(`[xmtp] history error:`, historyErr)
326
+ }
327
+
276
328
  const messages: AgentMessage[] = [
329
+ ...historyMessages,
277
330
  { id: randomUUID(), role: "user", parts: [{ type: "text", text }] }
278
331
  ]
279
332
 
333
+ console.log(`[xmtp] sending ${messages.length} messages to agent`)
334
+
280
335
  const baseRuntime: AgentRuntime = {
281
336
  conversation: conversation as unknown as XmtpConversation,
282
337
  message: message as unknown as XmtpMessage,
283
- xmtpClient
338
+ xmtpClient,
339
+ ...(context.scheduler ? { scheduler: context.scheduler } : {})
284
340
  }
285
341
 
286
342
  const runtime = await agent.createRuntimeContext(baseRuntime)
@@ -351,12 +407,15 @@ export function XMTPPlugin(): Plugin<PluginContext> {
351
407
  }
352
408
  })
353
409
 
410
+ // Store xmtpClient in context for scheduler and other components
411
+ ;(context as any).xmtpClient = xmtpClient
412
+
354
413
  // Event handlers removed due to incompatibility with current XMTP agent SDK
355
414
 
356
415
  void xmtp
357
416
  .start()
358
417
  .then(() => logger.debug("✅ XMTP agent listener started"))
359
- .catch((err) =>
418
+ .catch((err: any) =>
360
419
  console.error("❌ XMTP agent listener failed to start:", err)
361
420
  )
362
421
  }
package/src/index.ts.old DELETED
@@ -1,145 +0,0 @@
1
- // ===================================================================
2
- // XMTP Package - Main Entry Point
3
- // ===================================================================
4
- // This package provides a clean interface to XMTP functionality
5
- // Re-exports core XMTP SDK types and utilities
6
-
7
- // export * from "./client"
8
- // export * from "./constants"
9
- // export * from "./lib/message-listener"
10
- // export * from "./lib/subjects"
11
- // export * from "./resolver"
12
- // export * from "./resolver/basename-resolver"
13
- // export * from "./resolver/ens-resolver"
14
- // export * from "./resolver/xmtp-resolver"
15
- // export * from "./service-client"
16
- // export * from "./types"
17
-
18
- // ===================================================================
19
- // XMTP Plugin for Agent Integration
20
- // ===================================================================
21
- export { XMTPPlugin } from "./plugin"
22
- export type { Plugin, XMTPPluginContext } from "./plugin"
23
-
24
- // ===================================================================
25
- // JWT Utilities for XMTP Tools
26
- // ===================================================================
27
- export { generateXMTPToolsToken } from "./lib/jwt"
28
- export type { XMTPToolsPayload } from "./lib/jwt"
29
- export { logger } from "./lib/logger"
30
-
31
- // ===================================================================
32
- // Enhanced XMTP Client & Connection Management
33
- // ===================================================================
34
- // export {
35
- // // Enhanced connection management
36
- // XMTPConnectionManager,
37
- // createXMTPConnectionManager,
38
- // type XMTPConnectionConfig,
39
- // type XMTPConnectionHealth
40
- // } from "./client"
41
-
42
- // ===================================================================
43
- // XMTP Service Client (for external service communication)
44
- // ===================================================================
45
- // export {
46
- // XmtpServiceClient,
47
- // createXmtpServiceClient
48
- // } from "./service-client"
49
-
50
- // Service Client Types
51
- // export type {
52
- // GetMessageParams,
53
- // GetRootMessageParams,
54
- // // Function parameter types
55
- // SendMessageParams,
56
- // // Response types
57
- // SendMessageResponse,
58
- // SendReactionParams,
59
- // SendReactionResponse,
60
- // SendReplyParams,
61
- // SendReplyResponse,
62
- // SendTransactionParams,
63
- // SendTransactionResponse,
64
- // TransactionCall,
65
- // TransactionRequest,
66
- // XmtpRootMessageResponse,
67
- // XmtpServiceClientConfig,
68
- // XmtpServiceMessage,
69
- // XmtpServiceResponse
70
- // } from "./types"
71
-
72
- // ===================================================================
73
- // XMTP Core SDK Exports
74
- // ===================================================================
75
- export {
76
- Client,
77
- IdentifierKind,
78
- // type Conversation,
79
- type DecodedMessage,
80
- type Dm,
81
- // type Group,
82
- type LogLevel,
83
- type Signer,
84
- type XmtpEnv
85
- } from "@xmtp/node-sdk"
86
-
87
- // ===================================================================
88
- // XMTP Content Types
89
- // ===================================================================
90
- export {
91
- ContentTypeTransactionReference,
92
- type TransactionReference
93
- } from "@xmtp/content-type-transaction-reference"
94
-
95
- export { ContentTypeText, type TextParameters } from "@xmtp/content-type-text"
96
-
97
- export {
98
- ContentTypeReaction,
99
- type Reaction
100
- } from "@xmtp/content-type-reaction"
101
-
102
- export {
103
- ContentTypeReply,
104
- ReplyCodec,
105
- type Reply
106
- } from "@xmtp/content-type-reply"
107
-
108
- export {
109
- ContentTypeGroupUpdated,
110
- GroupUpdatedCodec,
111
- type GroupUpdated
112
- } from "@xmtp/content-type-group-updated"
113
-
114
- export {
115
- ContentTypeWalletSendCalls,
116
- type WalletSendCallsParams
117
- } from "@xmtp/content-type-wallet-send-calls"
118
-
119
- // ===================================================================
120
- // Local Client Utilities
121
- // ===================================================================
122
- // export {
123
- // backupDbToPersistentStorage,
124
- // createSigner,
125
- // createUser,
126
- // createXMTPClient,
127
- // diagnoseXMTPIdentityIssue,
128
- // generateEncryptionKeyHex,
129
- // getEncryptionKeyFromHex,
130
- // logAgentDetails,
131
- // startPeriodicBackup,
132
- // validateEnvironment
133
- // } from "./client"
134
-
135
- // ===================================================================
136
- // Application Constants
137
- // ===================================================================
138
- export {
139
- DEFAULT_AMOUNT,
140
- DEFAULT_OPTIONS,
141
- MAX_USDC_AMOUNT
142
- } from "./constants"
143
-
144
- export { Agent, createSigner, createUser, getTestUrl } from "@xmtp/agent-sdk"
145
-