@haven_ai/sdk 0.0.0-dev.202609191028.9c54815 → 0.0.0-dev.202609191429.4d85f59

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
@@ -147,6 +147,23 @@ if (apiResponse.status === 402) {
147
147
  }
148
148
  ```
149
149
 
150
+ **MCP merchants, both dialects (#3118).** A merchant may serve x402 over MCP
151
+ in Haven's HTTP layering (HTTP 402 with the `PAYMENT-REQUIRED` header or JSON
152
+ body, payment in `PAYMENT-SIGNATURE` / `X-PAYMENT`, settlement in
153
+ `PAYMENT-RESPONSE`) or in the official x402 MCP transport profile (a tool
154
+ result with `isError: true` and the `PaymentRequired` as `structuredContent`,
155
+ payment in `params._meta["x402/payment"]`, settlement in
156
+ `result._meta["x402/payment-response"]`). `fetch()`, `quoteX402()`,
157
+ `quoteMcpX402()` and `completeX402MerchantCall()` handle both: a payment-
158
+ required tool result quotes like a 402, the paid retry carries the header and
159
+ — for a `tools/call` body — the `_meta` object, and settlement is read from the
160
+ header first, the `_meta` second. An in-band refusal (an `isError` challenge on
161
+ the paid retry, or `success: false`) is a rejection even under HTTP 200. An
162
+ ordinary tool error, or a successful result that merely resembles a challenge,
163
+ is never paid for. The tool-result challenge is read only from a response
164
+ declaring `application/json` or `text/event-stream`; any other content type is
165
+ passed through untouched rather than buffered.
166
+
150
167
  ### Idempotency: what the key guarantees, and what it costs
151
168
 
152
169
  **Omit `idempotencyKey` and the SDK synthesises one** from the merchant's
package/dist/index.cjs CHANGED
@@ -1297,6 +1297,9 @@ function mapPaymentReceipt(raw) {
1297
1297
  if ("payment_intent_id" in raw) {
1298
1298
  receipt.paymentIntentId = raw.payment_intent_id ?? null;
1299
1299
  }
1300
+ if (raw.scope) {
1301
+ receipt.scope = { source: raw.scope.source, filter: raw.scope.filter };
1302
+ }
1300
1303
  if ("approval_request_id" in raw) {
1301
1304
  receipt.approvalRequestId = raw.approval_request_id ?? null;
1302
1305
  }
@@ -1488,6 +1491,9 @@ var MCP_NOTIFICATION_TIMEOUT = 1e4;
1488
1491
  var MCP_PROTOCOL_VERSION = "2025-06-18";
1489
1492
  var MCP_ACCEPT = "application/json, text/event-stream";
1490
1493
  var MCP_CLIENT_INFO = { name: "haven-sdk", version: "1" };
1494
+ var MCP_X402_PAYMENT_META_KEY = "x402/payment";
1495
+ var MCP_X402_PAYMENT_RESPONSE_META_KEY = "x402/payment-response";
1496
+ var X402_RETRY_REJECTED_STATUS = 402;
1491
1497
  var McpMerchantTransport = class {
1492
1498
  merchantTimeout;
1493
1499
  fetchImpl;
@@ -1583,7 +1589,7 @@ var McpMerchantTransport = class {
1583
1589
  return response;
1584
1590
  }
1585
1591
  const message = selectJsonRpcResult(parseSseJsonRpcMessages(text));
1586
- if (!message) return response;
1592
+ if (!message || !("result" in message || "error" in message)) return response;
1587
1593
  const body = "result" in message ? message.result : message;
1588
1594
  const headers = new Headers(response.headers);
1589
1595
  headers.set("content-type", "application/json");
@@ -1646,7 +1652,34 @@ var McpMerchantTransport = class {
1646
1652
  if (send.includes(name)) headers.set(name, paymentHeader);
1647
1653
  else headers.delete(name);
1648
1654
  }
1649
- return this.fetch(url, { ...init, headers });
1655
+ return this.fetch(url, withMcpPaymentMeta({ ...init, headers }, paymentHeader));
1656
+ }
1657
+ /**
1658
+ * #3118: read a native MCP payment-required tool result from a response
1659
+ * WITHOUT consuming it — JSON or SSE framed. `undefined` for anything that
1660
+ * is not an `isError: true` tool result carrying a valid `PaymentRequired`
1661
+ * (structured content first, text fallback second), so an ordinary tool
1662
+ * error, a successful result that merely resembles a challenge, or an
1663
+ * unparseable body is never mistaken for a payment demand.
1664
+ */
1665
+ async extractToolResultChallenge(response, init) {
1666
+ const toolResult = await this.readToolResult(response, init);
1667
+ return toolResult ? extractMcpPaymentRequired(toolResult) : void 0;
1668
+ }
1669
+ /**
1670
+ * #3118: the tool RESULT a response carries, read WITHOUT consuming it, or
1671
+ * `undefined`. Only a JSON or SSE body is read at all (#3155 review S2):
1672
+ * a non-402 answer used to be returned untouched, and buffering an
1673
+ * arbitrary body — a streaming or binary paid resource — to look for a
1674
+ * challenge would block the caller until the stream ended or the merchant
1675
+ * timeout fired. A merchant that speaks JSON-RPC declares one of those two
1676
+ * content types.
1677
+ */
1678
+ async readToolResult(response, init) {
1679
+ if (init !== void 0 && !isJsonRpcToolsCallBody(init.body)) return void 0;
1680
+ const contentType = (response.headers.get("content-type") ?? "").toLowerCase();
1681
+ if (!contentType.includes("application/json") && !contentType.includes("text/event-stream")) return void 0;
1682
+ return mcpToolResultOf(await this.readMessage(response));
1650
1683
  }
1651
1684
  async notifyInitialized(url, init, sessionId, wallet) {
1652
1685
  try {
@@ -1668,6 +1701,88 @@ var McpMerchantTransport = class {
1668
1701
  }
1669
1702
  }
1670
1703
  };
1704
+ function mcpToolResultOf(value) {
1705
+ if (!isRecord(value)) return void 0;
1706
+ if ("jsonrpc" in value) {
1707
+ return isRecord(value.result) ? value.result : void 0;
1708
+ }
1709
+ if ("isError" in value || Array.isArray(value.content) || isRecord(value._meta)) return value;
1710
+ return void 0;
1711
+ }
1712
+ function extractMcpPaymentRequired(toolResult) {
1713
+ if (toolResult.isError !== true) return void 0;
1714
+ if (isRecord(toolResult.structuredContent)) {
1715
+ const parsed = normalizePaymentRequired(toolResult.structuredContent);
1716
+ if (parsed) return parsed;
1717
+ }
1718
+ if (Array.isArray(toolResult.content)) {
1719
+ for (const item of toolResult.content) {
1720
+ if (!isRecord(item) || item.type !== "text" || typeof item.text !== "string") continue;
1721
+ let candidate;
1722
+ try {
1723
+ candidate = JSON.parse(item.text);
1724
+ } catch {
1725
+ continue;
1726
+ }
1727
+ const parsed = normalizePaymentRequired(candidate);
1728
+ if (parsed) return parsed;
1729
+ }
1730
+ }
1731
+ return void 0;
1732
+ }
1733
+ function mcpSettlementFromToolResult(toolResult) {
1734
+ const meta = toolResult._meta;
1735
+ if (!isRecord(meta)) return void 0;
1736
+ const settlement = meta[MCP_X402_PAYMENT_RESPONSE_META_KEY];
1737
+ if (!isRecord(settlement) || typeof settlement.success !== "boolean") return void 0;
1738
+ return {
1739
+ success: settlement.success,
1740
+ ...typeof settlement.transaction === "string" ? { transaction: settlement.transaction } : {},
1741
+ ...typeof settlement.network === "string" ? { network: settlement.network } : {},
1742
+ ...typeof settlement.payer === "string" ? { payer: settlement.payer } : {},
1743
+ ...typeof settlement.errorReason === "string" ? { errorReason: settlement.errorReason } : {}
1744
+ };
1745
+ }
1746
+ function withMcpPaymentMeta(init, paymentHeader) {
1747
+ const request = parseJsonRpcToolsCall(init.body);
1748
+ if (!request) return init;
1749
+ let envelope;
1750
+ try {
1751
+ envelope = decodeBase64Json(paymentHeader);
1752
+ } catch {
1753
+ return init;
1754
+ }
1755
+ if (!isRecord(envelope)) return init;
1756
+ const params = request.params;
1757
+ const existingMeta = isRecord(params._meta) ? params._meta : {};
1758
+ return {
1759
+ ...init,
1760
+ body: JSON.stringify({
1761
+ ...request,
1762
+ params: { ...params, _meta: { ...existingMeta, [MCP_X402_PAYMENT_META_KEY]: envelope } }
1763
+ })
1764
+ };
1765
+ }
1766
+ function encodeMcpSettlementReceipt(settlement) {
1767
+ return encodeBase64Json(settlement);
1768
+ }
1769
+ function parseJsonRpcToolsCall(body) {
1770
+ if (typeof body !== "string") return void 0;
1771
+ let request;
1772
+ try {
1773
+ request = JSON.parse(body);
1774
+ } catch {
1775
+ return void 0;
1776
+ }
1777
+ if (!isRecord(request) || request.method !== "tools/call" || !isRecord(request.params)) return void 0;
1778
+ return request;
1779
+ }
1780
+ function isJsonRpcToolsCallBody(body) {
1781
+ return parseJsonRpcToolsCall(body) !== void 0;
1782
+ }
1783
+ function isRecord(value) {
1784
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1785
+ }
1671
1786
  async function captureMerchantResponse(response) {
1672
1787
  const merchant_body = await response.text().catch(() => "");
1673
1788
  return {
@@ -2821,7 +2936,11 @@ var MerchantCompletion = class {
2821
2936
  initialInit,
2822
2937
  receipt.paymentHeader
2823
2938
  );
2824
- if (!retryResponse.ok) {
2939
+ const toolResult = retryResponse.ok ? await this.merchantTransport.readToolResult(retryResponse) : void 0;
2940
+ const inBandChallenge = toolResult ? extractMcpPaymentRequired(toolResult) : void 0;
2941
+ const metaSettlement = toolResult ? mcpSettlementFromToolResult(toolResult) : void 0;
2942
+ const inBandRejection = inBandChallenge !== void 0 || metaSettlement?.success === false;
2943
+ if (!retryResponse.ok || inBandRejection) {
2825
2944
  const merchant = await captureMerchantResponse(retryResponse);
2826
2945
  await this.recordRetryRejected({
2827
2946
  rail: "x402",
@@ -2836,7 +2955,7 @@ var MerchantCompletion = class {
2836
2955
  });
2837
2956
  throw new HavenApiError(
2838
2957
  "x402 retry failed after Haven funded the delegate wallet; reconciliation may be required.",
2839
- merchant.merchant_status,
2958
+ inBandRejection ? X402_RETRY_REJECTED_STATUS : merchant.merchant_status,
2840
2959
  {
2841
2960
  marker: "x402_retry_rejected_after_funding",
2842
2961
  payment_id: receipt.paymentId,
@@ -2848,7 +2967,10 @@ var MerchantCompletion = class {
2848
2967
  }
2849
2968
  );
2850
2969
  }
2851
- const merchantSettlement = parseMerchantSettlement(retryResponse.headers.get("PAYMENT-RESPONSE"));
2970
+ const headerReceipt = retryResponse.headers.get("PAYMENT-RESPONSE");
2971
+ const protocolReceiptHeader = headerReceipt ?? (metaSettlement ? encodeMcpSettlementReceipt(metaSettlement) : void 0);
2972
+ const protocolReceiptHeaderName = headerReceipt ? "PAYMENT-RESPONSE" : metaSettlement ? `_meta.${MCP_X402_PAYMENT_RESPONSE_META_KEY}` : void 0;
2973
+ const merchantSettlement = parseMerchantSettlement(protocolReceiptHeader ?? null);
2852
2974
  if (receipt.merchant && merchantSettlement.settlementTxHash) {
2853
2975
  receipt.merchant.settlementTxHash = merchantSettlement.settlementTxHash;
2854
2976
  receipt.merchant.settlementExplorerUrl = buildExplorerUrl(
@@ -2866,8 +2988,8 @@ var MerchantCompletion = class {
2866
2988
  selectedPayment: receipt.accepted,
2867
2989
  paymentProofHeaderName: x402PaymentHeaderNamesSent(receipt.paymentHeader),
2868
2990
  paymentProofHeader: receipt.paymentHeader,
2869
- protocolReceiptHeaderName: "PAYMENT-RESPONSE",
2870
- protocolReceiptHeader: retryResponse.headers.get("PAYMENT-RESPONSE") ?? void 0
2991
+ protocolReceiptHeaderName,
2992
+ protocolReceiptHeader
2871
2993
  });
2872
2994
  await this.reportMerchantReceipt(receipt.paymentId, retryResponse);
2873
2995
  return retryResponse;
@@ -3842,6 +3964,11 @@ var HavenClient = class {
3842
3964
  const request = snapshotX402Request(url, initialInit);
3843
3965
  const response = await this.merchantTransport.fetch(url, initialInit);
3844
3966
  if (response.status !== 402) {
3967
+ const toolResultChallenge = await this.merchantTransport.extractToolResultChallenge(response, initialInit);
3968
+ if (toolResultChallenge) {
3969
+ const mcpTransport2 = await this.merchantTransport.detect(url, toolResultChallenge, response);
3970
+ return buildX402Quote(toolResultChallenge, request, options.idempotencyKey, mcpTransport2);
3971
+ }
3845
3972
  let body;
3846
3973
  try {
3847
3974
  body = await response.clone().json();
@@ -4008,10 +4135,11 @@ var HavenClient = class {
4008
4135
  return this.merchantCompletion.retryRequest(url ?? paymentRequired.resource.url, initialInit, paymentRequired, receipt);
4009
4136
  }
4010
4137
  /**
4011
- * Fetch wrapper that automatically handles HTTP 402 responses.
4138
+ * Fetch wrapper that automatically handles HTTP 402 responses — and, since
4139
+ * #3118, a native-MCP payment-required tool result answered under HTTP 200.
4012
4140
  *
4013
- * Works like the standard `fetch()` but intercepts 402 responses,
4014
- * pays via x402 through Haven, and retries the request.
4141
+ * Works like the standard `fetch()` but intercepts 402 responses (or that
4142
+ * tool-result challenge), pays via x402 through Haven, and retries the request.
4015
4143
  *
4016
4144
  * ```ts
4017
4145
  * const response = await haven.fetch('https://paid-api.com/data')
@@ -4019,8 +4147,9 @@ var HavenClient = class {
4019
4147
  * ```
4020
4148
  *
4021
4149
  * **MCP-over-x402 auto-handshake (issue #315):** when the endpoint is
4022
- * MCP-shaped — the URL path ends in `/mcp`, or the 402 body carries a
4023
- * Coinbase Bazaar `extensions.bazaar` block — the SDK runs the MCP
4150
+ * MCP-shaped — the URL path ends in `/mcp`, or the 402 body (or, since
4151
+ * #3118, the tool-result challenge) carries a Coinbase Bazaar
4152
+ * `extensions.bazaar` block — the SDK runs the MCP
4024
4153
  * `initialize` handshake, threads the resulting `mcp-session-id`,
4025
4154
  * `Accept: application/json, text/event-stream`, and `x402-wallet` headers
4026
4155
  * through every request, and collapses SSE responses to the JSON-RPC
@@ -4038,16 +4167,23 @@ var HavenClient = class {
4038
4167
  let requestInit = withX402Wallet(init, x402PayerAddress(this.delegateAddress, this.x402Wallet));
4039
4168
  if (mcpSessionId) requestInit = this.merchantTransport.withSessionHeaders(requestInit, mcpSessionId);
4040
4169
  const response = await this.merchantTransport.fetch(url, requestInit);
4041
- if (response.status !== 402) {
4042
- return mcpSessionId ? this.merchantTransport.surfaceResult(response) : response;
4043
- }
4044
4170
  let paymentRequired;
4045
- try {
4046
- paymentRequired = await parsePaymentRequiredResponse(response);
4047
- } catch {
4048
- return response;
4171
+ let nativeChallenge = false;
4172
+ if (response.status !== 402) {
4173
+ const toolResultChallenge = await this.merchantTransport.extractToolResultChallenge(response, requestInit);
4174
+ if (!toolResultChallenge) {
4175
+ return mcpSessionId ? this.merchantTransport.surfaceResult(response) : response;
4176
+ }
4177
+ paymentRequired = toolResultChallenge;
4178
+ nativeChallenge = true;
4179
+ } else {
4180
+ try {
4181
+ paymentRequired = await parsePaymentRequiredResponse(response);
4182
+ } catch {
4183
+ return response;
4184
+ }
4049
4185
  }
4050
- if (!mcpSessionId && await this.merchantTransport.hasBazaarExtension(response)) {
4186
+ if (!mcpSessionId && (paymentRequired.extensions?.bazaar != null || await this.merchantTransport.hasBazaarExtension(response))) {
4051
4187
  mcpSessionId = await this.merchantTransport.initialize(url, init);
4052
4188
  if (mcpSessionId) requestInit = this.merchantTransport.withSessionHeaders(requestInit, mcpSessionId);
4053
4189
  }
@@ -4069,7 +4205,7 @@ var HavenClient = class {
4069
4205
  throw err;
4070
4206
  }
4071
4207
  const retryResponse = await this.merchantCompletion.retryRequest(url, requestInit, paymentRequired, receipt);
4072
- return mcpSessionId ? this.merchantTransport.surfaceResult(retryResponse) : retryResponse;
4208
+ return mcpSessionId || nativeChallenge ? this.merchantTransport.surfaceResult(retryResponse) : retryResponse;
4073
4209
  }
4074
4210
  /**
4075
4211
  * Deliver an already-signed x402 payment header to the merchant and return
@@ -4130,9 +4266,7 @@ var HavenClient = class {
4130
4266
  let requestInit = withX402Wallet(input.init, x402Wallet) ?? {};
4131
4267
  if (mcpSessionId) requestInit = this.merchantTransport.withSessionHeaders(requestInit, mcpSessionId);
4132
4268
  const response = await this.merchantTransport.deliverPayment(input.url, requestInit, input.paymentHeader);
4133
- const surfaced = mcpSessionId ? await this.merchantTransport.surfaceResult(response) : response;
4134
- const protocolReceiptHeader = surfaced.headers.get("PAYMENT-RESPONSE") ?? void 0;
4135
- const settlement = parseMerchantSettlement(protocolReceiptHeader ?? null);
4269
+ const surfaced = await this.merchantTransport.surfaceResult(response);
4136
4270
  const text = await surfaced.text();
4137
4271
  let body;
4138
4272
  try {
@@ -4140,8 +4274,16 @@ var HavenClient = class {
4140
4274
  } catch {
4141
4275
  body = text;
4142
4276
  }
4277
+ const toolResult = mcpToolResultOf(body);
4278
+ const inBandChallenge = toolResult ? extractMcpPaymentRequired(toolResult) : void 0;
4279
+ const metaSettlement = toolResult ? mcpSettlementFromToolResult(toolResult) : void 0;
4280
+ const headerReceipt = surfaced.headers.get("PAYMENT-RESPONSE") ?? void 0;
4281
+ const protocolReceiptHeaderName = headerReceipt ? "PAYMENT-RESPONSE" : metaSettlement ? `_meta.${MCP_X402_PAYMENT_RESPONSE_META_KEY}` : void 0;
4282
+ const protocolReceiptHeader = headerReceipt ?? (metaSettlement ? encodeMcpSettlementReceipt(metaSettlement) : void 0);
4283
+ const settlement = parseMerchantSettlement(protocolReceiptHeader ?? null);
4284
+ const merchantAccepted = surfaced.ok && inBandChallenge === void 0 && metaSettlement?.success !== false;
4143
4285
  let evidenceOutcome;
4144
- if (!surfaced.ok) {
4286
+ if (!merchantAccepted) {
4145
4287
  if (!input.noFundingLeg && fundingTxHash) {
4146
4288
  await this.merchantCompletion.recordRetryRejected({
4147
4289
  rail: "x402",
@@ -4171,7 +4313,7 @@ var HavenClient = class {
4171
4313
  merchantStatus: surfaced.status,
4172
4314
  paymentProofHeaderName: x402PaymentHeaderNamesSent(input.paymentHeader),
4173
4315
  paymentProofHeader: input.paymentHeader,
4174
- protocolReceiptHeaderName: protocolReceiptHeader ? "PAYMENT-RESPONSE" : void 0,
4316
+ protocolReceiptHeaderName,
4175
4317
  protocolReceiptHeader
4176
4318
  });
4177
4319
  }
@@ -4179,9 +4321,10 @@ var HavenClient = class {
4179
4321
  }
4180
4322
  return {
4181
4323
  status: surfaced.status,
4182
- ok: surfaced.ok,
4324
+ ok: merchantAccepted,
4183
4325
  body,
4184
- settlementTxHash: settlement.settlementTxHash ?? void 0,
4326
+ // #3155 review S3: a transaction beside a rejection is not a settlement.
4327
+ settlementTxHash: merchantAccepted ? settlement.settlementTxHash ?? void 0 : void 0,
4185
4328
  evidenceOutcome
4186
4329
  };
4187
4330
  }
@@ -4371,12 +4514,12 @@ var toolDescriptions = {
4371
4514
  payX402OneShot: {
4372
4515
  summary: "Fetch an x402 paid HTTP resource in a single call. Handles the full probe -> pay -> retry round trip and returns the merchant response.",
4373
4516
  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.",
4374
- 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.",
4517
+ behavior: "Calls the URL, parses any HTTP 402 x402 challenge (or, since #3118, a native-MCP payment-required tool result under HTTP 200), 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 with no payment-required tool result, returns it unchanged without contacting Haven.",
4375
4518
  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_account_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."
4376
4519
  },
4377
4520
  resumeX402: {
4378
4521
  summary: "Resume an x402 payment whose Haven-side authorization already succeeded but whose merchant retry did not complete.",
4379
- 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.",
4522
+ behavior: "Accepts either resume_state or payment_id, validates the x402 details against the authorized funding, and retries the merchant request with the signed payment header (PAYMENT-SIGNATURE, plus legacy X-PAYMENT on EIP-3009 only; also params._meta on an MCP tools/call body). No new Haven payment is created.",
4380
4523
  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."
4381
4524
  },
4382
4525
  // #1328: quoteMpp / payMpp / resumeMpp (the mpp_demo challenge/quote/resume
@@ -4415,8 +4558,8 @@ var toolDescriptions = {
4415
4558
  },
4416
4559
  listReceipts: {
4417
4560
  summary: "List machine-payment receipts, newest first, by page.",
4418
- selectionGuidance: "For transaction history or payment evidence; use the allowance tool instead for remaining allowance or what-can-I-spend questions.",
4419
- behavior: "Page: { receipts, total, hasMore, nextCursor }; total 0 = none exist (no indexing delay); hasMore = cut at limit, send nextCursor as cursor. parties.treasuryAccount is Haven's authoritative payer. protocolReceiptPayload is the merchant's PAYMENT-RESPONSE, relayed verbatim: merchant-controlled, unverified, not Haven's record; payer may differ from payerAddress. Proof header values are omitted.",
4561
+ selectionGuidance: "This agent's payment evidence, not the wallet's transaction history (sweeps, funding legs, other agents); use the allowance tool instead for remaining allowance or what-can-I-spend questions.",
4562
+ behavior: "Page { receipts, total, hasMore, nextCursor }; hasMore: pass nextCursor as cursor. parties.treasuryAccount is Haven's authoritative payer. protocolReceiptPayload is the merchant's PAYMENT-RESPONSE relayed verbatim: merchant-controlled, unverified, not Haven's record; payer may differ from payerAddress. Proof header values omitted.",
4420
4563
  nextActionGuidance: ""
4421
4564
  },
4422
4565
  verifyReceipt: {