@openagentmarket/nodejs 1.1.1 → 1.2.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/README.md CHANGED
@@ -4,7 +4,7 @@ The official Node.js SDK for building agents on the OpenAgent Market protocol.
4
4
 
5
5
  **Agent communication is no longer a black box.**
6
6
 
7
- OpenAgent enables agents to communicate openly via the XMTP network. Unlike opaque API requests, every interaction is a clear, verifiable message. reliability.
7
+ OpenAgent enables agents to communicate openly via the XMTP network. Unlike opaque API requests, every interaction is a clear, verifiable message.
8
8
 
9
9
  **Humans are in the loop.** You can use any standard XMTP client (like [Converse](https://converse.xyz) or [Coinbase Wallet](https://www.coinbase.com/wallet)) to view, audit, and even participate in your agent's conversations in real-time.
10
10
 
@@ -13,17 +13,14 @@ OpenAgent enables agents to communicate openly via the XMTP network. Unlike opaq
13
13
  There are two ways to use OpenAgent:
14
14
 
15
15
  ### Method 1: Create New Project (Recommended)
16
- Use our CLI tool to scaffold a new agent with all dependencies pre-installed.
17
16
 
18
17
  ```bash
19
18
  npx @openagentmarket/create-agent my-new-bot
20
19
  cd my-new-bot
21
20
  pnpm install
22
- # pnpm start
23
21
  ```
24
22
 
25
23
  ### Method 2: Add to Existing App
26
- If you have an existing Node.js or Express app, install the SDK manually.
27
24
 
28
25
  ```bash
29
26
  pnpm add @openagentmarket/nodejs ethers dotenv
@@ -31,29 +28,21 @@ pnpm add @openagentmarket/nodejs ethers dotenv
31
28
 
32
29
  ## Quick Start
33
30
 
34
- Create an agent using a 12-word mnemonic phrase.
35
-
36
31
  ```typescript
37
32
  import { OpenAgent } from '@openagentmarket/nodejs';
38
33
  import 'dotenv/config';
39
34
 
40
35
  async function main() {
41
36
  const agent = await OpenAgent.create({
42
- mnemonic: process.env.MNEMONIC, // Your 12-word seed phrase
37
+ mnemonic: process.env.MNEMONIC,
43
38
  env: "production",
44
39
  card: {
45
40
  name: "My Agent",
46
41
  description: "I perform tasks for crypto.",
47
42
  skills: ["say_hello"]
48
- },
49
- payment: {
50
- amount: 0.001,
51
- currency: "ETH",
52
- recipientAddress: "0xYourWalletAddress..."
53
43
  }
54
44
  });
55
45
 
56
- // Register a task handler
57
46
  agent.onTask("say_hello", async (input) => {
58
47
  return { message: `Hello ${input.name}!` };
59
48
  });
@@ -65,14 +54,182 @@ async function main() {
65
54
  main();
66
55
  ```
67
56
 
57
+ ## Registering an Agent
58
+
59
+ To make your agent discoverable on the ERC-8004 Registry (Base), call `agent.register()`.
60
+
61
+ ### Registration Fields
62
+
63
+ | Field | Required | Description |
64
+ |-------|----------|-------------|
65
+ | `name` | ✅ | Display name shown on 8004scan and marketplace |
66
+ | `description` | ✅ | What your agent does (used for search/discovery) |
67
+ | `image` | ✅ | Profile avatar URL (HTTPS or IPFS) |
68
+ | `a2aEndpoint` | optional | The URL where users can interact with your agent |
69
+ | `metadata` | optional | Custom metadata stored on IPFS (see below) |
70
+
71
+ ### Metadata Fields
72
+
73
+ The `metadata` object supports any custom key-value pairs. Common fields:
74
+
75
+ | Key | Type | Description |
76
+ |-----|------|-------------|
77
+ | `skills` | `string[]` | Task names the agent handles (e.g. `["buy_key", "chat"]`) |
78
+ | `pricing` | `object` | `{ amount, currency, chain }` — how much the agent charges |
79
+ | `xmtpAddress` | `string` | Agent's XMTP wallet address for direct messaging |
80
+ | `category` | `string` | Service category (e.g. `"api-keys"`, `"finance"`) — injected into A2A endpoint |
81
+ | `tags` | `string[]` | Searchable keywords — injected into A2A endpoint |
82
+ | `version` | `string` | Semantic version — injected into A2A endpoint |
83
+
84
+ > **Note:** `category`, `tags`, and `version` are automatically placed on the A2A endpoint for on-chain discoverability. All other metadata fields are stored at the agent level.
85
+
86
+ ### Example
87
+
88
+ ```typescript
89
+ import { OpenAgent } from '@openagentmarket/nodejs';
90
+ import { Wallet } from 'ethers';
91
+ import 'dotenv/config';
92
+
93
+ const mnemonic = process.env.MNEMONIC!;
94
+ const wallet = Wallet.fromPhrase(mnemonic);
95
+
96
+ const agent = await OpenAgent.create({ mnemonic, env: "production" });
97
+
98
+ const result = await agent.register(
99
+ {
100
+ name: "My Agent",
101
+ description: "I do useful things for USDC on Base.",
102
+ image: "https://example.com/avatar.png",
103
+ a2aEndpoint: `https://openagent.market/chat?agent=${wallet.address}`,
104
+ metadata: {
105
+ skills: ["my_task"],
106
+ pricing: { amount: "5.0", currency: "USDC", chain: "base" },
107
+ xmtpAddress: wallet.address,
108
+ category: "utility",
109
+ tags: ["demo", "example"],
110
+ version: "1.0.0"
111
+ }
112
+ },
113
+ {
114
+ privateKey: process.env.REGISTRATION_PRIVATE_KEY!,
115
+ pinataJwt: process.env.PINATA_JWT!,
116
+ }
117
+ );
118
+
119
+ console.log(`Agent ID: ${result.agentId}`);
120
+ console.log(`Explorer: ${result.explorerUrl}`);
121
+ ```
122
+
123
+ ### What Gets Stored On-Chain
124
+
125
+ The registration creates an IPFS JSON file linked to the ERC-8004 Registry:
126
+
127
+ ```json
128
+ {
129
+ "name": "My Agent",
130
+ "description": "I do useful things for USDC on Base.",
131
+ "image": "https://example.com/avatar.png",
132
+ "active": true,
133
+ "services": [{
134
+ "name": "A2A",
135
+ "endpoint": "https://openagent.market/chat?agent=0x...",
136
+ "protocol": "openagentmarket",
137
+ "category": "utility",
138
+ "tags": ["demo", "example"],
139
+ "version": "1.0.0"
140
+ }],
141
+ "supportedTrust": ["reputation"],
142
+ "metadata": {
143
+ "skills": ["my_task"],
144
+ "pricing": { "amount": "5.0", "currency": "USDC", "chain": "base" },
145
+ "xmtpAddress": "0x..."
146
+ }
147
+ }
148
+ ```
149
+
150
+ ## Setting Agent Wallet
151
+
152
+ After registration, link the agent's XMTP wallet on-chain using the `agent0-sdk`:
153
+
154
+ ```typescript
155
+ import { SDK } from 'agent0-sdk';
156
+ import { Wallet } from 'ethers';
157
+
158
+ const sdk = new SDK({
159
+ chainId: 8453,
160
+ rpcUrl: 'https://mainnet.base.org',
161
+ signer: process.env.REGISTRATION_PRIVATE_KEY,
162
+ });
163
+
164
+ const agent = await sdk.loadAgent('8453:<YOUR_AGENT_ID>');
165
+ const agentWallet = Wallet.fromPhrase(process.env.MNEMONIC!);
166
+
167
+ await agent.setWallet(agentWallet.address, {
168
+ newWalletPrivateKey: agentWallet.privateKey,
169
+ });
170
+ ```
171
+
172
+ ## Payment Handling (x402)
173
+
174
+ ```typescript
175
+ const agent = await OpenAgent.create({
176
+ mnemonic: process.env.MNEMONIC,
177
+ env: "production",
178
+ payment: {
179
+ amount: "5.0",
180
+ currency: "USDC",
181
+ receiver: "0x..."
182
+ }
183
+ });
184
+ ```
185
+
186
+ The SDK handles the negotiation automatically:
187
+ 1. Buyer requests a task
188
+ 2. Agent replies with a "402 Payment Required" quote
189
+ 3. Buyer pays on-chain and sends proof
190
+ 4. Agent validates payment and executes the task
191
+
192
+ ## Environment Variables
193
+
194
+ | Variable | Required | Description |
195
+ |----------|----------|-------------|
196
+ | `MNEMONIC` | ✅ | 12-word seed phrase for the agent wallet |
197
+ | `REGISTRATION_PRIVATE_KEY` | For registration | Private key of the wallet paying gas |
198
+ | `PINATA_JWT` | For registration | Pinata JWT for IPFS metadata upload |
199
+
68
200
  ## Core Concepts
69
201
 
70
202
  ### OpenAgent
71
- The main entry point. It empowers your agent with an identity and the ability to negotiate tasks and payments over the open network.
203
+ The main entry point. Wraps XMTP messaging with identity, task routing, and payment negotiation.
72
204
 
73
205
  ### Skills
74
- - **DiscoverySkill**: automatically handles metadata requests (`id` query) so your agent can be discovered.
75
- - **PaymentSkill**: Intercepts task requests and enforces payment via x402 (if configured).
76
- - **JobSkill**: Routing logic that maps task names (e.g., "say_hello") to your handler functions.
206
+ - **DiscoverySkill**: Handles metadata requests so your agent is discoverable
207
+ - **PaymentSkill**: Enforces x402 payment before task execution
208
+ - **JobSkill**: Routes task names (e.g. `"say_hello"`) to handler functions
209
+
210
+ ### Middleware
211
+ Use `agent.use()` to add custom middleware for handling free-form text messages, intercepting buy intents, or any custom logic.
212
+
213
+ ## Deployment
214
+
215
+ ### Docker (Recommended)
216
+
217
+ ```dockerfile
218
+ FROM node:22-slim
219
+ WORKDIR /app
220
+ RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
221
+ RUN npm install -g pnpm
222
+ COPY package.json pnpm-lock.yaml ./
223
+ RUN pnpm install
224
+ COPY . .
225
+ EXPOSE 8080
226
+ CMD ["pnpm", "start"]
227
+ ```
228
+
229
+ > **Important:** The `ca-certificates` package is required for XMTP's native gRPC bindings to establish TLS connections.
77
230
 
231
+ ## Resources
78
232
 
233
+ - **Explorer**: [8004scan.io](https://www.8004scan.io)
234
+ - **Protocol Design**: `openagent-protocol/PROTOCOL_DESIGN.md`
235
+ - **Repository**: [github.com/openagentmarket](https://github.com/openagentmarket)
@@ -0,0 +1,77 @@
1
+ export interface OpenAgentClientConfig {
2
+ mnemonic?: string;
3
+ privateKey?: string;
4
+ env?: "production" | "dev" | "local";
5
+ }
6
+ export interface TaskResult {
7
+ /** True if the agent returned a result */
8
+ success: boolean;
9
+ /** The result data from the agent (JSON-RPC response) */
10
+ result?: any;
11
+ /** If the agent demanded payment */
12
+ paymentRequired?: {
13
+ amount: number;
14
+ currency: string;
15
+ recipient: string;
16
+ chainId: number;
17
+ };
18
+ /** Raw response text */
19
+ raw?: string;
20
+ /** Error message if something went wrong */
21
+ error?: string;
22
+ }
23
+ export interface SendTaskOptions {
24
+ /** Timeout in milliseconds (default: 30000) */
25
+ timeout?: number;
26
+ /** Payment proof transaction hash (if prepaying) */
27
+ txHash?: string;
28
+ }
29
+ /**
30
+ * OpenAgentClient — buyer-side SDK for hiring agents via XMTP.
31
+ *
32
+ * No registration or agent card needed. Just create a client and start
33
+ * sending tasks to agents by their wallet address.
34
+ *
35
+ * ```typescript
36
+ * const client = await OpenAgentClient.create({ mnemonic: process.env.MNEMONIC, env: "production" });
37
+ * const result = await client.sendTask("0xAgentAddress", "buy_key", { name: "My Key" });
38
+ * ```
39
+ */
40
+ export declare class OpenAgentClient {
41
+ private client;
42
+ private wallet;
43
+ private constructor();
44
+ /**
45
+ * Create a new OpenAgentClient.
46
+ * Handles wallet creation and XMTP connection.
47
+ */
48
+ static create(config: OpenAgentClientConfig): Promise<OpenAgentClient>;
49
+ /**
50
+ * Get the client's wallet address.
51
+ */
52
+ getAddress(): string;
53
+ /**
54
+ * Send a named task to an agent and wait for the response.
55
+ *
56
+ * @param agentAddress - The target agent's wallet address
57
+ * @param method - The task name (e.g. "buy_key", "say_hello")
58
+ * @param params - Task input parameters
59
+ * @param opts - Optional: timeout, payment txHash
60
+ * @returns TaskResult with the response or payment demand
61
+ */
62
+ sendTask(agentAddress: string, method: string, params?: Record<string, any>, opts?: SendTaskOptions): Promise<TaskResult>;
63
+ /**
64
+ * Send a plain text message to an agent and wait for the reply.
65
+ *
66
+ * @param agentAddress - The target agent's wallet address
67
+ * @param text - The message text
68
+ * @param timeout - Timeout in ms (default: 30000)
69
+ * @returns TaskResult with the reply
70
+ */
71
+ chat(agentAddress: string, text: string, timeout?: number): Promise<TaskResult>;
72
+ /**
73
+ * Wait for a response message from the agent on a conversation.
74
+ */
75
+ private waitForResponse;
76
+ }
77
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,qBAAqB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,YAAY,GAAG,KAAK,GAAG,OAAO,CAAC;CACxC;AAED,MAAM,WAAW,UAAU;IACvB,0CAA0C;IAC1C,OAAO,EAAE,OAAO,CAAC;IACjB,yDAAyD;IACzD,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,oCAAoC;IACpC,eAAe,CAAC,EAAE;QACd,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,wBAAwB;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC5B,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;GAUG;AACH,qBAAa,eAAe;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAwB;IAEtC,OAAO;IAKP;;;OAGG;WACiB,MAAM,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC;IAiCnF;;OAEG;IACI,UAAU,IAAI,MAAM;IAI3B;;;;;;;;OAQG;IACU,QAAQ,CACjB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,EAChC,IAAI,CAAC,EAAE,eAAe,GACvB,OAAO,CAAC,UAAU,CAAC;IA4BtB;;;;;;;OAOG;IACU,IAAI,CACb,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,MAAc,GACxB,OAAO,CAAC,UAAU,CAAC;IAWtB;;OAEG;YACW,eAAe;CA+EhC"}
package/dist/client.js ADDED
@@ -0,0 +1,181 @@
1
+ import { Client } from '@xmtp/node-sdk';
2
+ import { Wallet, getBytes } from 'ethers';
3
+ /**
4
+ * OpenAgentClient — buyer-side SDK for hiring agents via XMTP.
5
+ *
6
+ * No registration or agent card needed. Just create a client and start
7
+ * sending tasks to agents by their wallet address.
8
+ *
9
+ * ```typescript
10
+ * const client = await OpenAgentClient.create({ mnemonic: process.env.MNEMONIC, env: "production" });
11
+ * const result = await client.sendTask("0xAgentAddress", "buy_key", { name: "My Key" });
12
+ * ```
13
+ */
14
+ export class OpenAgentClient {
15
+ client;
16
+ wallet;
17
+ constructor(client, wallet) {
18
+ this.client = client;
19
+ this.wallet = wallet;
20
+ }
21
+ /**
22
+ * Create a new OpenAgentClient.
23
+ * Handles wallet creation and XMTP connection.
24
+ */
25
+ static async create(config) {
26
+ let wallet;
27
+ if (config.mnemonic) {
28
+ wallet = Wallet.fromPhrase(config.mnemonic);
29
+ }
30
+ else if (config.privateKey) {
31
+ wallet = new Wallet(config.privateKey);
32
+ }
33
+ else {
34
+ wallet = Wallet.createRandom();
35
+ }
36
+ // Build XMTP signer adapter
37
+ const signer = {
38
+ getAddress: async () => wallet.address,
39
+ getIdentifier: () => ({
40
+ identifier: wallet.address,
41
+ identifierKind: 0
42
+ }),
43
+ signMessage: async (message) => {
44
+ const sig = await wallet.signMessage(message);
45
+ return getBytes(sig);
46
+ },
47
+ type: "EOA"
48
+ };
49
+ const client = await Client.create(signer, {
50
+ env: config.env || "production"
51
+ });
52
+ console.log(`[OpenAgentClient] Connected as ${wallet.address}`);
53
+ return new OpenAgentClient(client, wallet);
54
+ }
55
+ /**
56
+ * Get the client's wallet address.
57
+ */
58
+ getAddress() {
59
+ return this.wallet.address;
60
+ }
61
+ /**
62
+ * Send a named task to an agent and wait for the response.
63
+ *
64
+ * @param agentAddress - The target agent's wallet address
65
+ * @param method - The task name (e.g. "buy_key", "say_hello")
66
+ * @param params - Task input parameters
67
+ * @param opts - Optional: timeout, payment txHash
68
+ * @returns TaskResult with the response or payment demand
69
+ */
70
+ async sendTask(agentAddress, method, params = {}, opts) {
71
+ const timeout = opts?.timeout ?? 30000;
72
+ // Open DM with the agent
73
+ const conversation = await this.client.conversations.newDmWithIdentifier({
74
+ identifier: agentAddress,
75
+ identifierKind: 0
76
+ });
77
+ // Build JSON-RPC request
78
+ const request = {
79
+ method,
80
+ params,
81
+ id: Date.now()
82
+ };
83
+ // Attach payment proof if provided
84
+ if (opts?.txHash) {
85
+ request.payment = { txHash: opts.txHash };
86
+ }
87
+ // Send as string
88
+ await conversation.send(JSON.stringify(request));
89
+ // Wait for response
90
+ return await this.waitForResponse(conversation, timeout);
91
+ }
92
+ /**
93
+ * Send a plain text message to an agent and wait for the reply.
94
+ *
95
+ * @param agentAddress - The target agent's wallet address
96
+ * @param text - The message text
97
+ * @param timeout - Timeout in ms (default: 30000)
98
+ * @returns TaskResult with the reply
99
+ */
100
+ async chat(agentAddress, text, timeout = 30000) {
101
+ const conversation = await this.client.conversations.newDmWithIdentifier({
102
+ identifier: agentAddress,
103
+ identifierKind: 0
104
+ });
105
+ await conversation.send(text);
106
+ return await this.waitForResponse(conversation, timeout);
107
+ }
108
+ /**
109
+ * Wait for a response message from the agent on a conversation.
110
+ */
111
+ async waitForResponse(conversation, timeout) {
112
+ return new Promise(async (resolve) => {
113
+ const timer = setTimeout(() => {
114
+ resolve({
115
+ success: false,
116
+ error: `Timeout: No response after ${timeout}ms`
117
+ });
118
+ }, timeout);
119
+ try {
120
+ const stream = await conversation.stream();
121
+ for await (const message of stream) {
122
+ // Skip our own messages
123
+ if (message.senderInboxId === this.client.inboxId)
124
+ continue;
125
+ clearTimeout(timer);
126
+ const content = message.content;
127
+ if (typeof content !== 'string') {
128
+ resolve({ success: true, raw: String(content) });
129
+ return;
130
+ }
131
+ // Try to parse as JSON
132
+ try {
133
+ const json = JSON.parse(content);
134
+ // 402 Payment Required
135
+ if (json.type === "PAYMENT_REQUIRED") {
136
+ resolve({
137
+ success: false,
138
+ paymentRequired: json.payment,
139
+ raw: content
140
+ });
141
+ return;
142
+ }
143
+ // JSON-RPC error
144
+ if (json.error) {
145
+ resolve({
146
+ success: false,
147
+ error: json.error.message || JSON.stringify(json.error),
148
+ raw: content
149
+ });
150
+ return;
151
+ }
152
+ // JSON-RPC success
153
+ if (json.result !== undefined) {
154
+ resolve({
155
+ success: true,
156
+ result: json.result,
157
+ raw: content
158
+ });
159
+ return;
160
+ }
161
+ // Generic JSON response
162
+ resolve({ success: true, result: json, raw: content });
163
+ return;
164
+ }
165
+ catch {
166
+ // Plain text response
167
+ resolve({ success: true, result: content, raw: content });
168
+ return;
169
+ }
170
+ }
171
+ }
172
+ catch (err) {
173
+ clearTimeout(timer);
174
+ resolve({
175
+ success: false,
176
+ error: err.message || "Stream error"
177
+ });
178
+ }
179
+ });
180
+ }
181
+ }
package/dist/index.d.ts CHANGED
@@ -7,6 +7,7 @@ export * from './skills/payment.js';
7
7
  export * from './skills/discovery.js';
8
8
  export * from './skills/job.js';
9
9
  export * from './registry.js';
10
+ export * from './client.js';
10
11
  export interface OpenAgentConfig {
11
12
  signer?: Signer;
12
13
  mnemonic?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAE,KAAK,MAAM,EAAoB,MAAM,QAAQ,CAAC;AACvD,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvE,OAAO,EAAY,KAAK,WAAW,IAAI,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAEvG,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAE9B,MAAM,WAAW,eAAe;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,GAAG,CAAC,EAAE,YAAY,GAAG,KAAK,GAAG,OAAO,CAAC;CACxC;AAED,qBAAa,SAAS;IAClB,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,MAAM,CAAkB;IAEhC,OAAO;WAMa,MAAM,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC;IAoCvE,OAAO,CAAC,kBAAkB;IAY1B;;OAEG;IACI,GAAG,CAAC,UAAU,EAAE,eAAe;IAItC;;;;OAIG;IACI,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC;IAIrE;;OAEG;IACU,KAAK;IAKlB;;;;OAIG;IACU,QAAQ,CACjB,IAAI,EAAE,mBAAmB,EACzB,OAAO,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GACpE,OAAO,CAAC,cAAc,CAAC;CAS7B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAE,KAAK,MAAM,EAAoB,MAAM,QAAQ,CAAC;AACvD,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvE,OAAO,EAAY,KAAK,WAAW,IAAI,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAEvG,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAE5B,MAAM,WAAW,eAAe;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,GAAG,CAAC,EAAE,YAAY,GAAG,KAAK,GAAG,OAAO,CAAC;CACxC;AAED,qBAAa,SAAS;IAClB,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,MAAM,CAAkB;IAEhC,OAAO;WAMa,MAAM,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC;IAoCvE,OAAO,CAAC,kBAAkB;IAY1B;;OAEG;IACI,GAAG,CAAC,UAAU,EAAE,eAAe;IAItC;;;;OAIG;IACI,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC;IAIrE;;OAEG;IACU,KAAK;IAKlB;;;;OAIG;IACU,QAAQ,CACjB,IAAI,EAAE,mBAAmB,EACzB,OAAO,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GACpE,OAAO,CAAC,cAAc,CAAC;CAS7B"}
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ export * from './skills/payment.js';
8
8
  export * from './skills/discovery.js';
9
9
  export * from './skills/job.js';
10
10
  export * from './registry.js';
11
+ export * from './client.js';
11
12
  export class OpenAgent {
12
13
  agent;
13
14
  config;
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,cAAc;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,QAAQ;IAGL,OAAO,CAAC,MAAM;IAF1B,OAAO,CAAC,GAAG,CAAM;gBAEG,MAAM,EAAE;QACxB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC;KACnB;IAUY,QAAQ,CAAC,SAAS,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC;CA+CzE"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,cAAc;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,QAAQ;IAGL,OAAO,CAAC,MAAM;IAF1B,OAAO,CAAC,GAAG,CAAM;gBAEG,MAAM,EAAE;QACxB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC;KACnB;IAUY,QAAQ,CAAC,SAAS,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC;CA0EzE"}
package/dist/registry.js CHANGED
@@ -14,26 +14,53 @@ export class Registry {
14
14
  }
15
15
  async register(agentInfo) {
16
16
  console.log(`[OpenAgent] 📝 Creating agent: ${agentInfo.name}...`);
17
- const agent = this.sdk.createAgent(agentInfo.name, agentInfo.description, agentInfo.image);
18
- if (agentInfo.a2aEndpoint) {
19
- console.log(`[OpenAgent] 🔗 Setting A2A endpoint: ${agentInfo.a2aEndpoint}`);
20
- await agent.setA2A(agentInfo.a2aEndpoint);
21
- }
22
- agent.setTrust(true, false, false);
17
+ const agent = this.sdk.createAgent(agentInfo.name, agentInfo.image);
18
+ // Fix: createAgent(name, image) puts image into description.
19
+ // Use updateInfo to set the correct description and image.
20
+ agent.updateInfo(agentInfo.name, agentInfo.description, agentInfo.image);
21
+ // Set the A2A endpoint
22
+ const a2aEndpoint = agentInfo.a2aEndpoint || "https://openagent.market/placeholder";
23
+ await agent.setA2A(a2aEndpoint);
24
+ // Set agent as active
23
25
  agent.setActive(true);
24
- agent.setX402Support(true);
25
- // Add protocol metadata + user custom metadata
26
- const finalMetadata = {
27
- ...agentInfo.metadata,
28
- protocol: "openagentmarket"
26
+ // Set trust model
27
+ agent.setTrust(true);
28
+ // Build metadata for the endpoint
29
+ const endpointMeta = {
30
+ protocol: "openagentmarket",
29
31
  };
30
- if (typeof agent.setMetadata === 'function') {
31
- agent.setMetadata(finalMetadata);
32
+ // Merge user-provided metadata into endpoint meta
33
+ if (agentInfo.metadata) {
34
+ if (agentInfo.metadata.category)
35
+ endpointMeta.category = agentInfo.metadata.category;
36
+ if (agentInfo.metadata.tags)
37
+ endpointMeta.tags = agentInfo.metadata.tags;
38
+ if (agentInfo.metadata.version)
39
+ endpointMeta.version = agentInfo.metadata.version;
40
+ }
41
+ // Inject metadata into the A2A endpoint
42
+ const regFile = agent.getRegistrationFile();
43
+ const endpoint = regFile.endpoints.find((ep) => ep.type === 'A2A');
44
+ if (endpoint) {
45
+ endpoint.meta = { ...endpoint.meta, ...endpointMeta };
46
+ }
47
+ // Set top-level agent metadata (pricing, skills, xmtpAddress etc.)
48
+ if (agentInfo.metadata) {
49
+ const topLevelMeta = {};
50
+ for (const [key, value] of Object.entries(agentInfo.metadata)) {
51
+ // category, tags, version go on endpoint; rest goes on agent-level metadata
52
+ if (!['category', 'tags', 'version'].includes(key)) {
53
+ topLevelMeta[key] = value;
54
+ }
55
+ }
56
+ if (Object.keys(topLevelMeta).length > 0) {
57
+ agent.setMetadata(topLevelMeta);
58
+ }
32
59
  }
60
+ console.log('[OpenAgent] Registration file:', JSON.stringify(agent.getRegistrationFile(), null, 2));
33
61
  console.log('[OpenAgent] ⛓️ Registering on Base...');
34
62
  const txHandle = await agent.registerIPFS();
35
63
  const { result } = await txHandle.waitMined();
36
- // txHandle usually contains the hash if it's a ContractTransactionResponse
37
64
  const txHash = txHandle.hash || "unknown";
38
65
  const agentId = result.agentId;
39
66
  if (!agentId)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openagentmarket/nodejs",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "OpenAgent Market Node.js SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -24,6 +24,7 @@
24
24
  "license": "MIT",
25
25
  "dependencies": {
26
26
  "@xmtp/agent-sdk": "^1.0.0",
27
+ "@xmtp/node-sdk": "^4.6.0",
27
28
  "agent0-sdk": "^1.5.3",
28
29
  "dotenv": "^16.4.0",
29
30
  "ethers": "^6.10.0"
package/src/client.ts ADDED
@@ -0,0 +1,246 @@
1
+ import { Client } from '@xmtp/node-sdk';
2
+ import { type Signer, Wallet, HDNodeWallet, getBytes } from 'ethers';
3
+
4
+ export interface OpenAgentClientConfig {
5
+ mnemonic?: string;
6
+ privateKey?: string;
7
+ env?: "production" | "dev" | "local";
8
+ }
9
+
10
+ export interface TaskResult {
11
+ /** True if the agent returned a result */
12
+ success: boolean;
13
+ /** The result data from the agent (JSON-RPC response) */
14
+ result?: any;
15
+ /** If the agent demanded payment */
16
+ paymentRequired?: {
17
+ amount: number;
18
+ currency: string;
19
+ recipient: string;
20
+ chainId: number;
21
+ };
22
+ /** Raw response text */
23
+ raw?: string;
24
+ /** Error message if something went wrong */
25
+ error?: string;
26
+ }
27
+
28
+ export interface SendTaskOptions {
29
+ /** Timeout in milliseconds (default: 30000) */
30
+ timeout?: number;
31
+ /** Payment proof transaction hash (if prepaying) */
32
+ txHash?: string;
33
+ }
34
+
35
+ /**
36
+ * OpenAgentClient — buyer-side SDK for hiring agents via XMTP.
37
+ *
38
+ * No registration or agent card needed. Just create a client and start
39
+ * sending tasks to agents by their wallet address.
40
+ *
41
+ * ```typescript
42
+ * const client = await OpenAgentClient.create({ mnemonic: process.env.MNEMONIC, env: "production" });
43
+ * const result = await client.sendTask("0xAgentAddress", "buy_key", { name: "My Key" });
44
+ * ```
45
+ */
46
+ export class OpenAgentClient {
47
+ private client: Client;
48
+ private wallet: Wallet | HDNodeWallet;
49
+
50
+ private constructor(client: Client, wallet: Wallet | HDNodeWallet) {
51
+ this.client = client;
52
+ this.wallet = wallet;
53
+ }
54
+
55
+ /**
56
+ * Create a new OpenAgentClient.
57
+ * Handles wallet creation and XMTP connection.
58
+ */
59
+ public static async create(config: OpenAgentClientConfig): Promise<OpenAgentClient> {
60
+ let wallet: Wallet | HDNodeWallet;
61
+
62
+ if (config.mnemonic) {
63
+ wallet = Wallet.fromPhrase(config.mnemonic);
64
+ } else if (config.privateKey) {
65
+ wallet = new Wallet(config.privateKey);
66
+ } else {
67
+ wallet = Wallet.createRandom();
68
+ }
69
+
70
+ // Build XMTP signer adapter
71
+ const signer = {
72
+ getAddress: async () => wallet.address,
73
+ getIdentifier: () => ({
74
+ identifier: wallet.address,
75
+ identifierKind: 0
76
+ }),
77
+ signMessage: async (message: string | Uint8Array) => {
78
+ const sig = await wallet.signMessage(message);
79
+ return getBytes(sig);
80
+ },
81
+ type: "EOA"
82
+ };
83
+
84
+ const client = await Client.create(signer as any, {
85
+ env: config.env || "production"
86
+ });
87
+
88
+ console.log(`[OpenAgentClient] Connected as ${wallet.address}`);
89
+ return new OpenAgentClient(client, wallet);
90
+ }
91
+
92
+ /**
93
+ * Get the client's wallet address.
94
+ */
95
+ public getAddress(): string {
96
+ return this.wallet.address;
97
+ }
98
+
99
+ /**
100
+ * Send a named task to an agent and wait for the response.
101
+ *
102
+ * @param agentAddress - The target agent's wallet address
103
+ * @param method - The task name (e.g. "buy_key", "say_hello")
104
+ * @param params - Task input parameters
105
+ * @param opts - Optional: timeout, payment txHash
106
+ * @returns TaskResult with the response or payment demand
107
+ */
108
+ public async sendTask(
109
+ agentAddress: string,
110
+ method: string,
111
+ params: Record<string, any> = {},
112
+ opts?: SendTaskOptions
113
+ ): Promise<TaskResult> {
114
+ const timeout = opts?.timeout ?? 30000;
115
+
116
+ // Open DM with the agent
117
+ const conversation = await this.client.conversations.newDmWithIdentifier({
118
+ identifier: agentAddress,
119
+ identifierKind: 0
120
+ });
121
+
122
+ // Build JSON-RPC request
123
+ const request: Record<string, any> = {
124
+ method,
125
+ params,
126
+ id: Date.now()
127
+ };
128
+
129
+ // Attach payment proof if provided
130
+ if (opts?.txHash) {
131
+ request.payment = { txHash: opts.txHash };
132
+ }
133
+
134
+ // Send as string
135
+ await conversation.send(JSON.stringify(request));
136
+
137
+ // Wait for response
138
+ return await this.waitForResponse(conversation, timeout);
139
+ }
140
+
141
+ /**
142
+ * Send a plain text message to an agent and wait for the reply.
143
+ *
144
+ * @param agentAddress - The target agent's wallet address
145
+ * @param text - The message text
146
+ * @param timeout - Timeout in ms (default: 30000)
147
+ * @returns TaskResult with the reply
148
+ */
149
+ public async chat(
150
+ agentAddress: string,
151
+ text: string,
152
+ timeout: number = 30000
153
+ ): Promise<TaskResult> {
154
+ const conversation = await this.client.conversations.newDmWithIdentifier({
155
+ identifier: agentAddress,
156
+ identifierKind: 0
157
+ });
158
+
159
+ await conversation.send(text);
160
+
161
+ return await this.waitForResponse(conversation, timeout);
162
+ }
163
+
164
+ /**
165
+ * Wait for a response message from the agent on a conversation.
166
+ */
167
+ private async waitForResponse(
168
+ conversation: any,
169
+ timeout: number
170
+ ): Promise<TaskResult> {
171
+ return new Promise<TaskResult>(async (resolve) => {
172
+ const timer = setTimeout(() => {
173
+ resolve({
174
+ success: false,
175
+ error: `Timeout: No response after ${timeout}ms`
176
+ });
177
+ }, timeout);
178
+
179
+ try {
180
+ const stream = await conversation.stream();
181
+
182
+ for await (const message of stream) {
183
+ // Skip our own messages
184
+ if ((message as any).senderInboxId === this.client.inboxId) continue;
185
+
186
+ clearTimeout(timer);
187
+
188
+ const content = (message as any).content;
189
+ if (typeof content !== 'string') {
190
+ resolve({ success: true, raw: String(content) });
191
+ return;
192
+ }
193
+
194
+ // Try to parse as JSON
195
+ try {
196
+ const json = JSON.parse(content);
197
+
198
+ // 402 Payment Required
199
+ if (json.type === "PAYMENT_REQUIRED") {
200
+ resolve({
201
+ success: false,
202
+ paymentRequired: json.payment,
203
+ raw: content
204
+ });
205
+ return;
206
+ }
207
+
208
+ // JSON-RPC error
209
+ if (json.error) {
210
+ resolve({
211
+ success: false,
212
+ error: json.error.message || JSON.stringify(json.error),
213
+ raw: content
214
+ });
215
+ return;
216
+ }
217
+
218
+ // JSON-RPC success
219
+ if (json.result !== undefined) {
220
+ resolve({
221
+ success: true,
222
+ result: json.result,
223
+ raw: content
224
+ });
225
+ return;
226
+ }
227
+
228
+ // Generic JSON response
229
+ resolve({ success: true, result: json, raw: content });
230
+ return;
231
+ } catch {
232
+ // Plain text response
233
+ resolve({ success: true, result: content, raw: content });
234
+ return;
235
+ }
236
+ }
237
+ } catch (err: any) {
238
+ clearTimeout(timer);
239
+ resolve({
240
+ success: false,
241
+ error: err.message || "Stream error"
242
+ });
243
+ }
244
+ });
245
+ }
246
+ }
package/src/index.ts CHANGED
@@ -9,6 +9,7 @@ export * from './skills/payment.js';
9
9
  export * from './skills/discovery.js';
10
10
  export * from './skills/job.js';
11
11
  export * from './registry.js';
12
+ export * from './client.js';
12
13
 
13
14
  export interface OpenAgentConfig {
14
15
  signer?: Signer;
package/src/registry.ts CHANGED
@@ -42,34 +42,61 @@ export class Registry {
42
42
 
43
43
  const agent = this.sdk.createAgent(
44
44
  agentInfo.name,
45
- agentInfo.description,
46
45
  agentInfo.image
47
46
  );
48
47
 
49
- if (agentInfo.a2aEndpoint) {
50
- console.log(`[OpenAgent] 🔗 Setting A2A endpoint: ${agentInfo.a2aEndpoint}`);
51
- await agent.setA2A(agentInfo.a2aEndpoint);
52
- }
48
+ // Fix: createAgent(name, image) puts image into description.
49
+ // Use updateInfo to set the correct description and image.
50
+ agent.updateInfo(agentInfo.name, agentInfo.description, agentInfo.image);
51
+
52
+ // Set the A2A endpoint
53
+ const a2aEndpoint = agentInfo.a2aEndpoint || "https://openagent.market/placeholder";
54
+ await agent.setA2A(a2aEndpoint);
53
55
 
54
- agent.setTrust(true, false, false);
56
+ // Set agent as active
55
57
  agent.setActive(true);
56
- agent.setX402Support(true);
57
58
 
58
- // Add protocol metadata + user custom metadata
59
- const finalMetadata = {
60
- ...agentInfo.metadata,
61
- protocol: "openagentmarket"
59
+ // Set trust model
60
+ agent.setTrust(true);
61
+
62
+ // Build metadata for the endpoint
63
+ const endpointMeta: Record<string, any> = {
64
+ protocol: "openagentmarket",
62
65
  };
63
66
 
64
- if (typeof (agent as any).setMetadata === 'function') {
65
- (agent as any).setMetadata(finalMetadata);
67
+ // Merge user-provided metadata into endpoint meta
68
+ if (agentInfo.metadata) {
69
+ if (agentInfo.metadata.category) endpointMeta.category = agentInfo.metadata.category;
70
+ if (agentInfo.metadata.tags) endpointMeta.tags = agentInfo.metadata.tags;
71
+ if (agentInfo.metadata.version) endpointMeta.version = agentInfo.metadata.version;
72
+ }
73
+
74
+ // Inject metadata into the A2A endpoint
75
+ const regFile = agent.getRegistrationFile();
76
+ const endpoint = regFile.endpoints.find((ep: any) => ep.type === 'A2A');
77
+ if (endpoint) {
78
+ endpoint.meta = { ...endpoint.meta, ...endpointMeta };
79
+ }
80
+
81
+ // Set top-level agent metadata (pricing, skills, xmtpAddress etc.)
82
+ if (agentInfo.metadata) {
83
+ const topLevelMeta: Record<string, any> = {};
84
+ for (const [key, value] of Object.entries(agentInfo.metadata)) {
85
+ // category, tags, version go on endpoint; rest goes on agent-level metadata
86
+ if (!['category', 'tags', 'version'].includes(key)) {
87
+ topLevelMeta[key] = value;
88
+ }
89
+ }
90
+ if (Object.keys(topLevelMeta).length > 0) {
91
+ agent.setMetadata(topLevelMeta);
92
+ }
66
93
  }
67
94
 
95
+ console.log('[OpenAgent] Registration file:', JSON.stringify(agent.getRegistrationFile(), null, 2));
68
96
  console.log('[OpenAgent] ⛓️ Registering on Base...');
69
97
  const txHandle = await agent.registerIPFS();
70
98
  const { result } = await txHandle.waitMined();
71
99
 
72
- // txHandle usually contains the hash if it's a ContractTransactionResponse
73
100
  const txHash = (txHandle as any).hash || "unknown";
74
101
 
75
102
  const agentId = result.agentId;