@haven_ai/sdk 0.0.0-dev.202609141814.b7c3b83 → 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
@@ -85,9 +85,13 @@ shows up in your Haven dashboard activity feed.
85
85
  ## Step-by-Step API
86
86
 
87
87
  Discovery and listing: `discoverTools({ verified?: 'any' | 'verified' | 'operator' })` returns the merged
88
- catalog — operator-curated plus `verified_payable` directory entries (epic #1717), each with `source`,
89
- `domainVerified` and `verifiedPayable`. `submitCatalogEntry(resourceUrl)` submits a merchant endpoint to
90
- the Verified Payable Directory (queue-only; the seller still must prove domain ownership before listing),
88
+ catalog — operator-curated rows plus self-submitted directory entries (epic #1717), each with `source`,
89
+ `domainVerified` and `verifiedPayable`. `verified: 'verified'` (#2978) filters on the badge, not the
90
+ source: it returns any entry, from either source, whose endpoint Haven watched answer a live quote
91
+ probe (`verifiedPayable === true`); `domainVerified` stays true only for entries that also proved
92
+ domain ownership. `verified: 'operator'` filters on provenance alone. `submitCatalogEntry(resourceUrl)`
93
+ submits a merchant endpoint to the Verified Payable Directory (queue-only; the seller still must prove
94
+ domain ownership before listing),
91
95
  and `getCatalogSubmissionStatus(id)` returns coarse status plus the ownership-proof instructions while
92
96
  the submission can still prove ownership.
93
97
 
@@ -402,6 +406,7 @@ Hosted MCP and signer tools also return stable `code` values on recoverable x402
402
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. |
403
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. |
404
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`. |
405
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`. |
406
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. |
407
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
  };
@@ -3509,7 +3530,7 @@ var HavenClient = class {
3509
3530
  const query = params.size > 0 ? `?${params.toString()}` : "";
3510
3531
  const raw = await this.get(`/catalog${query}`);
3511
3532
  let entries = raw.entries.map(mapCatalogEntry);
3512
- if (options.verified === "verified") entries = entries.filter((e) => e.source === "ingestion");
3533
+ if (options.verified === "verified") entries = entries.filter((e) => e.verifiedPayable === true);
3513
3534
  if (options.verified === "operator") entries = entries.filter((e) => e.source === "operator");
3514
3535
  return entries;
3515
3536
  }
@@ -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")) {
@@ -4222,7 +4250,7 @@ var toolDescriptions = {
4222
4250
  },
4223
4251
  discoverTools: {
4224
4252
  summary: "Step 1 of a purchase: discover payable services from Haven's curated merchant catalog \u2014 names, prices, and which pay tool to use next.",
4225
- selectionGuidance: "Use this when the user asks what the agent can buy, pay for, or which paid services exist \u2014 or when you need a resource URL for a service the user described. Use verified=verified to show only self-submitted directory entries that passed domain-ownership proof and a live quote probe \u2014 never treat those badges as proof of merchant honesty, quality, or reliability. Do NOT use for balance, budget, or spend-limit questions \u2014 use haven_get_allowances. Do NOT use to pay \u2014 each returned entry names the pay tool to use next.",
4253
+ selectionGuidance: "Use this when the user asks what the agent can buy, pay for, or which paid services exist \u2014 or when you need a resource URL for a service the user described. Use verified=verified for entries Haven watched pass a live quote probe (operator-curated or self-submitted) \u2014 domain_verified is the only ownership claim; never treat these badges as proof of merchant honesty, quality, or reliability. Do NOT use for balance, budget, or spend-limit questions \u2014 use haven_get_allowances. Do NOT use to pay \u2014 each returned entry names the pay tool to use next.",
4226
4254
  behavior: "Use each entry's suggested_tool field first \u2014 it names the exact next call. Read-only lookup against Haven's curated catalog; entries are periodically re-verified against the live merchant and degraded entries are flagged. Use category for a case-insensitive category filter (for example, VPN or vpn), or search for a product name, category, or description term. Returns name, description, price, rail, resource URL, tool_name, tool_arguments, suggested_tool, and the provenance badges source/domain_verified/verified_payable. The catalog price (price_display/price_atomic, marked price_is_indicative) is a last-verified hint, NOT authoritative \u2014 the real price comes from the merchant's live 402 at pay time. Never creates a payment, signature, or approval.",
4227
4255
  nextActionGuidance: `Pick an entry and pay it with the tool named in suggested_tool, passing the entry's resource_url, tool_name, and tool_arguments for MCP merchants. Confirm the price from the live pay-tool result (not the catalog), and pass the user's cap as max_amount_human in whole tokens ("no more than 1 USDC" \u2192 max_amount_human: "1") \u2014 never convert it to atomic units by hand.`
4228
4256
  },
@@ -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.