@haven_ai/sdk 0.0.0-dev.202609142030.8358774 → 0.0.0-dev.202609142053.7f40fec

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
@@ -406,6 +406,7 @@ Hosted MCP and signer tools also return stable `code` values on recoverable x402
406
406
  | `PRICE_EXCEEDS_MAX` | The merchant-authoritative x402 price is above the caller's spending cap. No funding transfer was created. | Tell the user the live price exceeded the cap and retry only after they confirm a higher one. Payloads carry `next_action: stop_and_tell_user` and `retry_with_new_quote: true` — the latter means any retry needs a fresh quote, not that one should be attempted unattended. |
407
407
  | `AMBIGUOUS_MAX_AMOUNT` | Both `max_amount` (atomic units) and `max_amount_human` (whole tokens) were sent for one purchase. Nothing was contacted and nothing was spent. | Re-send with exactly one — `max_amount_human` for a cap the user stated in tokens, `max_amount` for an exact atomic figure. |
408
408
  | `MAX_AMOUNT_UNCONVERTIBLE` | `max_amount_human` could not be converted against this quote's asset — its decimals are unknown to Haven, or the cap has more decimal places than the asset supports. Nothing was spent. | Round the cap to the asset's decimals, or re-send it as an exact atomic `max_amount`. |
409
+ | `MERCHANT_NOT_READY` | The merchant answered the quote probe with its own `503 { error: 'merchant_not_ready', reason_code, retry_after_s }` instead of a 402 — it cannot settle right now (e.g. its settlement wallet is out of gas). No payment was created. | Tell the user and retry later (`retry_after_s` in the message when given); do not treat it as a wrong endpoint. Payloads carry `next_action: stop_and_tell_user` and `retry_with_new_quote: true`. |
409
410
  | `PAYMENT_WINDOW_EXPIRED` | The funding/quote window closed before `haven_x402_sign_header`, `haven_submit`, or `haven_complete_mcp_tool` could finish. | Re-run `haven_pay_mcp_tool` with the same `idempotency_key`, then sign and complete the fresh quote. Payloads include `retry_with_new_quote: true`. |
410
411
  | `MERCHANT_REJECTED_AFTER_FUNDING` | Haven's funding leg succeeded, but the merchant rejected the paid retry. | Stop retrying the merchant and call `haven_sweep_delegate` so the user can recover stranded delegate USDC. |
411
412
 
package/dist/index.cjs CHANGED
@@ -195,7 +195,17 @@ var AgentPaymentFailureCode = {
195
195
  * asset can represent (truncating it would silently change the user's cap).
196
196
  * The fallback is the exact atomic `max_amount`.
197
197
  */
198
- MaxAmountUnconvertible: "MAX_AMOUNT_UNCONVERTIBLE"
198
+ MaxAmountUnconvertible: "MAX_AMOUNT_UNCONVERTIBLE",
199
+ /**
200
+ * #2979: the merchant answered a `tools/call` probe with its own
201
+ * machine-readable "cannot settle right now" refusal (HTTP 503,
202
+ * `{ error: 'merchant_not_ready', reason_code, ... }`) instead of a 402
203
+ * challenge — e.g. its settlement wallet is out of gas. No 402 was ever
204
+ * issued and no payment was created; this is honest and (per
205
+ * `retry_after_s`, when present) usually transient, unlike a permanent
206
+ * endpoint miss.
207
+ */
208
+ MerchantNotReady: "MERCHANT_NOT_READY"
199
209
  };
200
210
  var AgentPaymentRail = {
201
211
  /** Standard Haven payment from the user's Safe through an approved delegate allowance. */
@@ -253,7 +263,8 @@ var AgentPaymentFailureCodeDescriptions = {
253
263
  [AgentPaymentFailureCode.MerchantUnresponsiveAfterFunding]: "The Haven funding leg succeeded, but the merchant did not answer the paid retry before the timeout. The merchant may still settle late \u2014 check haven_get_payment_status (and retry haven_complete_mcp_tool once) BEFORE sweeping; sweep only if no settlement appears.",
254
264
  [AgentPaymentFailureCode.MerchantCallContextUnavailable]: "merchant_url/tool_name were omitted and no stored merchant call context is available for this payment_id. Re-send merchant_url, tool_name, arguments, and mcp_transport explicitly.",
255
265
  [AgentPaymentFailureCode.AmbiguousMaxAmount]: "Both max_amount (atomic units) and max_amount_human (whole tokens) were supplied for one purchase. Nothing was contacted and nothing was spent. Re-send with exactly ONE: max_amount_human for a cap the user stated in tokens, max_amount for an exact atomic figure.",
256
- [AgentPaymentFailureCode.MaxAmountUnconvertible]: "max_amount_human could not be converted to atomic units against this quote's asset \u2014 either its decimals are unknown to Haven or the cap has more decimal places than the asset supports. Nothing was spent. Round the cap, or re-send it as an exact atomic max_amount."
266
+ [AgentPaymentFailureCode.MaxAmountUnconvertible]: "max_amount_human could not be converted to atomic units against this quote's asset \u2014 either its decimals are unknown to Haven or the cap has more decimal places than the asset supports. Nothing was spent. Round the cap, or re-send it as an exact atomic max_amount.",
267
+ [AgentPaymentFailureCode.MerchantNotReady]: 'The merchant refused the probe with its own "cannot settle right now" signal instead of a 402 challenge. No payment was created. Often transient \u2014 retry later (see retry_after_s in the message, if given) rather than treating this as a broken or wrong endpoint.'
257
268
  };
258
269
  var AgentPaymentWarningCode = {
259
270
  /** No max_amount cap was supplied — the live quoted price was accepted as-is. */
@@ -348,8 +359,18 @@ var MerchantTimeoutError = class extends HavenApiError {
348
359
  };
349
360
  var X402UnexpectedStatusError = class extends HavenApiError {
350
361
  x402ErrorCode = "unexpected_non_402_status";
351
- constructor(message, statusCode) {
352
- super(message, statusCode);
362
+ /**
363
+ * #2979: `body` is the merchant's own JSON, when the non-402 response
364
+ * carried one — e.g. the demo merchant's `/mcp` readiness gate answers
365
+ * `503 { error: 'merchant_not_ready', reason_code, ... }`. Optional and
366
+ * best-effort: a non-JSON or unreadable body leaves this `undefined`, same
367
+ * as before this field existed. Consumers key on it (not on the message
368
+ * string) to distinguish an honest, machine-readable merchant refusal from
369
+ * a genuine "this is not the x402 endpoint" miss, which otherwise look
370
+ * identical — both are just "some non-402 status".
371
+ */
372
+ constructor(message, statusCode, body) {
373
+ super(message, statusCode, body);
353
374
  this.name = "X402UnexpectedStatusError";
354
375
  }
355
376
  };
@@ -3646,9 +3667,16 @@ var HavenClient = class {
3646
3667
  const request = snapshotX402Request(url, initialInit);
3647
3668
  const response = await this.merchantTransport.fetch(url, initialInit);
3648
3669
  if (response.status !== 402) {
3670
+ let body;
3671
+ try {
3672
+ body = await response.clone().json();
3673
+ } catch {
3674
+ body = void 0;
3675
+ }
3649
3676
  throw new X402UnexpectedStatusError(
3650
3677
  `Expected an x402 quote response with HTTP 402, got HTTP ${response.status}.`,
3651
- response.status || 400
3678
+ response.status || 400,
3679
+ body
3652
3680
  );
3653
3681
  }
3654
3682
  if (response.headers.get("MACHINE-PAYMENT-CHALLENGE")) {
@@ -4850,6 +4878,11 @@ present and surface \`message\` or \`error\` verbatim. Common cases:
4850
4878
  Round the cap, or send an exact atomic \`max_amount\`.
4851
4879
  - \`PAYMENT_WINDOW_EXPIRED\`: re-run the quote/prepare tool with the same
4852
4880
  \`idempotency_key\`, then sign the fresh payload.
4881
+ - \`MERCHANT_NOT_READY\`: the merchant refused the quote with its own
4882
+ "cannot settle right now" signal (a 503 \`merchant_not_ready\` with a
4883
+ \`reason_code\`) instead of a 402. No payment was created. Tell the user;
4884
+ retry later (the message carries \`retry_after_s\` when the merchant gave
4885
+ one) \u2014 this is not a wrong or broken endpoint.
4853
4886
  - \`MERCHANT_REJECTED_AFTER_FUNDING\`: the merchant refused the paid retry.
4854
4887
  Stop-and-sweep \u2014 stop retrying the merchant and use
4855
4888
  \`mcp__haven__haven_sweep_delegate\` to recover stranded delegate funds.