agent402-client 0.8.16 → 0.8.18

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.
Files changed (3) hide show
  1. package/README.md +20 -7
  2. package/index.js +33 -9
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -5,7 +5,7 @@ instance) - the buy side of [Agentic Finance](https://agent402.tools/agentic-fin
5
5
  agents paying per request over x402 or MPP. **Resolve a task to a tool, then call it - with payment handled for
6
6
  you.** Free pure-CPU tools settle with a built-in proof-of-work (no wallet, zero
7
7
  dependencies); wallet-only tools settle via an x402- or MPP-wrapped fetch you
8
- provide, or through a prepaid credits key already issued. Results are cached, and every
8
+ provide, or through a prepaid credits key. Results are cached, and every
9
9
  send carries an `Idempotency-Key` that is stable per client and per operation, so
10
10
  a retried lost response replays the paid answer on the credits and proof-of-work
11
11
  paths (the wallet path is different: see "Retries and double charges").
@@ -71,11 +71,14 @@ const a = new Agent402({ fetch: payFetch });
71
71
  const article = await a.call("extract", { url: "https://agent402.tools/guides/x402-in-5-minutes" });
72
72
  ```
73
73
 
74
- ## Prepaid credits (keys already issued)
74
+ ## Paying with a credits key
75
75
 
76
- New credits are not on sale. A key already issued (`a402_...`) still works: pass it
76
+ A prepaid credits key (`a402_...`; agent402.tools/credits says whether packs are on
77
+ sale) pays for Agent402's own services: pass it
77
78
  as `creditsKey` and the SDK sends `Authorization: Bearer a402_...` on wallet-only
78
- calls (the wallet-scoped routes, memory and usage, refuse it and need a wallet); the server authorizes
79
+ calls (the wallet-scoped routes, memory and usage, refuse it and need a wallet, as do
80
+ the routes that buy from an outside seller: the route-execute tiers,
81
+ `seller-payability`, `decide-execute` and the flight routes); the server authorizes
79
82
  against the key's balance before the handler runs and debits the list price only
80
83
  on a successful (200) response (the `X-Credits-Balance` header carries what is
81
84
  left; credits never expire). A refused call throws with the balance; nothing is
@@ -116,7 +119,17 @@ What the server does with it depends on how the call was paid:
116
119
  dedupe). So on the wallet path a retry after a lost response can settle twice;
117
120
  the `Idempotency-Key` cannot prevent that on its own. If that matters for your
118
121
  workload, use a credits key for the tools you retry, or keep the signed
119
- request and resend it yourself.
122
+ request and resend it yourself. A resent signed request needs no key: the
123
+ server answers a request re-sent with the same signed EVM payment, to the same
124
+ route with the same body, from its first answer inside the replay window,
125
+ with no second settlement, and marks it `X-Replayed-Answer: 1` (identity-bound
126
+ routes, routes with a lasting effect and streamed calls excepted).
127
+ - **Circle Gateway batched option:** where the server offers it on Arc or Base,
128
+ the 402 carries a second accept whose `extra.name` is `GatewayWalletBatched`.
129
+ This package pays the plain accept on those networks (the stock x402 client
130
+ takes the first accept it can pay, and the plain one is first); the batched
131
+ one needs a client that signs under the Gateway domain against a Circle
132
+ Gateway deposit.
120
133
 
121
134
  ## Workflows (skill packs)
122
135
 
@@ -215,7 +228,7 @@ that runs as one.
215
228
 
216
229
  | Method | What |
217
230
  |---|---|
218
- | `new Agent402({ baseUrl?, fetch?, x402Client?, creditsKey?, cache?, fetchImpl?, maxPerCallUsd?, dailyLimitUsd?, maxPerHostUsd?, maxResponseBytes?, outputValidator?, respondAsync?, asyncTimeoutMs?, onProgress? })` | `fetch` is your x402- or MPP-wrapped fetch for paid tools (optional); `x402Client` is the `@x402/core` client that `fetch` wraps, so the caps are enforced on the payment that is signed (see below); `creditsKey` is a prepaid card-credits key (`a402_...`) used for paid tools when no `fetch` is given; `cache` (default `true`) memoizes deterministic results; the three USD caps set optional spending limits (see below); `maxResponseBytes` (default 32MB, `null` to disable) refuses an oversized response body before it is parsed; `outputValidator` binds a caller-supplied delivery check; `respondAsync` (default `"auto"`), `asyncTimeoutMs` (default 15 minutes) and `onProgress` control slow routes run as jobs (see above) |
231
+ | `new Agent402({ baseUrl?, fetch?, x402Client?, creditsKey?, cache?, fetchImpl?, maxPerCallUsd?, dailyLimitUsd?, maxPerHostUsd?, payNetworks?, maxResponseBytes?, outputValidator?, respondAsync?, asyncTimeoutMs?, onProgress? })` | `fetch` is your x402- or MPP-wrapped fetch for paid tools (optional); `x402Client` is the `@x402/core` client that `fetch` wraps, so the caps are enforced on the payment that is signed (see below); `payNetworks` (for example `["base"]`, the names `withNetworkPreference` takes) tells the caps which chains a pre-wrapped `fetch` without an `x402Client` pays on, so a call is counted at the dearest option on those chains (when none of a 402's options is on them, the dearest of all counts); `creditsKey` is a prepaid card-credits key (`a402_...`) used for paid tools when no `fetch` is given; `cache` (default `true`) memoizes deterministic results; the three USD caps set optional spending limits (see below); `maxResponseBytes` (default 32MB, `null` to disable) refuses an oversized response body before it is parsed; `outputValidator` binds a caller-supplied delivery check; `respondAsync` (default `"auto"`), `asyncTimeoutMs` (default 15 minutes) and `onProgress` control slow routes run as jobs (see above) |
219
232
  | `await a.find(task, { k = 5 })` | Resolve a plain-language task to the best-matching tools on **this host** (route, price, schema, example) |
220
233
  | `await a.route(task, { k?, include?, network? })` | Cross-seller Smart Order Router: rank eligible/routable seller rows from the host's current index (free, read-only; opaque results may include server-reported `executeVia`) |
221
234
  | `await a.findWorkflows(task, { k = 2 })` | Resolve a task to matching multi-tool workflow templates (skill packs) |
@@ -361,7 +374,7 @@ each call reserves its amount synchronously, so N simultaneous calls can't each
361
374
  pass against the same pre-commit total.
362
375
 
363
376
  - **Zero dependencies** for the free/proof-of-work path (uses `node:crypto`).
364
- - **Non-custodial:** paid settlement is your `@x402/fetch` / `mppx` fetch + wallet (or a prepaid credits key you bought); this client never sees a private key.
377
+ - **Non-custodial:** paid settlement is your `@x402/fetch` / `mppx` fetch + wallet (or a prepaid credits key); this client never sees a private key.
365
378
  - MIT licensed. Part of [Agent402](https://github.com/MikeyPetrillo/Agent402).
366
379
 
367
380
  ## Pick the settlement chain (`withNetworkPreference`)
package/index.js CHANGED
@@ -30,7 +30,7 @@ export { AsyncJobPendingError } from "./async-job.js";
30
30
  // `User-Agent: agent402-client/<version>` - a standard header, no extra
31
31
  // network calls - so a seller can attribute traffic (and settled payments)
32
32
  // to this SDK. Product token only; nothing about the caller rides along.
33
- const VERSION = "0.8.16";
33
+ const VERSION = "0.8.18";
34
34
  const USER_AGENT = `agent402-client/${VERSION}`;
35
35
  // 32MB: about a hundred times any realistic response from this catalog (the
36
36
  // largest is a base64 image at a few MB), so it cannot break a legitimate
@@ -91,8 +91,11 @@ export class Agent402 {
91
91
  * @param {number} [opts.maxPerHostUsd] hard ceiling on rolling-24h paid spend to one seller host (USD)
92
92
  * @param {object} [opts.x402Client] the @x402/core x402Client your `fetch` wraps: the caps are then
93
93
  * enforced on the payment that is signed (see guardX402Client)
94
- * @param {string} [opts.creditsKey] a prepaid card-credits key (a402_...) already issued (new credits are
95
- * not on sale) - pays wallet-only tools when no payFetch is given
94
+ * @param {string[]} [opts.payNetworks] the chains your pre-wrapped `fetch` pays on (e.g. ["base"], the
95
+ * names withNetworkPreference takes): without an x402Client the
96
+ * caps count the dearest accept on these chains, not on every chain
97
+ * @param {string} [opts.creditsKey] a prepaid card-credits key (a402_...) - pays wallet-only tools on
98
+ * Agent402's own services when no payFetch is given
96
99
  * @param {number|null} [opts.maxResponseBytes=33554432]
97
100
  * Hard ceiling on a response body, enforced BEFORE it is parsed. null disables it.
98
101
  * @param {{id:string, validate:function}} [opts.outputValidator]
@@ -109,7 +112,7 @@ export class Agent402 {
109
112
  * ({ status, stage, progress, etaSeconds, ... }) while a job runs
110
113
  */
111
114
  constructor({ baseUrl = "https://agent402.tools", fetch: payFetch, cache = true, fetchImpl = undefined, tlsDistrust = true,
112
- maxPerCallUsd = null, dailyLimitUsd = null, maxPerHostUsd = null, creditsKey = null, x402Client = null,
115
+ maxPerCallUsd = null, dailyLimitUsd = null, maxPerHostUsd = null, creditsKey = null, x402Client = null, payNetworks = null,
113
116
  maxResponseBytes = DEFAULT_MAX_RESPONSE_BYTES, outputValidator = null,
114
117
  respondAsync = "auto", asyncTimeoutMs = DEFAULT_ASYNC_TIMEOUT_MS, onProgress = null } = {}) {
115
118
  this.respondAsync = respondAsync;
@@ -155,6 +158,10 @@ export class Agent402 {
155
158
  this._signCtx = new AsyncLocalStorage();
156
159
  this._guarded = false;
157
160
  if (x402Client) this.guardX402Client(x402Client);
161
+ // The chains a pre-wrapped fetch pays on, as CAIP-2 ids (null = unknown).
162
+ this._payNetworks = Array.isArray(payNetworks) && payNetworks.length
163
+ ? new Set(payNetworks.map((n) => String(NETWORK_CAIP2[String(n).trim().toLowerCase()] || String(n).trim()).toLowerCase()))
164
+ : null;
158
165
  // A response-size ceiling, enforced before parsing. See _readJson.
159
166
  this.maxResponseBytes = maxResponseBytes === null ? null : (numOrNull(maxResponseBytes) ?? DEFAULT_MAX_RESPONSE_BYTES);
160
167
  // Buyer-owned delivery policy. The callback stays entirely local and may
@@ -574,7 +581,7 @@ export class Agent402 {
574
581
  }
575
582
  const r = await send(); // no wallet - succeeds only on a FREE_MODE instance
576
583
  if (r.ok) return this._deliverResponse(slug, r, cacheKey, cache, validator, { maxResponseBytes, paid: false });
577
- throw this._fail(slug, r, `call "${slug}" failed: HTTP ${r.status} - wallet-only tool; construct with { fetch: payFetch } (an @x402/fetch-wrapped fetch) or { creditsKey } (a prepaid credits key already issued; new credits are not on sale)`);
584
+ throw this._fail(slug, r, `call "${slug}" failed: HTTP ${r.status} - wallet-only tool; construct with { fetch: payFetch } (an @x402/fetch-wrapped fetch) or { creditsKey } (a prepaid credits key; routes that buy from an outside seller need a wallet)`);
578
585
  }
579
586
 
580
587
  // Free (compute-payable) tool: succeeds plainly on a FREE_MODE instance,
@@ -649,8 +656,11 @@ export class Agent402 {
649
656
  * "guarded" (the signing guard holds every signature to the caps, so the
650
657
  * cheapest known accept is what can be paid, and unknown ones are dropped
651
658
  * there), "wallet" (no guard: the wallet may sign any accept, so the dearest
652
- * one counts, and one that cannot be priced is refused here), or "credits"
653
- * (nothing is signed; the dearest known accept counts).
659
+ * one counts, among the chains in `payNetworks` when they are given, and one
660
+ * that cannot be priced is refused here), or "credits" (nothing is signed:
661
+ * the credits gate debits the route's own quote, and no accept is below it,
662
+ * since a chain premium or price floor only raises an accept, so the
663
+ * cheapest known accept counts).
654
664
  * @returns {Promise<number>} the quoted amount, or `listUsd` when there is no readable 402 */
655
665
  async _quotedUsd(send, listUsd, { slug = "", mode = "wallet" } = {}) {
656
666
  if (!this._spendCapsConfigured()) return listUsd;
@@ -658,7 +668,14 @@ export class Agent402 {
658
668
  try {
659
669
  const pre = await send();
660
670
  if (pre?.status !== 402) return listUsd;
661
- q = quoteOf(await challengeAccepts(pre));
671
+ let accepts = await challengeAccepts(pre);
672
+ if (mode === "wallet" && this._payNetworks && Array.isArray(accepts)) {
673
+ const onChains = accepts.filter((a) => this._payNetworks.has(String(a?.network || "").toLowerCase()));
674
+ // No option on the declared chains (or a name that maps to none): the
675
+ // wallet may still sign any of them, so the dearest of all counts.
676
+ if (onChains.length) accepts = onChains;
677
+ }
678
+ q = quoteOf(accepts);
662
679
  } catch (e) {
663
680
  // A redirect is refused here, before anything is reserved or signed.
664
681
  if (e instanceof PaymentRedirectError) throw e;
@@ -671,7 +688,7 @@ export class Agent402 {
671
688
  `refusing to pay for "${slug}" - ${q.max == null ? "no" : "a"} payment option it offers is in an asset this client cannot price, and a capped wallet signs only what it can price${mode === "wallet" ? " (pass your x402Client to guard signing and pay the options it can price)" : ""}`,
672
689
  { limit: "knownAsset", slug, priceUsd: null, unknownOptions: q.unknown });
673
690
  }
674
- const quoted = mode === "guarded" ? q.min : q.max;
691
+ const quoted = mode === "wallet" ? q.max : q.min;
675
692
  // Larger of the two: an understated catalog must not lower the ceiling
676
693
  // the cap is checked against.
677
694
  return Math.max(listUsd, quoted);
@@ -955,8 +972,15 @@ export const NETWORK_CAIP2 = {
955
972
  "base-sepolia": "eip155:84532",
956
973
  robinhood: "eip155:4663",
957
974
  arc: "eip155:5042",
975
+ monad: "eip155:143",
976
+ celo: "eip155:42220",
977
+ avalanche: "eip155:43114",
978
+ sei: "eip155:1329",
979
+ optimism: "eip155:10",
958
980
  solana: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
959
981
  "solana-devnet": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
982
+ stellar: "stellar:pubnet",
983
+ algorand: "algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=",
960
984
  };
961
985
 
962
986
  export function withNetworkPreference(client, networks) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent402-client",
3
- "version": "0.8.16",
3
+ "version": "0.8.18",
4
4
  "description": "Agent402 buyer SDK: resolve a task to a tool across 500+ pay-per-call endpoints and call it with payment handled - free via proof-of-work, or over x402 or MPP (a stock mppx fetch works) with a wallet you keep; the buy side of Agentic Finance. Caching, idempotent retries (Idempotency-Key), non-custodial, zero dependencies.",
5
5
  "type": "module",
6
6
  "main": "index.js",