@haven_ai/sdk 0.0.0-dev.202609180723.d49b927 → 0.0.0-dev.202609180817.b16be14

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.cjs CHANGED
@@ -1420,6 +1420,51 @@ function throwPaymentStateError(label, raw) {
1420
1420
  throw new HavenApiError(message, statusCode, raw);
1421
1421
  }
1422
1422
 
1423
+ // src/x402-retry-target.ts
1424
+ function resolveX402RetryTarget(input) {
1425
+ const requestUrl = input.requestUrl?.trim() || void 0;
1426
+ if (requestUrl) {
1427
+ return {
1428
+ url: requestUrl,
1429
+ source: "request",
1430
+ resourceUrlDiffersFromRequest: requestUrl !== input.resourceUrl
1431
+ };
1432
+ }
1433
+ return { url: input.resourceUrl, source: "resource" };
1434
+ }
1435
+ var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "::1", "[::1]"]);
1436
+ var RESERVED_SUFFIXES = [".test", ".localhost", ".invalid", ".example"];
1437
+ var IPV4_LOOPBACK = /^127(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}$/;
1438
+ function isSecureX402RetryTarget(url) {
1439
+ let parsed;
1440
+ try {
1441
+ parsed = new URL(url);
1442
+ } catch {
1443
+ return false;
1444
+ }
1445
+ if (parsed.protocol === "https:") return true;
1446
+ if (parsed.protocol !== "http:") return false;
1447
+ const host = parsed.hostname.toLowerCase();
1448
+ if (LOOPBACK_HOSTS.has(host) || IPV4_LOOPBACK.test(host)) return true;
1449
+ return RESERVED_SUFFIXES.some((suffix) => host.endsWith(suffix));
1450
+ }
1451
+ var INSECURE_RETRY_TARGET_CODE = "INSECURE_RETRY_TARGET";
1452
+ var HavenInsecureRetryTargetError = class extends HavenError {
1453
+ constructor(url) {
1454
+ super(
1455
+ `Refusing to send a payment header to ${url}: the paid x402 retry must go to an https URL (or a loopback / reserved test host). The merchant's challenge declared this resource URL; retry with the https URL you quoted (pass it as \`url\`), and report a merchant whose challenge downgrades the scheme.`,
1456
+ INSECURE_RETRY_TARGET_CODE,
1457
+ 400
1458
+ );
1459
+ this.url = url;
1460
+ this.name = "HavenInsecureRetryTargetError";
1461
+ }
1462
+ url;
1463
+ };
1464
+ function assertSecureX402RetryTarget(url) {
1465
+ if (!isSecureX402RetryTarget(url)) throw new HavenInsecureRetryTargetError(url);
1466
+ }
1467
+
1423
1468
  // src/mcp-merchant-transport.ts
1424
1469
  var DEFAULT_MERCHANT_TIMEOUT = 3e5;
1425
1470
  var MCP_NOTIFICATION_TIMEOUT = 1e4;
@@ -1577,6 +1622,7 @@ var McpMerchantTransport = class {
1577
1622
  * overwrite, which is worse than the duplicate this change removes.
1578
1623
  */
1579
1624
  async deliverPayment(url, init, paymentHeader) {
1625
+ assertSecureX402RetryTarget(url);
1580
1626
  const headers = new Headers(init?.headers);
1581
1627
  const send = x402PaymentHeaderNamesFor(paymentHeader);
1582
1628
  for (const name of X402_PAYMENT_HEADER_NAMES) {
@@ -2006,6 +2052,11 @@ function buildX402Quote(paymentRequired, request, idempotencyKey, mcpTransport)
2006
2052
  request,
2007
2053
  ...mcpTransport ? { mcpTransport } : {},
2008
2054
  resourceUrl: paymentRequired.resource.url,
2055
+ // #3097: the merchant's declaration vs the URL the caller quoted. The
2056
+ // paid retry goes to the caller's URL; a quote that says the two disagree
2057
+ // is how an agent sees a challenge that downgrades the scheme or moves
2058
+ // the host before it pays.
2059
+ resourceUrlDiffersFromRequest: paymentRequired.resource.url !== request.url,
2009
2060
  description: paymentRequired.resource.description ?? option.description ?? null,
2010
2061
  mimeType: paymentRequired.resource.mimeType ?? option.mimeType ?? null,
2011
2062
  amountAtomic: x402AuthorizationAmount(option),
@@ -4289,10 +4340,10 @@ var toolDescriptions = {
4289
4340
  nextActionGuidance: "On a decline, report the reason to the user and ask them to raise the budget in Haven \u2014 there is no approval queue to wait on. This tool retries the merchant itself while it runs, so do not wait on a signal mid-call. If the process crashes after payment, a later haven_get_payment_status call may report nextAction=retry_original_x402_request \u2014 resume via haven_resume_x402_payment instead of paying again."
4290
4341
  },
4291
4342
  discoverTools: {
4292
- 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.",
4293
- 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.",
4294
- 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.",
4295
- 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.`
4343
+ summary: "Step 1 of a purchase: discover payable services from Haven's curated merchant catalog \u2014 names, prices, the next call.",
4344
+ 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 next tool to call.",
4345
+ behavior: "Use each entry's suggested_tool + suggested_arguments first; an entry without them says why. 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, the hint, 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.",
4346
+ nextActionGuidance: `Call suggested_tool with suggested_arguments VERBATIM (no hint: read suggested_tool_omitted_reason). Confirm the price from the live quote or pay result, not the catalog; if the next tool takes a cap, pass the user's cap as max_amount_human in whole tokens ("no more than 1 USDC" \u2192 max_amount_human: "1"), never atomic units by hand.`
4296
4347
  },
4297
4348
  submitCatalogEntry: {
4298
4349
  summary: "Submit a merchant's payable (x402/MCP) endpoint to Haven's Verified Payable Directory for verification and listing.",
@@ -5087,11 +5138,13 @@ exports.HAVEN_SKILL_MD = HAVEN_SKILL_MD;
5087
5138
  exports.HavenApiError = HavenApiError;
5088
5139
  exports.HavenClient = HavenClient;
5089
5140
  exports.HavenError = HavenError;
5141
+ exports.HavenInsecureRetryTargetError = HavenInsecureRetryTargetError;
5090
5142
  exports.HavenPaymentStateError = HavenPaymentStateError;
5091
5143
  exports.HavenSigningError = HavenSigningError;
5092
5144
  exports.HavenTimeoutError = HavenTimeoutError;
5093
5145
  exports.HavenUnsupportedSignerVersionError = HavenUnsupportedSignerVersionError;
5094
5146
  exports.HavenZeroSettlementHashError = HavenZeroSettlementHashError;
5147
+ exports.INSECURE_RETRY_TARGET_CODE = INSECURE_RETRY_TARGET_CODE;
5095
5148
  exports.MERCHANT_DISCOVERY_PATHS = MERCHANT_DISCOVERY_PATHS;
5096
5149
  exports.MerchantTimeoutError = MerchantTimeoutError;
5097
5150
  exports.RECEIPT_VERSION = RECEIPT_VERSION;
@@ -5114,6 +5167,7 @@ exports.X402_PAYMENT_REQUIRED_HEADER_NAME = X402_PAYMENT_REQUIRED_HEADER_NAME;
5114
5167
  exports.X402_PAYMENT_RESPONSE_HEADER_NAME = X402_PAYMENT_RESPONSE_HEADER_NAME;
5115
5168
  exports.X402_SETTLEMENT_FORWARD_MARGIN_SECONDS = X402_SETTLEMENT_FORWARD_MARGIN_SECONDS;
5116
5169
  exports.addressFromKey = addressFromKey;
5170
+ exports.assertSecureX402RetryTarget = assertSecureX402RetryTarget;
5117
5171
  exports.buildSweepAuthorizationMessage = buildSweepAuthorizationMessage;
5118
5172
  exports.buildSweepTypedData = buildSweepTypedData;
5119
5173
  exports.buildX402ExpectedMessage = buildX402ExpectedMessage;
@@ -5130,6 +5184,7 @@ exports.encodePaymentProof = encodePaymentProof;
5130
5184
  exports.havenTools = havenTools;
5131
5185
  exports.isConnectorChannel = isConnectorChannel;
5132
5186
  exports.isErc7710Option = isErc7710Option;
5187
+ exports.isSecureX402RetryTarget = isSecureX402RetryTarget;
5133
5188
  exports.isSupportedNodeVersion = isSupportedNodeVersion;
5134
5189
  exports.isSweepableChain = isSweepableChain;
5135
5190
  exports.isZeroSettlementTxHash = isZeroSettlementTxHash;
@@ -5139,6 +5194,7 @@ exports.parsePaymentRequiredResponse = parsePaymentRequiredResponse;
5139
5194
  exports.readX402ReceiptPayer = readX402ReceiptPayer;
5140
5195
  exports.resolveConnectorChannel = resolveConnectorChannel;
5141
5196
  exports.resolveTokenFromAddress = resolveTokenFromAddress;
5197
+ exports.resolveX402RetryTarget = resolveX402RetryTarget;
5142
5198
  exports.sameUrl = sameUrl;
5143
5199
  exports.selectErc7710PaymentOption = selectErc7710PaymentOption;
5144
5200
  exports.selectPaymentOption = selectPaymentOption;