hybrid 1.2.2 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,392 @@
1
+ /**
2
+ * @fileoverview XMTP Communication Tools for Crypto Agents
3
+ *
4
+ * This module provides comprehensive XMTP messaging tools for crypto-enabled agents.
5
+ * Includes capabilities for sending messages, replies, reactions, and managing conversations.
6
+ *
7
+ * @module XMTPTools
8
+ */
9
+
10
+ import { z } from "zod"
11
+ import { createTool } from "../core/tool"
12
+
13
+ /**
14
+ * Send Reaction Tool
15
+ *
16
+ * Sends an emoji reaction to a specific message to indicate the message has been seen.
17
+ * This is used to acknowledge receipt of messages before responding.
18
+ *
19
+ * @tool sendReaction
20
+ * @category Communication
21
+ *
22
+ * @param {string} emoji - The emoji to send as a reaction (defaults to 👀)
23
+ * @param {string} [referenceMessageId] - The message ID to react to (uses current message if not provided)
24
+ *
25
+ * @returns {Promise<{success: boolean, emoji: string, error?: string}>}
26
+ */
27
+ export const sendReactionTool = createTool({
28
+ id: "sendReaction",
29
+ description:
30
+ "Send an emoji reaction to a message to indicate it has been seen",
31
+ inputSchema: z.object({
32
+ emoji: z
33
+ .string()
34
+ .default("👀")
35
+ .describe(
36
+ "The emoji to send as a reaction (supports common emoji like 👍, ❤️, 🔥, etc.)"
37
+ ),
38
+ referenceMessageId: z
39
+ .string()
40
+ .optional()
41
+ .describe(
42
+ "The message ID to react to (uses current message if not provided)"
43
+ )
44
+ }),
45
+ outputSchema: z.object({
46
+ success: z.boolean(),
47
+ emoji: z.string(),
48
+ error: z.string().optional()
49
+ }),
50
+ execute: async ({ input, runtime }) => {
51
+ try {
52
+ const xmtpClient = runtime.xmtpClient
53
+ const currentMessage = runtime.message
54
+
55
+ if (!xmtpClient) {
56
+ const errorMsg = "❌ XMTP service not available"
57
+ return { success: false, emoji: input.emoji, error: errorMsg }
58
+ }
59
+
60
+ if (!currentMessage) {
61
+ const errorMsg = "❌ No message to react to"
62
+ return { success: false, emoji: input.emoji, error: errorMsg }
63
+ }
64
+
65
+ // Use provided reference message ID or current message ID
66
+ const messageIdToReactTo = input.referenceMessageId || currentMessage.id
67
+
68
+ console.log(
69
+ `👀 [sendReaction] Sending ${input.emoji} reaction to message ${messageIdToReactTo}`
70
+ )
71
+
72
+ const reactionResult = await xmtpClient.sendReaction({
73
+ messageId: messageIdToReactTo,
74
+ emoji: input.emoji,
75
+ action: "added"
76
+ })
77
+
78
+ if (!reactionResult.success) {
79
+ const errorMsg = `❌ Failed to send reaction: ${reactionResult.error || "Unknown error"}`
80
+ return { success: false, emoji: input.emoji, error: errorMsg }
81
+ }
82
+
83
+ console.log(`✅ [sendReaction] Successfully sent ${input.emoji} reaction`)
84
+ return { success: true, emoji: input.emoji }
85
+ } catch (error) {
86
+ const errorMessage =
87
+ error instanceof Error ? error.message : String(error)
88
+ console.error("❌ [sendReaction] Error:", errorMessage)
89
+ return { success: false, emoji: input.emoji, error: errorMessage }
90
+ }
91
+ }
92
+ })
93
+
94
+ /**
95
+ * Send Message Tool
96
+ *
97
+ * Sends a message to an XMTP conversation or creates a new conversation.
98
+ *
99
+ * @tool sendMessage
100
+ * @category Communication
101
+ *
102
+ * @param {string} content - The message content to send
103
+ * @param {string} [recipientAddress] - Recipient address for new conversations
104
+ * @param {string} [conversationId] - Existing conversation ID to send to
105
+ *
106
+ * @returns {Promise<{success: boolean, messageId?: string, conversationId?: string, error?: string}>}
107
+ */
108
+ export const sendMessageTool = createTool({
109
+ id: "sendMessage",
110
+ description: "Send a message to an XMTP conversation",
111
+ inputSchema: z
112
+ .object({
113
+ content: z.string().describe("The message content to send"),
114
+ recipientAddress: z
115
+ .string()
116
+ .optional()
117
+ .describe("Recipient address for new conversations"),
118
+ conversationId: z
119
+ .string()
120
+ .optional()
121
+ .describe("Existing conversation ID to send to")
122
+ })
123
+ .refine((data) => data.recipientAddress || data.conversationId, {
124
+ message: "Either recipientAddress or conversationId must be provided"
125
+ }),
126
+ outputSchema: z.object({
127
+ success: z.boolean(),
128
+ messageId: z.string().optional(),
129
+ conversationId: z.string().optional(),
130
+ content: z.string(),
131
+ error: z.string().optional()
132
+ }),
133
+ execute: async ({ input, runtime }) => {
134
+ try {
135
+ const xmtpClient = runtime.xmtpClient
136
+ const { content, recipientAddress, conversationId } = input
137
+
138
+ if (!xmtpClient) {
139
+ return {
140
+ success: false,
141
+ content,
142
+ error: "XMTP service not available"
143
+ }
144
+ }
145
+
146
+ console.log(
147
+ `💬 [sendMessage] Sending message: "${content.substring(0, 50)}${content.length > 50 ? "..." : ""}"`
148
+ )
149
+
150
+ let targetConversationId = conversationId
151
+
152
+ // If no conversation ID provided, create or find conversation with recipient
153
+ if (!targetConversationId && recipientAddress) {
154
+ console.log(
155
+ `🔍 [sendMessage] Creating/finding conversation with ${recipientAddress}`
156
+ )
157
+ // This would depend on your XMTP client implementation
158
+ // For now, we'll assume the client handles conversation creation
159
+ targetConversationId = recipientAddress // Simplified for this example
160
+ }
161
+
162
+ // Send the message using the XMTP client
163
+ const messageResult = await xmtpClient.sendMessage({
164
+ content
165
+ })
166
+
167
+ if (!messageResult.success) {
168
+ return {
169
+ success: false,
170
+ content,
171
+ error: messageResult.error || "Failed to send message"
172
+ }
173
+ }
174
+
175
+ console.log(`✅ [sendMessage] Message sent successfully`)
176
+
177
+ return {
178
+ success: true,
179
+ messageId: messageResult.data?.conversationId,
180
+ conversationId: messageResult.data?.conversationId,
181
+ content
182
+ }
183
+ } catch (error) {
184
+ const errorMessage =
185
+ error instanceof Error ? error.message : String(error)
186
+ console.error("❌ [sendMessage] Error:", errorMessage)
187
+ return {
188
+ success: false,
189
+ content: input.content,
190
+ error: errorMessage
191
+ }
192
+ }
193
+ }
194
+ })
195
+
196
+ /**
197
+ * Send Reply Tool
198
+ *
199
+ * Sends a reply to a specific message in an XMTP conversation.
200
+ *
201
+ * @tool sendReply
202
+ * @category Communication
203
+ *
204
+ * @param {string} content - The reply content to send
205
+ * @param {string} [replyToMessageId] - Message ID to reply to (uses current message if not provided)
206
+ *
207
+ * @returns {Promise<{success: boolean, messageId?: string, replyToMessageId?: string, error?: string}>}
208
+ */
209
+ export const sendReplyTool = createTool({
210
+ id: "sendReply",
211
+ description: "Send a reply to a specific message in an XMTP conversation",
212
+ inputSchema: z.object({
213
+ content: z.string().describe("The reply content to send"),
214
+ replyToMessageId: z
215
+ .string()
216
+ .optional()
217
+ .describe("Message ID to reply to (uses current message if not provided)")
218
+ }),
219
+ outputSchema: z.object({
220
+ success: z.boolean(),
221
+ messageId: z.string().optional(),
222
+ replyToMessageId: z.string().optional(),
223
+ content: z.string(),
224
+ error: z.string().optional()
225
+ }),
226
+ execute: async ({ input, runtime }) => {
227
+ try {
228
+ const xmtpClient = runtime.xmtpClient
229
+ const currentMessage = runtime.message
230
+ const { content, replyToMessageId } = input
231
+
232
+ if (!xmtpClient) {
233
+ return {
234
+ success: false,
235
+ content,
236
+ error: "XMTP service not available"
237
+ }
238
+ }
239
+
240
+ if (!currentMessage && !replyToMessageId) {
241
+ return {
242
+ success: false,
243
+ content,
244
+ error: "No message to reply to"
245
+ }
246
+ }
247
+
248
+ const targetMessageId = replyToMessageId || currentMessage?.id
249
+
250
+ console.log(
251
+ `↩️ [sendReply] Sending reply to message ${targetMessageId}: "${content.substring(0, 50)}${content.length > 50 ? "..." : ""}"`
252
+ )
253
+
254
+ const replyResult = await xmtpClient.sendReply({
255
+ content,
256
+ messageId: targetMessageId
257
+ })
258
+
259
+ if (!replyResult.success) {
260
+ return {
261
+ success: false,
262
+ content,
263
+ replyToMessageId: targetMessageId,
264
+ error: replyResult.error || "Failed to send reply"
265
+ }
266
+ }
267
+
268
+ console.log(`✅ [sendReply] Reply sent successfully`)
269
+
270
+ return {
271
+ success: true,
272
+ messageId: replyResult.data?.conversationId,
273
+ replyToMessageId: targetMessageId,
274
+ content
275
+ }
276
+ } catch (error) {
277
+ const errorMessage =
278
+ error instanceof Error ? error.message : String(error)
279
+ console.error("❌ [sendReply] Error:", errorMessage)
280
+ return {
281
+ success: false,
282
+ content: input.content,
283
+ replyToMessageId: input.replyToMessageId,
284
+ error: errorMessage
285
+ }
286
+ }
287
+ }
288
+ })
289
+
290
+ /**
291
+ * Get Message Tool
292
+ *
293
+ * Retrieves a specific message by ID from the XMTP service.
294
+ *
295
+ * @tool getMessage
296
+ * @category Communication
297
+ *
298
+ * @param {string} messageId - The message ID to retrieve
299
+ *
300
+ * @returns {Promise<{success: boolean, message?: object, error?: string}>}
301
+ */
302
+ export const getMessageTool = createTool({
303
+ id: "getMessage",
304
+ description: "Get a specific message by ID from XMTP",
305
+ inputSchema: z.object({
306
+ messageId: z.string().describe("The message ID to retrieve")
307
+ }),
308
+ outputSchema: z.object({
309
+ success: z.boolean(),
310
+ message: z
311
+ .object({
312
+ id: z.string(),
313
+ conversationId: z.string(),
314
+ content: z.union([z.string(), z.record(z.unknown())]),
315
+ senderInboxId: z.string(),
316
+ sentAt: z.string(),
317
+ contentType: z
318
+ .object({
319
+ typeId: z.string(),
320
+ authorityId: z.string().optional(),
321
+ versionMajor: z.number().optional(),
322
+ versionMinor: z.number().optional()
323
+ })
324
+ .optional()
325
+ })
326
+ .optional(),
327
+ error: z.string().optional()
328
+ }),
329
+ execute: async ({ input, runtime }) => {
330
+ try {
331
+ const xmtpClient = runtime.xmtpClient
332
+ const { messageId } = input
333
+
334
+ if (!xmtpClient) {
335
+ return {
336
+ success: false,
337
+ error: "XMTP service not available"
338
+ }
339
+ }
340
+
341
+ console.log(`📜 [getMessage] Retrieving message ${messageId}`)
342
+
343
+ const messageResult = await xmtpClient.getMessage({
344
+ messageId
345
+ })
346
+
347
+ if (!messageResult.success) {
348
+ return {
349
+ success: false,
350
+ error: messageResult.error || "Failed to get message"
351
+ }
352
+ }
353
+
354
+ console.log(
355
+ `✅ [getMessage] Retrieved message from ${messageResult.data?.senderInboxId}`
356
+ )
357
+
358
+ return {
359
+ success: true,
360
+ message: messageResult.data
361
+ }
362
+ } catch (error) {
363
+ const errorMessage =
364
+ error instanceof Error ? error.message : String(error)
365
+ console.error("❌ [getMessage] Error:", errorMessage)
366
+ return {
367
+ success: false,
368
+ error: errorMessage
369
+ }
370
+ }
371
+ }
372
+ })
373
+
374
+ /**
375
+ * Collection of XMTP communication tools for crypto agents
376
+ *
377
+ * These tools provide comprehensive messaging capabilities including sending messages,
378
+ * replies, reactions, and retrieving message information.
379
+ *
380
+ * @namespace xmtpTools
381
+ *
382
+ * @property {Tool} sendMessage - Send a message to an XMTP conversation
383
+ * @property {Tool} sendReply - Send a reply to a specific message
384
+ * @property {Tool} sendReaction - Send an emoji reaction to a message
385
+ * @property {Tool} getMessage - Get a specific message by ID
386
+ */
387
+ export const xmtpTools = {
388
+ sendMessage: sendMessageTool,
389
+ sendReply: sendReplyTool,
390
+ sendReaction: sendReactionTool,
391
+ getMessage: getMessageTool
392
+ }