@xmtp/agent-sdk 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,48 +1,212 @@
1
- # XMTP Agent SDK for Node
1
+ # XMTP Agent SDK
2
2
 
3
- This package provides the XMTP Agent SDK for Node.
4
-
5
- To keep up with the latest SDK developments, see the [Issues tab](https://github.com/xmtp/xmtp-js/issues) in this repo.
3
+ Build event‑driven, middleware‑powered messaging agents on the XMTP network. 🚀
6
4
 
7
5
  > [!CAUTION]
8
6
  > This SDK is in beta status and ready for you to build with in production. Software in this status may change based on feedback.
9
7
 
10
- ## Features
8
+ ## Documentation
9
+
10
+ Full agent building guide: **[Build an XMTP Agent](https://docs.xmtp.org/agents/get-started/build-an-agent)**
11
+
12
+ This SDK is based on familiar Node.js patterns: you register event listeners, compose middleware, and extend behavior just like you would in frameworks such as [Express](https://expressjs.com/). This makes it easy to bring existing JavaScript and TypeScript skills into building conversational agents.
13
+
14
+ ## Installation
15
+
16
+ Choose your package manager:
11
17
 
12
- ### Event-driven architecture
18
+ ```bash
19
+ npm install @xmtp/agent-sdk
20
+ # or
21
+ pnpm add @xmtp/agent-sdk
22
+ # or
23
+ yarn add @xmtp/agent-sdk
24
+ ```
13
25
 
14
- Subscribe to specific events and receive only the ones that matter to you using Node's `EventEmitter`:
26
+ ## Quick Start
15
27
 
16
28
  ```ts
29
+ import { Agent, createSigner, createUser } from "@xmtp/agent-sdk";
30
+
31
+ // 1. Create a local user + signer (you can plug in your own wallet signer)
32
+ const user = createUser();
33
+ const signer = createSigner(user);
34
+
35
+ // 2. Spin up the agent
36
+ const agent = await Agent.create(signer, {
37
+ env: "dev", // or 'production'
38
+ dbPath: null, // in-memory store; provide a path to persist
39
+ });
40
+
41
+ // 3. Respond to any incoming message
17
42
  agent.on("message", async (ctx) => {
18
- await ctx.conversation.send("Hello!");
43
+ await ctx.conversation.send("Hello from my XMTP Agent! 👋");
19
44
  });
45
+
46
+ // 4. Log when we're ready
47
+ agent.on("start", () => {
48
+ const address = agent.client.accountIdentifier?.identifier;
49
+ const env = agent.client.options?.env;
50
+ console.log(`Agent online: http://xmtp.chat/dm/${address}?env=${env}`);
51
+ });
52
+
53
+ await agent.start();
20
54
  ```
21
55
 
22
- ### Middleware support
56
+ ## Environment Variables
57
+
58
+ The XMTP Agent SDK supports environment variables (`process.env`) to simplify configuration without code changes.
59
+
60
+ **Available Variables:**
23
61
 
24
- Build flexible middleware pipelines by composing the tools you need (Custom Filters, Telemetry, Analytics, …):
62
+ | Variable | Purpose | Example |
63
+ | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
64
+ | `XMTP_WALLET_KEY` | [Private key for wallet](https://docs.xmtp.org/inboxes/core-messaging/create-a-signer) | `XMTP_WALLET_KEY=0x1234...abcd` |
65
+ | `XMTP_ENV` | [Network environment](https://docs.xmtp.org/agents/core-messaging/create-a-client#xmtp-network-environments) | `XMTP_ENV=dev` or `XMTP_ENV=production` |
66
+ | `XMTP_DB_ENCRYPTION_KEY` | [Database encryption key](https://docs.xmtp.org/agents/core-messaging/create-a-client#keep-the-database-encryption-key-safe) | `XMTP_DB_ENCRYPTION_KEY=0xabcd...1234` |
67
+ | `XMTP_FORCE_DEBUG` | [Activate debugging logs](https://docs.xmtp.org/agents/debug-agents) | `XMTP_FORCE_DEBUG=true` |
68
+ | `XMTP_FORCE_REVOKE_INSTALLATIONS` | [Remove other installations](https://docs.xmtp.org/agents/core-messaging/agent-installations#revoke-agent-installations) | `XMTP_FORCE_REVOKE_INSTALLATIONS=true` |
69
+
70
+ Using the environment variables, you can setup your agent in just a few lines of code:
25
71
 
26
72
  ```ts
73
+ // Load variables from .env file
74
+ process.loadEnvFile(".env");
75
+
76
+ // Create agent using environment variables
77
+ const agent = await Agent.create();
78
+ ```
79
+
80
+ ## Core Concepts
81
+
82
+ ### 1. Event‑Driven Architecture
83
+
84
+ Subscribe only to what you need using Node’s `EventEmitter` interface.
85
+
86
+ Events you can listen for:
87
+
88
+ - `message` – a new incoming (non‑self) message
89
+ - `start` / `stop` – lifecycle events
90
+ - `error` – surfaced errors
91
+
92
+ **Example:**
93
+
94
+ ```ts
95
+ agent.on("error", (error) => {
96
+ console.error("Agent error", error);
97
+ });
98
+ ```
99
+
100
+ ### 2. Middleware Support
101
+
102
+ Extend your agent with custom business logic using middlewares. Compose cross-cutting behavior like routing, telemetry, rate limiting, analytics, and feature flags, or plug in your own.
103
+
104
+ **Example:**
105
+
106
+ ```ts
107
+ import { CommandRouter } from "@xmtp/agent-sdk";
108
+
27
109
  const router = new CommandRouter();
28
110
 
29
- router.command("/start", async (ctx) => {
30
- await ctx.conversation.send("👋 Welcome to your XMTP agent!");
111
+ router.command("/version", async (ctx) => {
112
+ await ctx.conversation.send(`v${process.env.npm_package_version}`);
31
113
  });
32
114
 
33
- const agent = new Agent({ client });
34
115
  agent.use(router.middleware());
35
116
  ```
36
117
 
37
- ### Built-in Filters
118
+ ### 3. Builtin Filters
119
+
120
+ Instead of manually checking every incoming message, you can compose simple, reusable filters that make intent clear.
38
121
 
39
- Skip repetitive condition checks with ready-to-use message filters, easily chained through a fluent API:
122
+ **Example:**
40
123
 
41
124
  ```ts
125
+ import { withFilter, filter } from "@xmtp/agent-sdk";
126
+
127
+ // Using filter in message handler
42
128
  agent.on(
43
129
  "message",
44
- withFilter(filter.and(filter.notFromSelf, filter.textOnly), async (ctx) => {
45
- await ctx.conversation.send("Hey!");
130
+ withFilter(filter.startsWith("@agent"), async (ctx) => {
131
+ await ctx.conversation.send("How can I help you?");
46
132
  }),
47
133
  );
134
+
135
+ // Combination of filters
136
+ const combined = filter.and(filter.notFromSelf, filter.textOnly);
137
+
138
+ agent.on(
139
+ "message",
140
+ withFilter(combined, async (ctx) => {
141
+ await ctx.conversation.send("You sent a text message ✅");
142
+ }),
143
+ );
144
+ ```
145
+
146
+ For convenience, the `filter` object can also be imported as `f`:
147
+
148
+ ```ts
149
+ // You can import either name:
150
+ import { filter, f } from "@xmtp/agent-sdk";
151
+
152
+ // Both work the same way:
153
+ const longVersion = filter.and(filter.notFromSelf, filter.textOnly);
154
+ const shortVersion = f.and(f.notFromSelf, f.textOnly);
155
+ ```
156
+
157
+ You can find all available prebuilt filters [here](https://github.com/xmtp/xmtp-js/blob/main/sdks/agent-sdk/src/utils/filter.ts).
158
+
159
+ ### 4. Rich Context
160
+
161
+ Every `message` handler receives an `AgentContext` with:
162
+
163
+ - `message` – decoded message
164
+ - `conversation` – the active conversation object
165
+ - `client` – underlying XMTP client
166
+ - Helpers like `sendText()` / `sendTextReply()`
167
+
168
+ **Example:**
169
+
170
+ ```ts
171
+ agent.on("message", async (ctx) => {
172
+ await ctx.sendTextReply("Reply using helper ✨");
173
+ });
174
+ ```
175
+
176
+ ## Adding Custom Content Types
177
+
178
+ Pass codecs when creating your agent to extend supported content:
179
+
180
+ ```ts
181
+ import { ReplyCodec } from "@xmtp/content-type-reply";
182
+
183
+ const agent = await Agent.create(signer, {
184
+ env: "dev",
185
+ dbPath: null,
186
+ codecs: [new ReplyCodec()],
187
+ });
48
188
  ```
189
+
190
+ ## Debugging
191
+
192
+ - [Debug an agent](https://docs.xmtp.org/agents/debug-agents)
193
+ - [Further debugging info](https://docs.xmtp.org/inboxes/debug-your-app#debug-your-inbox-app)
194
+
195
+ ## FAQ (Quick Hits)
196
+
197
+ | Question | Answer |
198
+ | -------------------------------------------------------------------------------------------- | ----------------------------------------------- |
199
+ | Does middleware run for every message? | Yes, in the order added. |
200
+ | How do I reject a message early? | Don’t call `next()` in middleware. |
201
+ | How do I filter messages? | Use `withFilter(...)` around an event listener. |
202
+ | Can I send custom [content types](https://docs.xmtp.org/agents/content-types/content-types)? | Yes, register codecs during agent creation. |
203
+
204
+ ## Contributing / Feedback
205
+
206
+ We’d love your feedback: [open an issue](https://github.com/xmtp/xmtp-js/issues) or discussion. PRs welcome for docs, examples, and core improvements.
207
+
208
+ ---
209
+
210
+ Build something delightful. Then tell us what you wish was easier.
211
+
212
+ Happy hacking 💫
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env node
2
+ import { writeFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { generateClientKeys } from "../utils/crypto.js";
5
+ /**
6
+ * Generates client keys and saves them to a .env file in the project root.
7
+ * This script creates the necessary environment variables for XMTP agent initialization.
8
+ */
9
+ function main() {
10
+ try {
11
+ // Generate the client keys
12
+ const keys = generateClientKeys();
13
+ // Create the .env file content
14
+ const envContent = Object.entries(keys)
15
+ .map(([key, value]) => `${key}=${value}`)
16
+ .join("\n") + "\n";
17
+ if (!process.env.INIT_CWD) {
18
+ throw new Error(`Cannot invoke script because "process.env.INIT_CWD" wasn't found.`);
19
+ }
20
+ const envFilePath = join(process.env.INIT_CWD, ".env");
21
+ writeFileSync(envFilePath, envContent, "utf8");
22
+ console.log("✅ Successfully generated client keys and saved to .env file");
23
+ console.log(`📁 File location: ${envFilePath}`);
24
+ console.log("🔑 Generated keys:");
25
+ Object.keys(keys).forEach((key) => {
26
+ console.log(` - ${key}`);
27
+ });
28
+ }
29
+ catch (error) {
30
+ console.error("❌ Error generating client keys:", error);
31
+ process.exit(1);
32
+ }
33
+ }
34
+ main();
35
+ //# sourceMappingURL=generateKeys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generateKeys.js","sourceRoot":"","sources":["../../src/bin/generateKeys.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD;;;GAGG;AACH,SAAS,IAAI;IACX,IAAI,CAAC;QACH,2BAA2B;QAC3B,MAAM,IAAI,GAAG,kBAAkB,EAAE,CAAC;QAElC,+BAA+B;QAC/B,MAAM,UAAU,GACd,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;aACjB,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,KAAK,EAAE,CAAC;aACxC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QAEvB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACb,mEAAmE,CACpE,CAAC;QACJ,CAAC;QACD,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAEvD,aAAa,CAAC,WAAW,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;QAE/C,OAAO,CAAC,GAAG,CAAC,6DAA6D,CAAC,CAAC;QAC3E,OAAO,CAAC,GAAG,CAAC,qBAAqB,WAAW,EAAE,CAAC,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;QAClC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;YAChC,OAAO,CAAC,GAAG,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,KAAK,CAAC,CAAC;QACxD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC"}
@@ -1,5 +1,8 @@
1
1
  import EventEmitter from "node:events";
2
2
  import type { ContentCodec } from "@xmtp/content-type-primitives";
3
+ import { ReactionCodec } from "@xmtp/content-type-reaction";
4
+ import { RemoteAttachmentCodec } from "@xmtp/content-type-remote-attachment";
5
+ import { ReplyCodec } from "@xmtp/content-type-reply";
3
6
  import { Client, type ClientOptions } from "@xmtp/node-sdk";
4
7
  import { AgentContext } from "./AgentContext.js";
5
8
  interface EventHandlerMap<ContentTypes> {
@@ -11,16 +14,14 @@ interface EventHandlerMap<ContentTypes> {
11
14
  export interface AgentOptions<ContentTypes> {
12
15
  client: Client<ContentTypes>;
13
16
  }
17
+ export type AgentEventHandler<ContentTypes = unknown> = (ctx: AgentContext<ContentTypes>) => Promise<void> | void;
14
18
  export type AgentMiddleware<ContentTypes> = (ctx: AgentContext<ContentTypes>, next: () => Promise<void>) => Promise<void>;
15
19
  export declare class Agent<ContentTypes> extends EventEmitter<EventHandlerMap<ContentTypes>> {
16
20
  #private;
17
21
  constructor({ client }: AgentOptions<ContentTypes>);
18
- static create<ContentCodecs extends ContentCodec[] = []>(signer: Parameters<typeof Client.create>[0], options?: Omit<ClientOptions, "codecs"> & {
22
+ static create<ContentCodecs extends ContentCodec[] = []>(signer?: Parameters<typeof Client.create>[0], options?: Omit<ClientOptions, "codecs"> & {
19
23
  codecs?: ContentCodecs;
20
- }): Promise<Agent<import("@xmtp/node-sdk").ExtractCodecContentTypes<ContentCodecs>>>;
21
- static build<ContentCodecs extends ContentCodec[] = []>(identifier: Parameters<typeof Client.build>[0], options?: Omit<ClientOptions, "codecs"> & {
22
- codecs?: ContentCodecs;
23
- }): Promise<Agent<import("@xmtp/node-sdk").ExtractCodecContentTypes<ContentCodecs>>>;
24
+ }): Promise<Agent<import("@xmtp/node-sdk").ExtractCodecContentTypes<(ReactionCodec | ReplyCodec | RemoteAttachmentCodec | (never[] | ContentCodecs)[number])[]>>>;
24
25
  use(middleware: AgentMiddleware<ContentTypes>): this;
25
26
  start(): Promise<void>;
26
27
  get client(): Client<ContentTypes>;
@@ -1,6 +1,13 @@
1
1
  import EventEmitter from "node:events";
2
- import { Client, } from "@xmtp/node-sdk";
3
- import { filter } from "@/utils/filter.js";
2
+ import { ReactionCodec } from "@xmtp/content-type-reaction";
3
+ import { RemoteAttachmentCodec } from "@xmtp/content-type-remote-attachment";
4
+ import { ReplyCodec } from "@xmtp/content-type-reply";
5
+ import { ApiUrls, Client, } from "@xmtp/node-sdk";
6
+ import { isHex } from "viem/utils";
7
+ import { getEncryptionKeyFromHex } from "../utils/crypto.js";
8
+ import { logDetails } from "../utils/debug.js";
9
+ import { filter } from "../utils/filter.js";
10
+ import { createSigner, createUser } from "../utils/user.js";
4
11
  import { AgentContext } from "./AgentContext.js";
5
12
  export class Agent extends EventEmitter {
6
13
  #client;
@@ -13,13 +20,44 @@ export class Agent extends EventEmitter {
13
20
  static async create(signer,
14
21
  // Note: we need to omit this so that "Client.create" can correctly infer the codecs.
15
22
  options) {
16
- const client = await Client.create(signer, options);
17
- return new Agent({ client });
18
- }
19
- static async build(identifier,
20
- // Note: we need to omit this so that "Client.build" can correctly infer the codecs.
21
- options) {
22
- const client = await Client.build(identifier, options);
23
+ if (!signer) {
24
+ if (isHex(process.env.XMTP_WALLET_KEY)) {
25
+ signer = createSigner(createUser(process.env.XMTP_WALLET_KEY));
26
+ }
27
+ else {
28
+ throw new Error(`No signer detected. Provide a "signer" to "Agent.create()" or set the "XMTP_WALLET_KEY" environment variable to a private key in hexadecimal format. Read more: https://docs.xmtp.org/inboxes/core-messaging/create-a-signer`);
29
+ }
30
+ }
31
+ const initializedOptions = { ...options };
32
+ initializedOptions.appVersion ??= "agent-sdk/alpha";
33
+ if (process.env.XMTP_DB_ENCRYPTION_KEY) {
34
+ initializedOptions.dbEncryptionKey = getEncryptionKeyFromHex(process.env.XMTP_DB_ENCRYPTION_KEY);
35
+ }
36
+ if (process.env.XMTP_ENV &&
37
+ Object.keys(ApiUrls).includes(process.env.XMTP_ENV)) {
38
+ initializedOptions.env = process.env.XMTP_ENV;
39
+ }
40
+ if (process.env.XMTP_FORCE_DEBUG) {
41
+ initializedOptions.debugEventsEnabled = true;
42
+ initializedOptions.loggingLevel = "warn" /* LogLevel.warn */;
43
+ initializedOptions.structuredLogging = true;
44
+ }
45
+ const upgradedCodecs = [
46
+ ...(initializedOptions.codecs ?? []),
47
+ new ReactionCodec(),
48
+ new ReplyCodec(),
49
+ new RemoteAttachmentCodec(),
50
+ ];
51
+ const client = await Client.create(signer, {
52
+ ...initializedOptions,
53
+ codecs: upgradedCodecs,
54
+ });
55
+ if (process.env.XMTP_FORCE_REVOKE_INSTALLATIONS) {
56
+ await client.revokeAllOtherInstallations();
57
+ }
58
+ if (process.env.XMTP_FORCE_DEBUG) {
59
+ await logDetails(client);
60
+ }
23
61
  return new Agent({ client });
24
62
  }
25
63
  use(middleware) {
@@ -1 +1 @@
1
- {"version":3,"file":"Agent.js","sourceRoot":"","sources":["../../src/core/Agent.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,aAAa,CAAC;AAEvC,OAAO,EACL,MAAM,GAGP,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAkBjD,MAAM,OAAO,KAAoB,SAAQ,YAExC;IACC,OAAO,CAAuB;IAC9B,WAAW,GAAoC,EAAE,CAAC;IAClD,YAAY,GAAG,KAAK,CAAC;IAErB,YAAY,EAAE,MAAM,EAA8B;QAChD,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;IACxB,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,MAAM,CACjB,MAA2C;IAC3C,qFAAqF;IACrF,OAAoE;QAEpE,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACpD,OAAO,IAAI,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,KAAK,CAChB,UAA8C;IAC9C,oFAAoF;IACpF,OAAoE;QAEpE,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACvD,OAAO,IAAI,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED,GAAG,CAAC,UAAyC;QAC3C,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAClC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;YACzB,KAAK,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAExB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,iBAAiB,EAAE,CAAC;YACpE,IAAI,KAAK,EAAE,MAAM,OAAO,IAAI,MAAM,EAAE,CAAC;gBACnC,yCAAyC;gBACzC,uEAAuE;gBACvE,IAAI,CAAC,IAAI,CAAC,YAAY;oBAAE,MAAM;gBAC9B,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;gBACtC,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;gBAC1B,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;YAC1B,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,OAAqC;QACzD,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,mBAAmB,CACvE,OAAO,CAAC,cAAc,CACvB,CAAC;QAEF,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,IAAI,CAAC,WAAW,CACd,IAAI,KAAK,CACP,iCAAiC,OAAO,CAAC,EAAE,0BAA0B,OAAO,CAAC,cAAc,gDAAgD,CAC5I,CACF,CAAC;YACF,OAAO;QACT,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAEtE,IAAI,eAAe,GAAG,CAAC,CAAC;QACxB,MAAM,IAAI,GAAG,KAAK,IAAI,EAAE;YACtB,IAAI,eAAe,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC;gBAC9C,MAAM,iBAAiB,GAAG,IAAI,CAAC,WAAW,CAAC,eAAe,EAAE,CAAC,CAAC;gBAC9D,MAAM,iBAAiB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YACzC,CAAC;iBAAM,IAAI,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBACrD,0DAA0D;gBAC1D,2DAA2D;gBAC3D,KAAK,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;YACrC,CAAC;QACH,CAAC,CAAC;QAEF,MAAM,IAAI,EAAE,CAAC;IACf,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,IAAI;QACF,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;QAC1B,KAAK,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACzB,CAAC;IAED,WAAW,CAAC,KAAc;QACxB,MAAM,QAAQ,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3E,KAAK,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACpC,CAAC;CACF"}
1
+ {"version":3,"file":"Agent.js","sourceRoot":"","sources":["../../src/core/Agent.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,12 @@
1
+ import { type Reaction } from "@xmtp/content-type-reaction";
1
2
  import type { Client, Conversation, DecodedMessage } from "@xmtp/node-sdk";
2
3
  export declare class AgentContext<ContentTypes = unknown> {
3
4
  #private;
4
5
  constructor(message: DecodedMessage<ContentTypes>, conversation: Conversation, client: Client<ContentTypes>);
6
+ sendReaction(content: string, schema: Reaction["schema"]): Promise<void>;
5
7
  sendText(text: string): Promise<void>;
6
8
  sendTextReply(text: string): Promise<void>;
9
+ getOwnAddress(): string | undefined;
7
10
  getSenderAddress(): Promise<string>;
8
11
  get message(): DecodedMessage<ContentTypes>;
9
12
  get conversation(): Conversation<unknown>;
@@ -1,3 +1,4 @@
1
+ import { ContentTypeReaction, } from "@xmtp/content-type-reaction";
1
2
  import { ContentTypeReply } from "@xmtp/content-type-reply";
2
3
  import { ContentTypeText } from "@xmtp/content-type-text";
3
4
  export class AgentContext {
@@ -9,6 +10,16 @@ export class AgentContext {
9
10
  this.#conversation = conversation;
10
11
  this.#client = client;
11
12
  }
13
+ async sendReaction(content, schema) {
14
+ const reaction = {
15
+ action: "added",
16
+ reference: this.#message.id,
17
+ referenceInboxId: this.#message.senderInboxId,
18
+ schema,
19
+ content,
20
+ };
21
+ await this.#conversation.send(reaction, ContentTypeReaction);
22
+ }
12
23
  async sendText(text) {
13
24
  await this.#conversation.send(text, ContentTypeText);
14
25
  }
@@ -21,6 +32,9 @@ export class AgentContext {
21
32
  };
22
33
  await this.#conversation.send(reply, ContentTypeReply);
23
34
  }
35
+ getOwnAddress() {
36
+ return this.#client.accountIdentifier?.identifier;
37
+ }
24
38
  async getSenderAddress() {
25
39
  const inboxState = await this.#client.preferences.inboxStateFromInboxIds([
26
40
  this.#message.senderInboxId,
@@ -1 +1 @@
1
- {"version":3,"file":"AgentContext.js","sourceRoot":"","sources":["../../src/core/AgentContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAc,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAG1D,MAAM,OAAO,YAAY;IACvB,OAAO,CAAuB;IAC9B,QAAQ,CAA+B;IACvC,aAAa,CAAe;IAE5B,YACE,OAAqC,EACrC,YAA0B,EAC1B,MAA4B;QAE5B,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QACxB,IAAI,CAAC,aAAa,GAAG,YAAY,CAAC;QAClC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,IAAY;QACzB,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;IACvD,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,IAAY;QAC9B,MAAM,KAAK,GAAU;YACnB,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE;YAC3B,gBAAgB,EAAE,IAAI,CAAC,QAAQ,CAAC,aAAa;YAC7C,WAAW,EAAE,eAAe;YAC5B,OAAO,EAAE,IAAI;SACd,CAAC;QACF,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC;IACzD,CAAC;IAED,KAAK,CAAC,gBAAgB;QACpB,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,sBAAsB,CAAC;YACvE,IAAI,CAAC,QAAQ,CAAC,aAAa;SAC5B,CAAC,CAAC;QACH,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;IACjD,CAAC;IAED,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;CACF"}
1
+ {"version":3,"file":"AgentContext.js","sourceRoot":"","sources":["../../src/core/AgentContext.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,GAEpB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,gBAAgB,EAAc,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAG1D,MAAM,OAAO,YAAY;IACvB,OAAO,CAAuB;IAC9B,QAAQ,CAA+B;IACvC,aAAa,CAAe;IAE5B,YACE,OAAqC,EACrC,YAA0B,EAC1B,MAA4B;QAE5B,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QACxB,IAAI,CAAC,aAAa,GAAG,YAAY,CAAC;QAClC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,OAAe,EAAE,MAA0B;QAC5D,MAAM,QAAQ,GAAa;YACzB,MAAM,EAAE,OAAO;YACf,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE;YAC3B,gBAAgB,EAAE,IAAI,CAAC,QAAQ,CAAC,aAAa;YAC7C,MAAM;YACN,OAAO;SACR,CAAC;QACF,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;IAC/D,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,IAAY;QACzB,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;IACvD,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,IAAY;QAC9B,MAAM,KAAK,GAAU;YACnB,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE;YAC3B,gBAAgB,EAAE,IAAI,CAAC,QAAQ,CAAC,aAAa;YAC7C,WAAW,EAAE,eAAe;YAC5B,OAAO,EAAE,IAAI;SACd,CAAC;QACF,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC;IACzD,CAAC;IAED,aAAa;QACX,OAAO,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,UAAU,CAAC;IACpD,CAAC;IAED,KAAK,CAAC,gBAAgB;QACpB,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,sBAAsB,CAAC;YACvE,IAAI,CAAC,QAAQ,CAAC,aAAa;SAC5B,CAAC,CAAC;QACH,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;IACjD,CAAC;IAED,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;CACF"}
package/dist/demo.js CHANGED
@@ -1,33 +1,36 @@
1
- import { ReplyCodec } from "@xmtp/content-type-reply";
1
+ import { loadEnvFile } from "node:process";
2
2
  import { Agent } from "./core/index.js";
3
- import { createSigner, createUser, filter as f, withFilter, } from "./utils/index.js";
4
- const user = createUser();
5
- const signer = createSigner(user);
6
- const agent = await Agent.create(signer, {
7
- codecs: [new ReplyCodec()],
8
- dbPath: null,
9
- env: "dev",
3
+ import { CommandRouter } from "./middleware/CommandRouter.js";
4
+ import { getTestUrl } from "./utils/debug.js";
5
+ import { createSigner, createUser, f, withFilter } from "./utils/index.js";
6
+ try {
7
+ loadEnvFile(".env");
8
+ console.info(`Loaded keys from ".env" file.`);
9
+ }
10
+ catch { }
11
+ const agent = process.env.XMTP_WALLET_KEY
12
+ ? await Agent.create()
13
+ : await Agent.create(createSigner(createUser()), {
14
+ dbPath: null,
15
+ });
16
+ const router = new CommandRouter();
17
+ router.command("/version", async (ctx) => {
18
+ await ctx.conversation.send(`v${process.env.npm_package_version}`);
10
19
  });
11
- agent.on("message", (ctx) => {
12
- void ctx.conversation.send("First message!");
13
- });
14
- agent.on("message", withFilter(f.and(f.notFromSelf, f.textOnly), (ctx) => {
15
- void ctx.conversation.send("Goodbye!");
16
- agent.stop();
17
- }));
20
+ agent.use(router.middleware());
18
21
  agent.on("message", (ctx) => {
19
22
  console.log("Got message:", ctx.message.content);
20
23
  });
24
+ agent.on("message", withFilter(f.startsWith("@agent"), async (ctx) => {
25
+ await ctx.conversation.send("How can I help you?");
26
+ }));
21
27
  const errorHandler = (error) => {
22
28
  console.log("Caught error", error);
23
29
  };
24
30
  agent.on("error", errorHandler);
25
31
  agent.off("error", errorHandler);
26
32
  agent.on("start", () => {
27
- const address = agent.client.accountIdentifier?.identifier;
28
- const env = agent.client.options?.env;
29
- const url = `http://xmtp.chat/dm/${address}?env=${env}`;
30
- console.log(`We are online: ${url}`);
33
+ console.log(`We are online: ${getTestUrl(agent)}`);
31
34
  });
32
35
  void agent.start();
33
36
  //# sourceMappingURL=demo.js.map
package/dist/demo.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"demo.js","sourceRoot":"","sources":["../src/demo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxC,OAAO,EACL,YAAY,EACZ,UAAU,EACV,MAAM,IAAI,CAAC,EACX,UAAU,GACX,MAAM,kBAAkB,CAAC;AAE1B,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;AAC1B,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;AAElC,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE;IACvC,MAAM,EAAE,CAAC,IAAI,UAAU,EAAE,CAAC;IAC1B,MAAM,EAAE,IAAI;IACZ,GAAG,EAAE,KAAK;CACX,CAAC,CAAC;AAEH,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE;IAC1B,KAAK,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;AAC/C,CAAC,CAAC,CAAC;AAEH,KAAK,CAAC,EAAE,CACN,SAAS,EACT,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE;IACnD,KAAK,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACvC,KAAK,CAAC,IAAI,EAAE,CAAC;AACf,CAAC,CAAC,CACH,CAAC;AAEF,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE;IAC1B,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;AACnD,CAAC,CAAC,CAAC;AAEH,MAAM,YAAY,GAAG,CAAC,KAAc,EAAE,EAAE;IACtC,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;AACrC,CAAC,CAAC;AAEF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;AAEhC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;AAEjC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;IACrB,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,iBAAiB,EAAE,UAAU,CAAC;IAC3D,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC;IACtC,MAAM,GAAG,GAAG,uBAAuB,OAAO,QAAQ,GAAG,EAAE,CAAC;IACxD,OAAO,CAAC,GAAG,CAAC,kBAAkB,GAAG,EAAE,CAAC,CAAC;AACvC,CAAC,CAAC,CAAC;AAEH,KAAK,KAAK,CAAC,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"demo.js","sourceRoot":"","sources":["../src/demo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE3E,IAAI,CAAC;IACH,WAAW,CAAC,MAAM,CAAC,CAAC;IACpB,OAAO,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;AAChD,CAAC;AAAC,MAAM,CAAC,CAAA,CAAC;AAEV,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe;IACvC,CAAC,CAAC,MAAM,KAAK,CAAC,MAAM,EAAE;IACtB,CAAC,CAAC,MAAM,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,UAAU,EAAE,CAAC,EAAE;QAC7C,MAAM,EAAE,IAAI;KACb,CAAC,CAAC;AAEP,MAAM,MAAM,GAAG,IAAI,aAAa,EAAE,CAAC;AAEnC,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;IACvC,MAAM,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,CAAC,CAAC;AACrE,CAAC,CAAC,CAAC;AAEH,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;AAE/B,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE;IAC1B,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;AACnD,CAAC,CAAC,CAAC;AAEH,KAAK,CAAC,EAAE,CACN,SAAS,EACT,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;IAC/C,MAAM,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;AACrD,CAAC,CAAC,CACH,CAAC;AAEF,MAAM,YAAY,GAAG,CAAC,KAAc,EAAE,EAAE;IACtC,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;AACrC,CAAC,CAAC;AAEF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;AAEhC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;AAEjC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;IACrB,OAAO,CAAC,GAAG,CAAC,kBAAkB,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AACrD,CAAC,CAAC,CAAC;AAEH,KAAK,KAAK,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { type AgentEventHandler, type AgentMiddleware } from "../core/Agent.js";
2
+ import type { AgentContext } from "../core/AgentContext.js";
3
+ export declare class CommandRouter<ContentTypes> {
4
+ private commandMap;
5
+ private defaultHandler;
6
+ command(command: string, handler: AgentEventHandler): this;
7
+ default(handler: AgentEventHandler): this;
8
+ handle(ctx: AgentContext): Promise<boolean>;
9
+ middleware: () => AgentMiddleware<ContentTypes>;
10
+ }
@@ -0,0 +1,45 @@
1
+ import { isText } from "../utils/message.js";
2
+ export class CommandRouter {
3
+ commandMap = new Map();
4
+ defaultHandler = null;
5
+ command(command, handler) {
6
+ if (!command.startsWith("/")) {
7
+ throw new Error('Command must start with "/"');
8
+ }
9
+ this.commandMap.set(command.toLowerCase(), handler);
10
+ return this;
11
+ }
12
+ default(handler) {
13
+ this.defaultHandler = handler;
14
+ return this;
15
+ }
16
+ async handle(ctx) {
17
+ if (!isText(ctx.message)) {
18
+ return false;
19
+ }
20
+ const messageText = ctx.message.content;
21
+ const parts = messageText.split(" ");
22
+ const command = parts[0].toLowerCase();
23
+ // Check if this is a command message
24
+ if (command.startsWith("/")) {
25
+ const handler = this.commandMap.get(command);
26
+ if (handler) {
27
+ await handler(ctx);
28
+ return true;
29
+ }
30
+ }
31
+ // If no command matched and there's a default handler, use it
32
+ if (this.defaultHandler) {
33
+ await this.defaultHandler(ctx);
34
+ return true;
35
+ }
36
+ return false;
37
+ }
38
+ middleware = () => async (ctx, next) => {
39
+ const handled = await this.handle(ctx);
40
+ if (!handled) {
41
+ await next();
42
+ }
43
+ };
44
+ }
45
+ //# sourceMappingURL=CommandRouter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandRouter.js","sourceRoot":"","sources":["../../src/middleware/CommandRouter.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,OAAO,aAAa;IAChB,UAAU,GAAG,IAAI,GAAG,EAA6B,CAAC;IAClD,cAAc,GAA6B,IAAI,CAAC;IAExD,OAAO,CAAC,OAAe,EAAE,OAA0B;QACjD,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACjD,CAAC;QACD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,OAAO,CAAC,CAAC;QACpD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,CAAC,OAA0B;QAChC,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC;QAC9B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAiB;QAC5B,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YACzB,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC;QACxC,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAEvC,qCAAqC;QACrC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC7C,IAAI,OAAO,EAAE,CAAC;gBACZ,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;gBACnB,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QAED,8DAA8D;QAC9D,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxB,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;YAC/B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED,UAAU,GAAwC,GAAG,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC1E,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACvC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,EAAE,CAAC;QACf,CAAC;IACH,CAAC,CAAC;CACH"}
@@ -0,0 +1 @@
1
+ export * from "./CommandRouter.js";
@@ -0,0 +1,2 @@
1
+ export * from "./CommandRouter.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Convert a hex string to a Uint8Array encryption key.
3
+ * @param hex - The hex string
4
+ * @returns The encryption key
5
+ */
6
+ export declare const getEncryptionKeyFromHex: (hex: string) => Uint8Array;
7
+ /**
8
+ * Generates a complete set of client keys required for XMTP agent initialization.
9
+ *
10
+ * Creates both a wallet private key for client authentication and a database
11
+ * encryption key for secure local storage. The returned keys can be used
12
+ * directly as environment variables.
13
+ *
14
+ * @returns An object containing the keys
15
+ */
16
+ export declare const generateClientKeys: () => {
17
+ XMTP_DB_ENCRYPTION_KEY: string;
18
+ XMTP_WALLET_KEY: `0x${string}`;
19
+ };
@@ -0,0 +1,55 @@
1
+ import { getRandomValues } from "node:crypto";
2
+ import { secp256k1 } from "@noble/curves/secp256k1.js";
3
+ import { fromString, toString } from "uint8arrays";
4
+ import { toHex } from "viem";
5
+ /**
6
+ * Convert a hex string to a Uint8Array encryption key.
7
+ * @param hex - The hex string
8
+ * @returns The encryption key
9
+ */
10
+ export const getEncryptionKeyFromHex = (hex) => {
11
+ return fromString(hex, "hex");
12
+ };
13
+ /**
14
+ * Generates a cryptographically secure random private key for wallet operations.
15
+ *
16
+ * Uses the secp256k1 elliptic curve to generate a private key suitable for
17
+ * Ethereum-compatible wallets and XMTP client authentication.
18
+ *
19
+ * @returns A hex-encoded private key string (64 characters, prefixed with '0x')
20
+ */
21
+ function generatePrivateKey() {
22
+ return toHex(secp256k1.utils.randomSecretKey());
23
+ }
24
+ /**
25
+ * Generates a cryptographically secure random encryption key for database encryption.
26
+ *
27
+ * Creates a 256-bit (32-byte) encryption key using the Node.js crypto module's
28
+ * secure random number generator.
29
+ *
30
+ * @returns A hex-encoded encryption key string (64 characters)
31
+ */
32
+ const generateEncryptionKeyHex = () => {
33
+ /* Generate a random encryption key */
34
+ const uint8Array = getRandomValues(new Uint8Array(32));
35
+ /* Convert the encryption key to a hex string */
36
+ return toString(uint8Array, "hex");
37
+ };
38
+ /**
39
+ * Generates a complete set of client keys required for XMTP agent initialization.
40
+ *
41
+ * Creates both a wallet private key for client authentication and a database
42
+ * encryption key for secure local storage. The returned keys can be used
43
+ * directly as environment variables.
44
+ *
45
+ * @returns An object containing the keys
46
+ */
47
+ export const generateClientKeys = () => {
48
+ const walletKey = generatePrivateKey();
49
+ const dbEncryptionKey = generateEncryptionKeyHex();
50
+ return {
51
+ XMTP_DB_ENCRYPTION_KEY: dbEncryptionKey,
52
+ XMTP_WALLET_KEY: walletKey,
53
+ };
54
+ };
55
+ //# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/utils/crypto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAE7B;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,GAAW,EAAc,EAAE;IACjE,OAAO,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;AAChC,CAAC,CAAC;AAEF;;;;;;;GAOG;AACH,SAAS,kBAAkB;IACzB,OAAO,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC,CAAC;AAClD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,wBAAwB,GAAG,GAAG,EAAE;IACpC,sCAAsC;IACtC,MAAM,UAAU,GAAG,eAAe,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;IACvD,gDAAgD;IAChD,OAAO,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;AACrC,CAAC,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,EAAE;IACrC,MAAM,SAAS,GAAG,kBAAkB,EAAE,CAAC;IACvC,MAAM,eAAe,GAAG,wBAAwB,EAAE,CAAC;IACnD,OAAO;QACL,sBAAsB,EAAE,eAAe;QACvC,eAAe,EAAE,SAAS;KAC3B,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { Client } from "@xmtp/node-sdk";
2
+ import type { Agent } from "../core/Agent.js";
3
+ export declare const logDetails: <ContentTypes>(client: Client<ContentTypes>) => Promise<void>;
4
+ /**
5
+ * Returns a URL to test your agent on https://xmtp.chat/ (for development purposes only).
6
+ *
7
+ * @param agent - Your agent instance
8
+ * @returns The URL to test your agent with
9
+ */
10
+ export declare const getTestUrl: <ContentTypes>(agent: Agent<ContentTypes>) => string;
@@ -0,0 +1,53 @@
1
+ import { Client } from "@xmtp/node-sdk";
2
+ export const logDetails = async (client) => {
3
+ const xmtp = `\x1b[38;2;252;76;52m
4
+ ██╗ ██╗███╗ ███╗████████╗██████╗
5
+ ╚██╗██╔╝████╗ ████║╚══██╔══╝██╔══██╗
6
+ ╚███╔╝ ██╔████╔██║ ██║ ██████╔╝
7
+ ██╔██╗ ██║╚██╔╝██║ ██║ ██╔═══╝
8
+ ██╔╝ ██╗██║ ╚═╝ ██║ ██║ ██║
9
+ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝
10
+ \x1b[0m`;
11
+ const clientsByAddress = client.accountIdentifier?.identifier;
12
+ const inboxId = client.inboxId;
13
+ const installationId = client.installationId;
14
+ const environments = client.options?.env ?? "dev";
15
+ const urls = [`http://xmtp.chat/dm/${clientsByAddress}`];
16
+ const conversations = await client.conversations.list();
17
+ const inboxState = await client.preferences.inboxState();
18
+ const keyPackageStatuses = await client.getKeyPackageStatusesForInstallationIds([installationId]);
19
+ let createdDate = new Date();
20
+ let expiryDate = new Date();
21
+ // Extract key package status for the specific installation
22
+ const keyPackageStatus = keyPackageStatuses[installationId] ?? {};
23
+ if (keyPackageStatus.lifetime) {
24
+ createdDate = new Date(Number(keyPackageStatus.lifetime.notBefore) * 1000);
25
+ expiryDate = new Date(Number(keyPackageStatus.lifetime.notAfter) * 1000);
26
+ }
27
+ console.log(`
28
+ ${xmtp}
29
+
30
+ ✓ XMTP Client:
31
+ • InboxId: ${inboxId}
32
+ • Version: ${Client.version}
33
+ • Address: ${clientsByAddress}
34
+ • Conversations: ${conversations.length}
35
+ • Installations: ${inboxState.installations.length}
36
+ • InstallationId: ${installationId}
37
+ • Key Package created: ${createdDate.toLocaleString()}
38
+ • Key Package valid until: ${expiryDate.toLocaleString()}
39
+ • Networks: ${environments}
40
+ ${urls.map((url) => `• URL: ${url}`).join("\n")}`);
41
+ };
42
+ /**
43
+ * Returns a URL to test your agent on https://xmtp.chat/ (for development purposes only).
44
+ *
45
+ * @param agent - Your agent instance
46
+ * @returns The URL to test your agent with
47
+ */
48
+ export const getTestUrl = (agent) => {
49
+ const address = agent.client.accountIdentifier?.identifier;
50
+ const env = agent.client.options?.env ?? "dev";
51
+ return `http://xmtp.chat/dm/${address}?env=${env}`;
52
+ };
53
+ //# sourceMappingURL=debug.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debug.js","sourceRoot":"","sources":["../../src/utils/debug.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAGxC,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAC7B,MAA4B,EAC5B,EAAE;IACF,MAAM,IAAI,GAAG;;;;;;;UAOL,CAAC;IAET,MAAM,gBAAgB,GAAG,MAAM,CAAC,iBAAiB,EAAE,UAAU,CAAC;IAC9D,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAC/B,MAAM,cAAc,GAAG,MAAM,CAAC,cAAc,CAAC;IAC7C,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,KAAK,CAAC;IAElD,MAAM,IAAI,GAAG,CAAC,uBAAuB,gBAAgB,EAAE,CAAC,CAAC;IAEzD,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC;IACxD,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC;IACzD,MAAM,kBAAkB,GACtB,MAAM,MAAM,CAAC,uCAAuC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC;IAEzE,IAAI,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC;IAC7B,IAAI,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC;IAE5B,2DAA2D;IAC3D,MAAM,gBAAgB,GAAG,kBAAkB,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;IAClE,IAAI,gBAAgB,CAAC,QAAQ,EAAE,CAAC;QAC9B,WAAW,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC;QAC3E,UAAU,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,CAAC,GAAG,CAAC;MACR,IAAI;;;iBAGO,OAAO;iBACP,MAAM,CAAC,OAAO;iBACd,gBAAgB;uBACV,aAAa,CAAC,MAAM;uBACpB,UAAU,CAAC,aAAa,CAAC,MAAM;wBAC9B,cAAc;6BACT,WAAW,CAAC,cAAc,EAAE;iCACxB,UAAU,CAAC,cAAc,EAAE;kBAC1C,YAAY;MACxB,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,UAAU,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACvD,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAe,KAA0B,EAAE,EAAE;IACrE,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,iBAAiB,EAAE,UAAU,CAAC;IAC3D,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,KAAK,CAAC;IAC/C,OAAO,uBAAuB,OAAO,QAAQ,GAAG,EAAE,CAAC;AACrD,CAAC,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import type { Client, DecodedMessage } from "@xmtp/node-sdk";
2
- import type { AgentContext } from "@/core/AgentContext.js";
2
+ import type { AgentContext } from "../core/AgentContext.js";
3
3
  /**
4
4
  * Function type for filtering messages based on content and client state.
5
5
  */
@@ -11,6 +11,13 @@ export type MessageFilter<ContentTypes> = (message: DecodedMessage, client: Clie
11
11
  * @returns Filter function
12
12
  */
13
13
  declare function fromSender<ContentTypes>(senderInboxId: string | string[]): MessageFilter<ContentTypes>;
14
+ /**
15
+ * Creates a filter that matches text messages starting with a specific string.
16
+ *
17
+ * @param prefix - The string prefix to match against
18
+ * @returns Filter function
19
+ */
20
+ declare function startsWith<ContentTypes>(prefix: string): MessageFilter<ContentTypes>;
14
21
  /**
15
22
  * Creates a filter that requires all provided filters to pass
16
23
  *
@@ -40,6 +47,17 @@ export declare const filter: {
40
47
  fromSelf: MessageFilter<unknown>;
41
48
  textOnly: MessageFilter<unknown>;
42
49
  fromSender: typeof fromSender;
50
+ startsWith: typeof startsWith;
51
+ and: typeof and;
52
+ or: typeof or;
53
+ not: typeof not;
54
+ };
55
+ export declare const f: {
56
+ notFromSelf: MessageFilter<unknown>;
57
+ fromSelf: MessageFilter<unknown>;
58
+ textOnly: MessageFilter<unknown>;
59
+ fromSender: typeof fromSender;
60
+ startsWith: typeof startsWith;
43
61
  and: typeof and;
44
62
  or: typeof or;
45
63
  not: typeof not;
@@ -1,4 +1,5 @@
1
1
  import { ContentTypeText } from "@xmtp/content-type-text";
2
+ import { getTextContent } from "./message.js";
2
3
  /**
3
4
  * Creates a filter that excludes messages from the agent itself.
4
5
  *
@@ -43,6 +44,18 @@ function fromSender(senderInboxId) {
43
44
  return senderIds.includes(message.senderInboxId);
44
45
  };
45
46
  }
47
+ /**
48
+ * Creates a filter that matches text messages starting with a specific string.
49
+ *
50
+ * @param prefix - The string prefix to match against
51
+ * @returns Filter function
52
+ */
53
+ function startsWith(prefix) {
54
+ return (message) => {
55
+ const text = getTextContent(message);
56
+ return !!(text && text.startsWith(prefix));
57
+ };
58
+ }
46
59
  /**
47
60
  * Creates a filter that requires all provided filters to pass
48
61
  *
@@ -96,11 +109,13 @@ export const filter = {
96
109
  textOnly: textOnly(),
97
110
  // factory functions
98
111
  fromSender,
112
+ startsWith,
99
113
  // combinators
100
114
  and,
101
115
  or,
102
116
  not,
103
117
  };
118
+ export const f = filter;
104
119
  export const withFilter = (filter, listener) => (ctx) => {
105
120
  if (filter(ctx.message, ctx.client)) {
106
121
  listener(ctx);
@@ -1 +1 @@
1
- {"version":3,"file":"filter.js","sourceRoot":"","sources":["../../src/utils/filter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAY1D;;;;GAIG;AACH,SAAS,WAAW;IAClB,OAAO,CAAC,OAAuB,EAAE,MAA4B,EAAE,EAAE;QAC/D,OAAO,OAAO,CAAC,aAAa,KAAK,MAAM,CAAC,OAAO,CAAC;IAClD,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,QAAQ;IACf,OAAO,CAAC,OAAuB,EAAE,MAA4B,EAAE,EAAE;QAC/D,OAAO,OAAO,CAAC,aAAa,KAAK,MAAM,CAAC,OAAO,CAAC;IAClD,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,QAAQ;IACf,OAAO,CAAC,OAAuB,EAAE,EAAE;QACjC,OAAO,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;IACxD,CAAC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,UAAU,CACjB,aAAgC;IAEhC,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC;QAC5C,CAAC,CAAC,aAAa;QACf,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC;IAEpB,OAAO,CAAC,OAAuB,EAAE,EAAE;QACjC,OAAO,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IACnD,CAAC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,GAAG,CACV,GAAG,OAAsC;IAEzC,OAAO,CAAC,OAAuB,EAAE,MAA4B,EAAE,EAAE;QAC/D,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YACvC,IAAI,CAAC,MAAM;gBAAE,OAAO,KAAK,CAAC;QAC5B,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,EAAE,CACT,GAAG,OAAsC;IAEzC,OAAO,CAAC,OAAuB,EAAE,MAA4B,EAAE,EAAE;QAC/D,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YACvC,IAAI,MAAM;gBAAE,OAAO,IAAI,CAAC;QAC1B,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,GAAG,CACV,MAAmC;IAEnC,OAAO,CAAC,OAAuB,EAAE,MAA4B,EAAE,EAAE;QAC/D,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAClC,CAAC,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,gBAAgB;IAChB,WAAW,EAAE,WAAW,EAAE;IAC1B,QAAQ,EAAE,QAAQ,EAAE;IACpB,QAAQ,EAAE,QAAQ,EAAE;IACpB,oBAAoB;IACpB,UAAU;IACV,cAAc;IACd,GAAG;IACH,EAAE;IACF,GAAG;CACJ,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GACrB,CACE,MAAmC,EACnC,QAAmD,EACnD,EAAE,CACJ,CAAC,GAA+B,EAAE,EAAE;IAClC,IAAI,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QACpC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAChB,CAAC;AACH,CAAC,CAAC"}
1
+ {"version":3,"file":"filter.js","sourceRoot":"","sources":["../../src/utils/filter.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,4 @@
1
+ export * from "./crypto.js";
1
2
  export * from "./filter.js";
3
+ export * from "./message.js";
2
4
  export * from "./user.js";
@@ -1,3 +1,5 @@
1
+ export * from "./crypto.js";
1
2
  export * from "./filter.js";
3
+ export * from "./message.js";
2
4
  export * from "./user.js";
3
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC"}
@@ -0,0 +1,35 @@
1
+ import { type Reaction } from "@xmtp/content-type-reaction";
2
+ import { type RemoteAttachment } from "@xmtp/content-type-remote-attachment";
3
+ import { type Reply } from "@xmtp/content-type-reply";
4
+ import type { DecodedMessage } from "@xmtp/node-sdk";
5
+ type ContentMessage = Pick<DecodedMessage, "content" | "contentType" | "fallback">;
6
+ export declare const isReaction: <M extends ContentMessage>(m: M) => m is M & {
7
+ content: Reaction;
8
+ };
9
+ export declare const isReply: <M extends ContentMessage>(m: M) => m is M & {
10
+ content: Reply;
11
+ };
12
+ export declare const isTextReply: <M extends ContentMessage>(m: M) => m is M & {
13
+ content: Reply & {
14
+ content: string;
15
+ };
16
+ };
17
+ export declare const isText: <M extends ContentMessage>(m: M) => m is M & {
18
+ content: string;
19
+ };
20
+ export declare const isRemoteAttachment: <M extends ContentMessage>(m: M) => m is M & {
21
+ content: RemoteAttachment;
22
+ };
23
+ /**
24
+ * Extracts the text content from various XMTP message types.
25
+ *
26
+ * Supports extracting text from:
27
+ * - Text messages: Returns the direct string content
28
+ * - Text replies: Returns the reply content text
29
+ * - Reactions: Returns the reaction content (emoji/text)
30
+ *
31
+ * @param message - The decoded message to extract text from
32
+ * @returns The text content as a string, or `undefined` for message types that don't contain extractable text content
33
+ */
34
+ export declare const getTextContent: (message: ContentMessage) => string | undefined;
35
+ export {};
@@ -0,0 +1,30 @@
1
+ import { ContentTypeReaction, } from "@xmtp/content-type-reaction";
2
+ import { ContentTypeRemoteAttachment, } from "@xmtp/content-type-remote-attachment";
3
+ import { ContentTypeReply } from "@xmtp/content-type-reply";
4
+ import { ContentTypeText } from "@xmtp/content-type-text";
5
+ export const isReaction = (m) => !!m.contentType?.sameAs(ContentTypeReaction);
6
+ export const isReply = (m) => !!m.contentType?.sameAs(ContentTypeReply);
7
+ export const isTextReply = (m) => isReply(m) && typeof m.content.content === "string";
8
+ export const isText = (m) => !!m.contentType?.sameAs(ContentTypeText);
9
+ export const isRemoteAttachment = (m) => !!m.contentType?.sameAs(ContentTypeRemoteAttachment);
10
+ /**
11
+ * Extracts the text content from various XMTP message types.
12
+ *
13
+ * Supports extracting text from:
14
+ * - Text messages: Returns the direct string content
15
+ * - Text replies: Returns the reply content text
16
+ * - Reactions: Returns the reaction content (emoji/text)
17
+ *
18
+ * @param message - The decoded message to extract text from
19
+ * @returns The text content as a string, or `undefined` for message types that don't contain extractable text content
20
+ */
21
+ export const getTextContent = (message) => {
22
+ switch (true) {
23
+ case isReaction(message):
24
+ case isTextReply(message):
25
+ return message.content.content;
26
+ case isText(message):
27
+ return message.content;
28
+ }
29
+ };
30
+ //# sourceMappingURL=message.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message.js","sourceRoot":"","sources":["../../src/utils/message.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,GAEpB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,2BAA2B,GAE5B,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAc,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAQ1D,MAAM,CAAC,MAAM,UAAU,GAAG,CACxB,CAAI,EAC4B,EAAE,CAClC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,MAAM,CAAC,mBAAmB,CAAC,CAAC;AAE/C,MAAM,CAAC,MAAM,OAAO,GAAG,CACrB,CAAI,EACyB,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,MAAM,CAAC,gBAAgB,CAAC,CAAC;AAE5E,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,CAAI,EAC+C,EAAE,CACrD,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,OAAO,CAAC,OAAO,KAAK,QAAQ,CAAC;AAEtD,MAAM,CAAC,MAAM,MAAM,GAAG,CACpB,CAAI,EAC0B,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;AAE5E,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,CAAI,EACoC,EAAE,CAC1C,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,MAAM,CAAC,2BAA2B,CAAC,CAAC;AAEvD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAuB,EAAE,EAAE;IACxD,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,UAAU,CAAC,OAAO,CAAC,CAAC;QACzB,KAAK,WAAW,CAAC,OAAO,CAAC;YACvB,OAAO,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;QACjC,KAAK,MAAM,CAAC,OAAO,CAAC;YAClB,OAAO,OAAO,CAAC,OAAO,CAAC;IAC3B,CAAC;AACH,CAAC,CAAC"}
package/package.json CHANGED
@@ -4,8 +4,13 @@
4
4
  "url": "https://github.com/xmtp/xmtp-js/issues"
5
5
  },
6
6
  "dependencies": {
7
- "@xmtp/content-type-reply": "^2.0.2",
8
- "@xmtp/node-sdk": "^4.0.3",
7
+ "@noble/curves": "^2.0.0",
8
+ "@xmtp/content-type-reaction": "workspace:^",
9
+ "@xmtp/content-type-remote-attachment": "workspace:^",
10
+ "@xmtp/content-type-reply": "workspace:^",
11
+ "@xmtp/content-type-text": "workspace:^",
12
+ "@xmtp/node-sdk": "workspace:^",
13
+ "uint8arrays": "^5.1.0",
9
14
  "viem": "^2.31.7"
10
15
  },
11
16
  "description": "XMTP Agent SDK for interacting with XMTP networks",
@@ -13,6 +18,7 @@
13
18
  "@arethetypeswrong/cli": "^0.18.2",
14
19
  "@types/node": "^22.16.3",
15
20
  "@vitest/coverage-v8": "^3.2.4",
21
+ "tsc-alias": "^1.8.16",
16
22
  "tsx": "^4.20.3",
17
23
  "typescript": "^5.8.3",
18
24
  "vite": "^7.0.4",
@@ -53,15 +59,17 @@
53
59
  "url": "git+https://git@github.com/xmtp/xmtp-js.git"
54
60
  },
55
61
  "scripts": {
56
- "build": "yarn clean:dist && tsc -p tsconfig.build.json && attw --pack . --ignore-rules cjs-resolves-to-esm",
62
+ "build": "yarn clean:dist && tsc -p tsconfig.build.json && tsc-alias && attw --pack . --ignore-rules cjs-resolves-to-esm",
57
63
  "clean": "rm -rf .turbo && rm -rf node_modules && yarn clean:dist",
58
64
  "clean:dist": "rm -rf dist",
65
+ "demo": "tsx src/demo.ts",
59
66
  "dev": "tsx --watch src/main.ts",
60
- "start": "tsx src/demo.ts",
61
- "test": "vitest --typecheck",
67
+ "gen:keys": "tsx src/bin/generateKeys.ts",
68
+ "start": "yarn demo",
69
+ "test": "yarn typecheck && vitest --typecheck",
62
70
  "test:cov": "vitest run --coverage",
63
71
  "typecheck": "tsc --noEmit"
64
72
  },
65
73
  "type": "module",
66
- "version": "0.0.3"
74
+ "version": "0.0.5"
67
75
  }