agent402-client 0.8.5 → 0.8.7
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/index.js +21 -14
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -23,13 +23,12 @@ import { createHash, randomBytes } from "node:crypto";
|
|
|
23
23
|
// `User-Agent: agent402-client/<version>` - a standard header, no extra
|
|
24
24
|
// network calls - so a seller can attribute traffic (and settled payments)
|
|
25
25
|
// to this SDK. Product token only; nothing about the caller rides along.
|
|
26
|
-
const VERSION = "0.8.
|
|
26
|
+
const VERSION = "0.8.7";
|
|
27
27
|
const USER_AGENT = `agent402-client/${VERSION}`;
|
|
28
28
|
// 32MB: about a hundred times any realistic response from this catalog (the
|
|
29
29
|
// largest is a base64 image at a few MB), so it cannot break a legitimate
|
|
30
30
|
// caller, while still stopping the class it exists for. A ceiling that is off
|
|
31
31
|
// by default protects nobody, so this one is on; pass null to disable it.
|
|
32
|
-
const FAILURE_DETAIL_MAX_CHARS = 2_000;
|
|
33
32
|
const DEFAULT_MAX_RESPONSE_BYTES = 32 * 1024 * 1024;
|
|
34
33
|
|
|
35
34
|
const leadingZeroBits = (buf) => { let n = 0; for (const b of buf) { if (b === 0) { n += 8; continue; } n += Math.clz32(b) - 24; break; } return n; };
|
|
@@ -366,7 +365,7 @@ export class Agent402 {
|
|
|
366
365
|
let settled = false;
|
|
367
366
|
try {
|
|
368
367
|
const r = await send({}, this.payFetch);
|
|
369
|
-
if (!r.ok) throw
|
|
368
|
+
if (!r.ok) throw this._fail(slug, r, await this._failureDetail(slug, r));
|
|
370
369
|
this._spendSettle(reservation); // confirm the reservation as settled spend
|
|
371
370
|
settled = true;
|
|
372
371
|
return this._deliverResponse(slug, r, cacheKey, cache, validator, { maxResponseBytes, paid: true });
|
|
@@ -389,9 +388,9 @@ export class Agent402 {
|
|
|
389
388
|
const r = await send({ Authorization: `Bearer ${this.creditsKey}` });
|
|
390
389
|
if (r.status === 402) {
|
|
391
390
|
const body = await r.json().catch(() => ({}));
|
|
392
|
-
throw
|
|
391
|
+
throw this._fail(slug, r, `call "${slug}" refused by credits: ${body.error || "payment required"}${body.balanceUsd != null ? ` (balance $${body.balanceUsd})` : ""} - top up at ${body.topup || `${this.baseUrl}/credits`}`);
|
|
393
392
|
}
|
|
394
|
-
if (!r.ok) throw
|
|
393
|
+
if (!r.ok) throw this._fail(slug, r, await this._failureDetail(slug, r));
|
|
395
394
|
this._spendSettle(reservation);
|
|
396
395
|
settled = true;
|
|
397
396
|
return this._deliverResponse(slug, r, cacheKey, cache, validator, { maxResponseBytes, paid: true });
|
|
@@ -399,7 +398,7 @@ export class Agent402 {
|
|
|
399
398
|
}
|
|
400
399
|
const r = await send(); // no wallet - succeeds only on a FREE_MODE instance
|
|
401
400
|
if (r.ok) return this._deliverResponse(slug, r, cacheKey, cache, validator, { maxResponseBytes, paid: false });
|
|
402
|
-
throw
|
|
401
|
+
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 } (prepaid card credits from ${this.baseUrl}/credits)`);
|
|
403
402
|
}
|
|
404
403
|
|
|
405
404
|
// Free (compute-payable) tool: succeeds plainly on a FREE_MODE instance,
|
|
@@ -410,7 +409,7 @@ export class Agent402 {
|
|
|
410
409
|
const chal = await this._powChallenge(slug);
|
|
411
410
|
r = await send({ "X-Pow-Solution": Agent402.solvePow(chal) });
|
|
412
411
|
}
|
|
413
|
-
if (!r.ok) throw
|
|
412
|
+
if (!r.ok) throw this._fail(slug, r, await this._failureDetail(slug, r, " after proof-of-work"));
|
|
414
413
|
return this._deliverResponse(slug, r, cacheKey, cache, validator, { maxResponseBytes, paid: false });
|
|
415
414
|
}
|
|
416
415
|
|
|
@@ -479,8 +478,20 @@ export class Agent402 {
|
|
|
479
478
|
/** The message for a failed call CARRIES THE SELLER'S OWN DETAIL. Agent402's
|
|
480
479
|
* 4xx bodies are self-explaining ({error, tool, expected}); a message that
|
|
481
480
|
* said only "HTTP 400" threw that away, and an agent reading it could not
|
|
482
|
-
* correct its input (elizaOS registry review, 2026-09-14).
|
|
483
|
-
*
|
|
481
|
+
* correct its input (elizaOS registry review, 2026-09-14). WHOLE: 0.8.4
|
|
482
|
+
* cut the detail at 2,000 characters and 0.8.5 said so when it cut, and
|
|
483
|
+
* the same review measured a 2,517-character seller error reaching the
|
|
484
|
+
* model without its end (2026-09-16). The body is already bounded before
|
|
485
|
+
* it is parsed (maxResponseBytes, 32 MB default), so a second cut here
|
|
486
|
+
* protected nothing and lost the part of the message an agent needs to
|
|
487
|
+
* act on. A body that is not JSON contributes nothing but its status. */
|
|
488
|
+
/** A refusal a caller can branch on: `status` (the HTTP status), `slug` and
|
|
489
|
+
* `paid: false` ride on the Error, so a 402 is not something to regex out of
|
|
490
|
+
* the message. Nothing was spent on any path that reaches this. */
|
|
491
|
+
_fail(slug, r, message) {
|
|
492
|
+
return Object.assign(new Error(message), { status: r?.status, slug, paid: false });
|
|
493
|
+
}
|
|
494
|
+
|
|
484
495
|
async _failureDetail(slug, r, when = "") {
|
|
485
496
|
let detail = "";
|
|
486
497
|
try {
|
|
@@ -489,11 +500,7 @@ export class Agent402 {
|
|
|
489
500
|
if (msg != null) detail = ` - ${typeof msg === "string" ? msg : JSON.stringify(msg)}`;
|
|
490
501
|
if (body?.expected != null) detail += ` (expected: ${JSON.stringify(body.expected)})`;
|
|
491
502
|
} catch { /* not JSON: the status is the whole story */ }
|
|
492
|
-
|
|
493
|
-
// bound that cuts says so, with the length it cut, rather than ending
|
|
494
|
-
// mid-sentence as if that were the whole message.
|
|
495
|
-
const cut = detail.length > FAILURE_DETAIL_MAX_CHARS ? `${detail.slice(0, FAILURE_DETAIL_MAX_CHARS)} [${detail.length - FAILURE_DETAIL_MAX_CHARS} more characters not shown]` : detail;
|
|
496
|
-
return `call "${slug}" failed${when}: HTTP ${r.status}${cut}`;
|
|
503
|
+
return `call "${slug}" failed${when}: HTTP ${r.status}${detail}`;
|
|
497
504
|
}
|
|
498
505
|
|
|
499
506
|
/** True if any spending ceiling is configured (worth preflighting the 402). */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent402-client",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.7",
|
|
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",
|