@haven_ai/sdk 0.1.32-alpha.0 → 0.1.33-alpha.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/dist/index.js CHANGED
@@ -665,6 +665,30 @@ var NETWORK_TOKENS = {
665
665
  "eip155:84532": BASE_SEPOLIA_TOKENS,
666
666
  "base-sepolia": BASE_SEPOLIA_TOKENS
667
667
  };
668
+ var X402_PAYMENT_HEADER_NAME = "PAYMENT-SIGNATURE";
669
+ var X402_LEGACY_PAYMENT_HEADER_NAME = "X-PAYMENT";
670
+ var X402_PAYMENT_REQUIRED_HEADER_NAME = "PAYMENT-REQUIRED";
671
+ var X402_PAYMENT_RESPONSE_HEADER_NAME = "PAYMENT-RESPONSE";
672
+ var X402_PAYMENT_HEADER_NAMES_SENT = `${X402_PAYMENT_HEADER_NAME}, ${X402_LEGACY_PAYMENT_HEADER_NAME}`;
673
+ var X402_PAYMENT_HEADER_NAMES = [
674
+ X402_PAYMENT_HEADER_NAME,
675
+ X402_LEGACY_PAYMENT_HEADER_NAME
676
+ ];
677
+ function x402PaymentHeaderNamesFor(paymentHeader) {
678
+ const both = [X402_PAYMENT_HEADER_NAME, X402_LEGACY_PAYMENT_HEADER_NAME];
679
+ let decoded;
680
+ try {
681
+ decoded = decodeBase64Json(paymentHeader);
682
+ } catch {
683
+ return both;
684
+ }
685
+ const accepted = decoded?.accepted;
686
+ if (!accepted || typeof accepted !== "object" || Array.isArray(accepted)) return both;
687
+ return isErc7710Option(accepted) ? [X402_PAYMENT_HEADER_NAME] : both;
688
+ }
689
+ function x402PaymentHeaderNamesSent(paymentHeader) {
690
+ return x402PaymentHeaderNamesFor(paymentHeader).join(", ");
691
+ }
668
692
  function parsePaymentRequired(response) {
669
693
  const v2Header = response.headers.get("PAYMENT-REQUIRED");
670
694
  if (v2Header) {
@@ -1155,7 +1179,7 @@ function nextActionForStatus(status) {
1155
1179
  if (status === "submitted") return AgentPaymentNextAction.CheckStatusLater;
1156
1180
  if (status === "confirmed") return AgentPaymentNextAction.None;
1157
1181
  if (status === "pending" || status === "pending_approval") return AgentPaymentNextAction.StopAndTellUser;
1158
- if (status === "approved") return AgentPaymentNextAction.WaitForUserToCompletePayment;
1182
+ if (status === "approved") return AgentPaymentNextAction.StopAndTellUser;
1159
1183
  if (status === "proposed") return AgentPaymentNextAction.StopAndTellUser;
1160
1184
  if (status === "executed") return AgentPaymentNextAction.StopAndTellUser;
1161
1185
  if (status === "rejected") return AgentPaymentNextAction.StopAndTellUser;
@@ -1167,6 +1191,9 @@ function messageForState(label, status, paymentId, nextAction) {
1167
1191
  if (status === "pending" || status === "pending_approval") {
1168
1192
  return `${label} is not payable: it is outside the agent's on-chain budget and no approval is pending (payment_id: ${paymentId}). Ask the user to grant or raise the budget in Haven.`;
1169
1193
  }
1194
+ if (status === "approved") {
1195
+ return `This payment carries a retired status ("approved") that no live Haven rail produces (payment_id: ${paymentId}). Nothing is waiting to be completed \u2014 tell the user to review this payment in Haven.`;
1196
+ }
1170
1197
  if (status === "executed") {
1171
1198
  return `This payment carries a retired status ("executed") that no live Haven rail produces (payment_id: ${paymentId}). Do not retry it \u2014 tell the user to review this payment in Haven.`;
1172
1199
  }
@@ -1384,10 +1411,37 @@ var McpMerchantTransport = class {
1384
1411
  hasBazaarExtension(response) {
1385
1412
  return responseHasBazaarExtension(response);
1386
1413
  }
1387
- /** Deliver an already-signed x402 header without changing the caller body. */
1414
+ /**
1415
+ * Deliver an already-signed x402 header without changing the caller body.
1416
+ *
1417
+ * #2289: x402 v2 reads `PAYMENT-SIGNATURE`; v1 reads `X-PAYMENT`. Sending
1418
+ * only the legacy name meant a strict v2 merchant never saw the header —
1419
+ * indistinguishable, from the merchant's side, from sending no header at
1420
+ * all, while on the EIP-3009 bridge the funding leg had already moved the
1421
+ * money.
1422
+ *
1423
+ * #2341: WHICH names go on is per-payload, not always both — see
1424
+ * `x402PaymentHeaderNamesFor`. Both for EIP-3009; `PAYMENT-SIGNATURE` alone
1425
+ * for erc7710, whose header carries a whole delegation chain and answered
1426
+ * HTTP 431 when duplicated. The decision is made here rather than by the
1427
+ * caller so every path inherits it, and it is read from the payload rather
1428
+ * than passed in, because a flag a caller supplies is a flag a caller can
1429
+ * get wrong.
1430
+ *
1431
+ * Always `set`, never `append`, so a stale header on the caller's `init` is
1432
+ * replaced rather than added to — a merchant that reads the first of two
1433
+ * values would otherwise verify a superseded authorization. The name NOT
1434
+ * being sent is deleted for the same reason: on erc7710 a stale `X-PAYMENT`
1435
+ * left in place would be a superseded authorization we chose not to
1436
+ * overwrite, which is worse than the duplicate this change removes.
1437
+ */
1388
1438
  async deliverPayment(url, init, paymentHeader) {
1389
1439
  const headers = new Headers(init?.headers);
1390
- headers.set("X-PAYMENT", paymentHeader);
1440
+ const send = x402PaymentHeaderNamesFor(paymentHeader);
1441
+ for (const name of X402_PAYMENT_HEADER_NAMES) {
1442
+ if (send.includes(name)) headers.set(name, paymentHeader);
1443
+ else headers.delete(name);
1444
+ }
1391
1445
  return this.fetch(url, { ...init, headers });
1392
1446
  }
1393
1447
  async notifyInitialized(url, init, sessionId, wallet) {
@@ -2233,10 +2287,11 @@ var X402Erc7710 = class {
2233
2287
  * settled when this returns** — that is why it does not return an
2234
2288
  * `X402Receipt`.
2235
2289
  *
2236
- * Requires a delegation-rail account. The backend enforces that
2237
- * (`validateGenericSchemeRail`), and so does this method, before building a
2238
- * request the backend would only reject: an error a client can explain is
2239
- * worth more than a 400 it has to decode.
2290
+ * Requires a delegation-rail account. The backend enforces that at the
2291
+ * rail seam — a non-delegation account gets the #1986 retired-rail 410 from
2292
+ * `POST /x402/authorize` whatever scheme it asks for (#2245) — and so does
2293
+ * this method, before building a request the backend would only reject: an
2294
+ * error a client can explain is worth more than a refusal it has to decode.
2240
2295
  *
2241
2296
  * **MCP callers must pass `options.resourceUrl`.** An in-band MCP 402
2242
2297
  * challenge frequently carries no `resource` object at all, so
@@ -2530,7 +2585,7 @@ var MerchantCompletion = class {
2530
2585
  merchantStatus: retryResponse.status,
2531
2586
  challengePayload: paymentRequired,
2532
2587
  selectedPayment: receipt.accepted,
2533
- paymentProofHeaderName: "X-PAYMENT",
2588
+ paymentProofHeaderName: x402PaymentHeaderNamesSent(receipt.paymentHeader),
2534
2589
  paymentProofHeader: receipt.paymentHeader,
2535
2590
  protocolReceiptHeaderName: "PAYMENT-RESPONSE",
2536
2591
  protocolReceiptHeader: retryResponse.headers.get("PAYMENT-RESPONSE") ?? void 0
@@ -2640,6 +2695,109 @@ var MerchantCompletion = class {
2640
2695
  } catch {
2641
2696
  }
2642
2697
  }
2698
+ /**
2699
+ * #2292: record what a merchant said to a retry **Haven did not make**.
2700
+ *
2701
+ * On the plain-HTTP x402 path Haven tells the agent to call the merchant
2702
+ * itself — that is the keyless design, not an oversight — so the two writes
2703
+ * above were reachable only from `completeX402MerchantCall`, where Haven IS
2704
+ * the caller. A manual retry had nowhere to put its outcome, which left
2705
+ * `intentStateFor`'s merchant-rejected branch dead on the one flow Haven
2706
+ * prescribes and made the 15-minute grace window the only route to
2707
+ * `funded_but_unsettled`.
2708
+ *
2709
+ * Three properties distinguish this from `recordRetryRejected` /
2710
+ * `reportEvidence`, and each is deliberate:
2711
+ *
2712
+ * 1. **It does not swallow.** Those two are bookkeeping hung off a call
2713
+ * whose outcome is already decided, so an exception there would turn a
2714
+ * completed payment into a reported failure. Here the report IS the
2715
+ * caller's request; silently dropping it would recreate the exact
2716
+ * unobservability #2292 exists to remove.
2717
+ * 2. **The anchor is server-side.** `txHash` and `resourceUrl` come from
2718
+ * the payment's own Haven record, never from the reporter — so a report
2719
+ * cannot be pointed at a different transaction or a different resource,
2720
+ * and it can never CONFIRM an intent (an erc7710 intent has no Haven
2721
+ * tx hash and is refused here rather than completed from a supplied one,
2722
+ * which is #2092's verified seam and stays its own path).
2723
+ * 3. **It is evidence, never authority.** Haven does not and must not check
2724
+ * the claim: verifying it would mean calling the merchant, which is the
2725
+ * property this whole path exists to preserve. What bounds a false
2726
+ * report is scope — the backend routes resolve the payment
2727
+ * `WHERE agent_id = $`, so a caller can only ever describe its own
2728
+ * payment — plus the fact that nothing financial keys off the claim:
2729
+ * the sweep is balance-driven, the intent's status/amount/recipient are
2730
+ * untouched, and a false `accepted` runs the server's own on-chain
2731
+ * residue check, which re-flags stranded funds independently.
2732
+ */
2733
+ async reportMerchantOutcome(input) {
2734
+ if (!Number.isInteger(input.merchantStatus) || input.merchantStatus < 100 || input.merchantStatus > 599) {
2735
+ throw new HavenApiError(
2736
+ `merchant_status must be an integer HTTP status from 100 to 599 (received ${input.merchantStatus}).`,
2737
+ 400,
2738
+ void 0,
2739
+ input.paymentId
2740
+ );
2741
+ }
2742
+ const looksAccepted = input.merchantStatus >= 200 && input.merchantStatus < 300;
2743
+ if (looksAccepted !== (input.outcome === "accepted")) {
2744
+ throw new HavenApiError(
2745
+ `outcome "${input.outcome}" contradicts merchant_status ${input.merchantStatus}: report "accepted" only for a 2xx and "rejected" only for a non-2xx.`,
2746
+ 400,
2747
+ void 0,
2748
+ input.paymentId
2749
+ );
2750
+ }
2751
+ const status = await this.getPaymentStatus(input.paymentId);
2752
+ if (status.rail !== "x402") {
2753
+ throw new HavenPaymentStateError(
2754
+ `Payment ${status.paymentId} is ${status.rail}, not x402 \u2014 there is no merchant retry to report.`,
2755
+ 409,
2756
+ status
2757
+ );
2758
+ }
2759
+ if (status.status !== "confirmed" || !status.txHash) {
2760
+ throw new HavenPaymentStateError(
2761
+ `Payment ${status.paymentId} has no confirmed Haven funding transaction to anchor a merchant report to (status ${status.status}). ${status.message}`,
2762
+ paymentStateStatusCode(status.status, 409),
2763
+ status
2764
+ );
2765
+ }
2766
+ const resourceUrl = status.resourceUrl ?? status.x402?.resourceUrl ?? null;
2767
+ if (!resourceUrl) {
2768
+ throw new HavenApiError(
2769
+ `Payment ${status.paymentId} has no recorded resource URL, so a merchant report cannot be stored.`,
2770
+ 409,
2771
+ status,
2772
+ status.paymentId
2773
+ );
2774
+ }
2775
+ const txHash = status.txHash;
2776
+ if (input.outcome === "rejected") {
2777
+ await this.post("/machine-payments/reconciliation-events", {
2778
+ paymentId: status.paymentId,
2779
+ rail: "x402",
2780
+ eventType: "merchant_retry_rejected_after_payment",
2781
+ txHash,
2782
+ reason: `Agent-reported: merchant returned HTTP ${input.merchantStatus} to a manual retry after Haven payment confirmation`,
2783
+ details: {
2784
+ resource_url: resourceUrl,
2785
+ retry_status: input.merchantStatus,
2786
+ retry_body: input.merchantBody?.slice(0, MERCHANT_BODY_SNIPPET_LIMIT) || null,
2787
+ reported_by: "agent_manual_retry"
2788
+ }
2789
+ });
2790
+ return { paymentId: status.paymentId, outcome: "rejected", txHash, resourceUrl, recorded: "reconciliation_event" };
2791
+ }
2792
+ await this.post("/machine-payments/evidence", {
2793
+ paymentId: status.paymentId,
2794
+ rail: "x402",
2795
+ txHash,
2796
+ resourceUrl,
2797
+ merchantStatus: input.merchantStatus
2798
+ });
2799
+ return { paymentId: status.paymentId, outcome: "accepted", txHash, resourceUrl, recorded: "evidence" };
2800
+ }
2643
2801
  async reportEvidence(input) {
2644
2802
  const body = {
2645
2803
  paymentId: input.paymentId,
@@ -3526,7 +3684,7 @@ var HavenClient = class {
3526
3684
  * Deliver an already-signed x402 payment header to the merchant and return
3527
3685
  * the merchant's response. Used by the hosted MCP server to complete the
3528
3686
  * merchant leg of an MCP tool payment after the edge signer has built the
3529
- * `X-PAYMENT` header.
3687
+ * merchant payment header.
3530
3688
  *
3531
3689
  * Custody note: this never needs the delegate key. It relays a signed,
3532
3690
  * amount/merchant/nonce-bound EIP-3009 authorization the edge signer already
@@ -3536,13 +3694,14 @@ var HavenClient = class {
3536
3694
  * says the merchant was Bazaar-discoverable, runs a fresh `initialize`
3537
3695
  * handshake (the quote-time session is gone once funding confirms; the x402
3538
3696
  * challenge is stateless w.r.t. the MCP session, so a fresh session is
3539
- * accepted), threads the session + wallet headers, sets `X-PAYMENT`, and
3697
+ * accepted), threads the session + wallet headers, sets the x402 payment
3698
+ * header under the names that scheme requires (#2341), and
3540
3699
  * collapses an SSE JSON-RPC response to its `result`.
3541
3700
  */
3542
3701
  /**
3543
3702
  * Wait for a payment's Safe→delegate funding tx to reach ≥1 on-chain
3544
3703
  * confirmation. The hosted x402 completion path MUST call this after funding
3545
- * and before delivering the X-PAYMENT header, so the merchant's
3704
+ * and before delivering the merchant payment header, so the merchant's
3546
3705
  * balanceOf(delegate) / transferWithAuthorization verification sees the funded
3547
3706
  * balance — otherwise it rejects with "Payment verification failed". The
3548
3707
  * SDK's local path already does this (see `X402FundingLeg.authorize`); the hosted
@@ -3617,7 +3776,7 @@ var HavenClient = class {
3617
3776
  txHash: evidenceTxHash,
3618
3777
  resourceUrl: evidenceContext.resourceUrl,
3619
3778
  merchantStatus: surfaced.status,
3620
- paymentProofHeaderName: "X-PAYMENT",
3779
+ paymentProofHeaderName: x402PaymentHeaderNamesSent(input.paymentHeader),
3621
3780
  paymentProofHeader: input.paymentHeader,
3622
3781
  protocolReceiptHeaderName: protocolReceiptHeader ? "PAYMENT-RESPONSE" : void 0,
3623
3782
  protocolReceiptHeader
@@ -3632,6 +3791,18 @@ var HavenClient = class {
3632
3791
  settlementTxHash: settlement.settlementTxHash ?? void 0
3633
3792
  };
3634
3793
  }
3794
+ /**
3795
+ * #2292: report the outcome of a merchant retry the AGENT performed.
3796
+ *
3797
+ * The hosted `haven_complete_mcp_tool` / `completeX402MerchantCall` path is
3798
+ * for merchants Haven calls itself. On the plain-HTTP x402 path Haven never
3799
+ * talks to the merchant, so the outcome of that retry had no way back —
3800
+ * see `MerchantCompletion.reportMerchantOutcome` for what is verified about
3801
+ * a caller-asserted report and what deliberately is not.
3802
+ */
3803
+ async reportX402MerchantOutcome(input) {
3804
+ return await this.merchantCompletion.reportMerchantOutcome(input);
3805
+ }
3635
3806
  /**
3636
3807
  * GET /x402/:id/merchant-call-context — the settle-leg twin of #1263's
3637
3808
  * sign-context fetch (#1307). Re-serves the stored merchant MCP-tool call
@@ -3793,12 +3964,12 @@ var toolDescriptions = {
3793
3964
  payX402OneShot: {
3794
3965
  summary: "Fetch an x402 paid HTTP resource in a single call. Handles the full probe -> pay -> retry round trip and returns the merchant response.",
3795
3966
  selectionGuidance: "Prefer this over the quote+pay split when the agent just wants the paid resource and does not need to inspect the price first. If you already have a quote from haven_quote_x402, use haven_pay_x402_quote instead. Do not use for read-only allowance, budget, spend-limit, remaining-amount, reset-period, or what-can-I-spend questions; use the allowance lookup tool instead.",
3796
- behavior: "Calls the URL, parses any HTTP 402 x402 challenge, signs the payment locally, then retries the original request with the X-PAYMENT header and returns the merchant response. Settlement is either direct account-to-merchant with no funding leg, or a bridge that first redeems the agent's budget delegation to fund the delegate wallet for an EIP-3009 authorization. A payment outside the on-chain budget is declined before any money moves; nothing is queued for a human to approve later. If the resource returns a non-402 status, returns it unchanged without contacting Haven.",
3967
+ behavior: "Calls the URL, parses any HTTP 402 x402 challenge, signs the payment locally, then retries the original request with the signed payment header (sent under PAYMENT-SIGNATURE, plus the legacy X-PAYMENT on the EIP-3009 path only) and returns the merchant response. Settlement is either direct account-to-merchant with no funding leg, or a bridge that first redeems the agent's budget delegation to fund the delegate wallet for an EIP-3009 authorization. A payment outside the on-chain budget is declined before any money moves; nothing is queued for a human to approve later. If the resource returns a non-402 status, returns it unchanged without contacting Haven.",
3797
3968
  nextActionGuidance: "Preserve the returned resume_state or paymentId \u2014 either identifies this payment if you need to ask about it later. This tool performs the merchant retry itself, so do not wait on a signal while the call is in flight. If the process crashes after this call and a later haven_get_payment_status reports nextAction=retry_original_x402_request, Haven's funding leg confirmed but no merchant response was ever recorded \u2014 call the resume tool with the preserved resume_state or payment_id instead of paying again. If the response carries phase=insufficient_funds and nextAction=fund_safe_or_raise_allowance, the payment cannot be retried until the account is funded or the agent budget raised \u2014 stop and tell the user the shortfall reported on the response."
3798
3969
  },
3799
3970
  resumeX402: {
3800
3971
  summary: "Resume an x402 payment whose Haven-side authorization already succeeded but whose merchant retry did not complete.",
3801
- behavior: "Accepts either resume_state or payment_id, validates the original x402 details against the authorized Haven funding, and retries the merchant request with the X-PAYMENT header. No new Haven payment is created.",
3972
+ behavior: "Accepts either resume_state or payment_id, validates the original x402 details against the authorized Haven funding, and retries the merchant request with the signed payment header (sent under PAYMENT-SIGNATURE, plus the legacy X-PAYMENT on the EIP-3009 path only). No new Haven payment is created.",
3802
3973
  nextActionGuidance: "Only call this after haven_get_payment_status reports nextAction=retry_original_x402_request \u2014 that means Haven's funding leg confirmed but no merchant response was ever recorded, most often because the process crashed between funding and the merchant retry. Any other nextAction reports a conflict instead of retrying, so do not call this speculatively. Do not start a new merchant session and do not pay again \u2014 that would pay twice for one resource."
3803
3974
  },
3804
3975
  // #1328: quoteMpp / payMpp / resumeMpp (the mpp_demo challenge/quote/resume
@@ -3984,7 +4155,7 @@ var resumeX402Schema = {
3984
4155
  var MAKE_PAYMENT_DESCRIPTION = "Request and sign a payment from the user-controlled account within its on-chain budget. For read-only allowance, budget, spend-limit, remaining-amount, or reset-period questions, use get_allowances instead of making a payment. Haven authenticates the agent and relays the signed transaction that redeems the agent budget delegation; it does not hold keys or control funds. Gnosis Chain tokens: EURe, USDC.e, xDAI. Base tokens: USDC, ETH.";
3985
4156
  var GET_STATUS_DESCRIPTION = toolDescriptions.getPaymentStatus.summary + " Accepts payment intent IDs. Returns the current status, phase, next_action, transaction hash if available, and payment details.";
3986
4157
  var GET_ALLOWANCES_DESCRIPTION = composeDescription(toolDescriptions.getAllowances);
3987
- var AUTHORIZE_X402_DESCRIPTION = composeDescription(toolDescriptions.payX402) + " In this SDK tool set, the allowance lookup tool is get_allowances. When a paid API returns x402 payment requirements, use this tool to sign with the agent-owned delegate key; funding, when the scheme needs it, is redeemed from the agent budget delegation and is bounded by it. Haven relays signed transactions only; the agent key authorizes payment and on-chain limits enforce spend. A payment outside the on-chain budget is declined before any money moves \u2014 report the decline and ask the user to raise the budget in Haven; do not loop retries and do not wait for an approval, because none is queued. Preserve the original merchant/MCP session and x402 details. Use the returned payment_header as the X-PAYMENT header on the retry request when doing a manual HTTP retry.";
4158
+ var AUTHORIZE_X402_DESCRIPTION = composeDescription(toolDescriptions.payX402) + " In this SDK tool set, the allowance lookup tool is get_allowances. When a paid API returns x402 payment requirements, use this tool to sign with the agent-owned delegate key; funding, when the scheme needs it, is redeemed from the agent budget delegation and is bounded by it. Haven relays signed transactions only; the agent key authorizes payment and on-chain limits enforce spend. A payment outside the on-chain budget is declined before any money moves \u2014 report the decline and ask the user to raise the budget in Haven; do not loop retries and do not wait for an approval, because none is queued. Preserve the original merchant/MCP session and x402 details. On a manual HTTP retry always set PAYMENT-SIGNATURE (x402 v2) to the returned payment_header; a strict v2 merchant reads only that name. Also set X-PAYMENT (v1) on the EIP-3009 funding path for legacy merchants, but NEVER on erc7710 \u2014 that header carries a delegation chain and duplicating it is refused with HTTP 431.";
3988
4159
  var RESUME_X402_DESCRIPTION = toolDescriptions.resumeX402.summary + " Only call this after get_payment_status reports nextAction=retry_original_x402_request \u2014 that means Haven's funding leg confirmed but no merchant response was ever recorded (typically a crash between funding and the merchant retry). Any other nextAction reports a conflict instead of retrying \u2014 do not call this speculatively, and do not pay again.";
3989
4160
  var SWEEP_DELEGATE_DESCRIPTION = composeDescription(toolDescriptions.sweep_delegate);
3990
4161
  var sweepDelegateSchema = {
@@ -4207,8 +4378,20 @@ merchant leg for you.
4207
4378
  recipient, amount, and token for a plain transfer. For an arbitrary,
4208
4379
  non-MCP x402 paywall: \`mcp__haven__haven_quote_x402\` to get a quote, then
4209
4380
  \`mcp__haven__haven_pay_x402_quote\` \u2014 follow the result's guidance fields
4210
- first and sign in the local Haven signer. The pay tool performs the merchant
4211
- retry itself, so do not wait on a signal while it runs. If the process
4381
+ first and sign in the local Haven signer. On THIS path Haven does not talk to
4382
+ the merchant: \`mcp__haven-signer__haven_sign_x402\` returns both
4383
+ \`signature\` and \`payment_header\`; relay \`signature\` with
4384
+ \`mcp__haven__haven_submit\`, then retry the paywalled URL yourself with
4385
+ \`payment_header\`. Do not pass that call's \`x402_binding\` to
4386
+ \`mcp__haven-signer__haven_x402_sign_header\` \u2014 the one-shot already spent it
4387
+ building the header, so the call can only refuse. Then tell Haven what the
4388
+ merchant answered: \`mcp__haven__haven_report_x402_outcome\` with the
4389
+ \`payment_id\`, \`outcome\` (\`"accepted"\` for a 2xx, else \`"rejected"\`)
4390
+ and the \`merchant_status\` you got. Because Haven never contacted that
4391
+ merchant, this is the only way it can learn the purchase failed \u2014 without it a
4392
+ failed purchase reads as complete for fifteen minutes. (The SDK's own
4393
+ \`haven_pay_x402\` tool does perform the merchant retry itself; that tool is
4394
+ not part of the hosted MCP surface.) If the process
4212
4395
  crashes after payment, a later \`mcp__haven__haven_get_payment_status\` call
4213
4396
  may report \`nextAction: 'retry_original_x402_request'\` \u2014 only then call
4214
4397
  \`mcp__haven__haven_resume_x402_payment\` with the preserved resume state or
@@ -4223,9 +4406,11 @@ payment id, instead of paying again.
4223
4406
  result, never a catalog price. \`haven_discover_tools\` prices are indicative
4224
4407
  (\`price_is_indicative\`) and can be stale. A read-only quote is informational
4225
4408
  only and does not reserve a price; the later paid call re-quotes and enforces
4226
- the cap. The pay-tool result's \`amount\` / \`amount_atomic\` is the amount
4227
- Haven authorizes for that call \u2014 a ceiling the merchant settles at or below \u2014
4228
- so present it as the most the user will pay.
4409
+ the cap. The pay-tool result's \`amount\` / \`amount_atomic\` is the merchant's
4410
+ own quoted price for that call \u2014 a ceiling the merchant settles at or below \u2014
4411
+ so present it as the most the user will pay. It is a price, not an approval:
4412
+ the payment goes through only if it also fits the cap you set and the on-chain
4413
+ budget the user signed, which is enforced on-chain rather than by Haven.
4229
4414
 
4230
4415
  **Status:** \`mcp__haven__haven_get_payment_status\` with a \`payment_id\` to
4231
4416
  check on in-flight payments. Do not poll in a tight loop.
@@ -4376,6 +4561,6 @@ function sameUrl(a, b) {
4376
4561
  }
4377
4562
  }
4378
4563
 
4379
- export { AGENT_PAYMENT_FAILURE_CODE_VALUES, AGENT_PAYMENT_NEXT_ACTION_VALUES, AGENT_PAYMENT_PHASE_VALUES, AGENT_PAYMENT_RAIL_VALUES, AgentPaymentFailureCode, AgentPaymentFailureCodeDescriptions, AgentPaymentFailureCodeSchema, AgentPaymentNextAction, AgentPaymentNextActionDescriptions, AgentPaymentNextActionSchema, AgentPaymentPhase, AgentPaymentPhaseDescriptions, AgentPaymentPhaseSchema, AgentPaymentRail, AgentPaymentRailDescriptions, AgentPaymentRailSchema, AgentPaymentWarningCode, DEFAULT_CONFIRMATION_TIMEOUT_MS, DISCOVERY_MAX_BYTES, ERC7710_ASSET_TRANSFER_METHOD, HAVEN_MINIMUM_NODE_VERSION, HAVEN_SKILL_BODY_MD, HAVEN_SKILL_MD, HavenApiError, HavenClient, HavenError, HavenPaymentStateError, HavenSigningError, HavenTimeoutError, HavenUnsupportedSignerVersionError, MERCHANT_DISCOVERY_PATHS, MerchantTimeoutError, RECEIPT_VERSION, SIGNER_UPDATE_FALLBACK, SKILL_FOLDER_NAME, SWEEP_BASE_CHAIN_ID, SWEEP_BASE_SEPOLIA_CHAIN_ID, SWEEP_BASE_SEPOLIA_USDC_ADDRESS, SWEEP_BASE_USDC_ADDRESS, SignerRefusalCode, TRANSFER_WITH_AUTHORIZATION_TYPES, X402AlreadySettledError, X402PaymentHeaderValidationError, X402UnexpectedStatusError, X402_MAX_AUTHORIZATION_WINDOW_SECONDS, X402_SETTLEMENT_FORWARD_MARGIN_SECONDS, addressFromKey, buildSweepAuthorizationMessage, buildSweepTypedData, buildX402ExpectedMessage, compareNodeVersions, composeDescription, decodeBase64Json, decodeBase64Utf8, discoverMerchantMcpUrl, encodeBase64Json, encodeBase64Utf8, encodePaymentProof, havenTools, isErc7710Option, isSupportedNodeVersion, isSweepableChain, normalizePaymentRequired, parsePaymentRequired, parsePaymentRequiredResponse, resolveTokenFromAddress, sameUrl, selectErc7710PaymentOption, selectPaymentOption, selectStandardPaymentOption, selectX402SettlementScheme, signHash, signUserOpTypedDataForDelegation, sweepUsdcAddress, sweepUsdcDomain, toStandardPaymentRequirements, toolDescriptions, unsupportedNodeVersionMessage, validateStandardX402PaymentHeader, verifyPaymentReceipt, verifySignature, x402AssetTransferMethod, x402AuthorizationAmount, x402FacilitatorAddresses };
4564
+ export { AGENT_PAYMENT_FAILURE_CODE_VALUES, AGENT_PAYMENT_NEXT_ACTION_VALUES, AGENT_PAYMENT_PHASE_VALUES, AGENT_PAYMENT_RAIL_VALUES, AgentPaymentFailureCode, AgentPaymentFailureCodeDescriptions, AgentPaymentFailureCodeSchema, AgentPaymentNextAction, AgentPaymentNextActionDescriptions, AgentPaymentNextActionSchema, AgentPaymentPhase, AgentPaymentPhaseDescriptions, AgentPaymentPhaseSchema, AgentPaymentRail, AgentPaymentRailDescriptions, AgentPaymentRailSchema, AgentPaymentWarningCode, DEFAULT_CONFIRMATION_TIMEOUT_MS, DISCOVERY_MAX_BYTES, ERC7710_ASSET_TRANSFER_METHOD, HAVEN_MINIMUM_NODE_VERSION, HAVEN_SKILL_BODY_MD, HAVEN_SKILL_MD, HavenApiError, HavenClient, HavenError, HavenPaymentStateError, HavenSigningError, HavenTimeoutError, HavenUnsupportedSignerVersionError, MERCHANT_DISCOVERY_PATHS, MerchantTimeoutError, RECEIPT_VERSION, SIGNER_UPDATE_FALLBACK, SKILL_FOLDER_NAME, SWEEP_BASE_CHAIN_ID, SWEEP_BASE_SEPOLIA_CHAIN_ID, SWEEP_BASE_SEPOLIA_USDC_ADDRESS, SWEEP_BASE_USDC_ADDRESS, SignerRefusalCode, TRANSFER_WITH_AUTHORIZATION_TYPES, X402AlreadySettledError, X402PaymentHeaderValidationError, X402UnexpectedStatusError, X402_LEGACY_PAYMENT_HEADER_NAME, X402_MAX_AUTHORIZATION_WINDOW_SECONDS, X402_PAYMENT_HEADER_NAME, X402_PAYMENT_HEADER_NAMES_SENT, X402_PAYMENT_REQUIRED_HEADER_NAME, X402_PAYMENT_RESPONSE_HEADER_NAME, X402_SETTLEMENT_FORWARD_MARGIN_SECONDS, addressFromKey, buildSweepAuthorizationMessage, buildSweepTypedData, buildX402ExpectedMessage, compareNodeVersions, composeDescription, decodeBase64Json, decodeBase64Utf8, discoverMerchantMcpUrl, encodeBase64Json, encodeBase64Utf8, encodePaymentProof, havenTools, isErc7710Option, isSupportedNodeVersion, isSweepableChain, normalizePaymentRequired, parsePaymentRequired, parsePaymentRequiredResponse, resolveTokenFromAddress, sameUrl, selectErc7710PaymentOption, selectPaymentOption, selectStandardPaymentOption, selectX402SettlementScheme, signHash, signUserOpTypedDataForDelegation, sweepUsdcAddress, sweepUsdcDomain, toStandardPaymentRequirements, toolDescriptions, unsupportedNodeVersionMessage, validateStandardX402PaymentHeader, verifyPaymentReceipt, verifySignature, x402AssetTransferMethod, x402AuthorizationAmount, x402FacilitatorAddresses };
4380
4565
  //# sourceMappingURL=index.js.map
4381
4566
  //# sourceMappingURL=index.js.map