@haven_ai/sdk 0.4.0-alpha.0 → 0.6.0-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/README.md CHANGED
@@ -21,6 +21,18 @@ and managing the account from the shell with `@haven_ai/cli`.
21
21
  npm install @haven_ai/sdk@alpha
22
22
  ```
23
23
 
24
+ The package has three entries. `@haven_ai/sdk` is the full client. `@haven_ai/sdk/edge`
25
+ (#3173) is the ethers-free subset the local signer imports — error classes, the
26
+ typed-next-step builder, the x402 message builders, viem-based key helpers, and
27
+ (#3283) the signing-surface guard: `assertBoundDirectPaymentUserOp`, the
28
+ redemption guard, `deriveDelegateAccountAddress`, `verifySettlementChild` and
29
+ (#3281) the funding-leg recipient pin `assertFundingLegPaysDelegate` —
30
+ and loads in about a third of the time; use it when you need those helpers
31
+ without the HTTP client. A class imported from either entry is the same class.
32
+ `@haven_ai/sdk/test-support` (#3283) holds test fixture builders shared by
33
+ Haven's own packages; it is not a signing API, nothing at runtime imports it,
34
+ and it carries no semver guarantee.
35
+
24
36
  ## Quick Start
25
37
 
26
38
  ```typescript
@@ -106,8 +118,18 @@ const intent = await haven.createIntent({
106
118
  to: '0xabc...',
107
119
  })
108
120
 
109
- // Step 2: Sign the hash (or sign externally)
110
- const signature = haven.sign(intent.signData.hash)
121
+ // Step 2: Sign externally. On the delegation rail (the only rail that pays)
122
+ // the account validates the EIP-712 typed data in intent.signData.typed_data,
123
+ // never the bare hash — a signature over signData.hash is rejected on-chain
124
+ // (AA24). Sign that typed data with your delegate key (viem signTypedData),
125
+ // after checking it with assertUserOpTypedDataBinding(typed_data, hash) AND
126
+ // assertBoundDirectPaymentUserOp(typed_data, yourDelegateAddress) — the
127
+ // first proves the typed data matches the hash, the second that it is your
128
+ // own account redeeming your budget delegation, not a self-call (#3283).
129
+ // There is no public HavenClient method for this step yet; pay() does both
130
+ // for you. HavenClient.sign(hash) and signUserOpTypedDataForDelegation are
131
+ // verbatim primitives: they check nothing.
132
+ const signature = await signTypedDataWithYourDelegateKey(intent.signData.typed_data)
111
133
 
112
134
  // Step 3: Submit the signature
113
135
  await haven.submitSignature(intent.paymentId, signature)
@@ -147,6 +169,23 @@ if (apiResponse.status === 402) {
147
169
  }
148
170
  ```
149
171
 
172
+ **MCP merchants, both dialects (#3118).** A merchant may serve x402 over MCP
173
+ in Haven's HTTP layering (HTTP 402 with the `PAYMENT-REQUIRED` header or JSON
174
+ body, payment in `PAYMENT-SIGNATURE` / `X-PAYMENT`, settlement in
175
+ `PAYMENT-RESPONSE`) or in the official x402 MCP transport profile (a tool
176
+ result with `isError: true` and the `PaymentRequired` as `structuredContent`,
177
+ payment in `params._meta["x402/payment"]`, settlement in
178
+ `result._meta["x402/payment-response"]`). `fetch()`, `quoteX402()`,
179
+ `quoteMcpX402()` and `completeX402MerchantCall()` handle both: a payment-
180
+ required tool result quotes like a 402, the paid retry carries the header and
181
+ — for a `tools/call` body — the `_meta` object, and settlement is read from the
182
+ header first, the `_meta` second. An in-band refusal (an `isError` challenge on
183
+ the paid retry, or `success: false`) is a rejection even under HTTP 200. An
184
+ ordinary tool error, or a successful result that merely resembles a challenge,
185
+ is never paid for. The tool-result challenge is read only from a response
186
+ declaring `application/json` or `text/event-stream`; any other content type is
187
+ passed through untouched rather than buffered.
188
+
150
189
  ### Idempotency: what the key guarantees, and what it costs
151
190
 
152
191
  **Omit `idempotencyKey` and the SDK synthesises one** from the merchant's
@@ -228,7 +267,7 @@ const data = await response.json()
228
267
 
229
268
  Merchant-verified x402 retries use the official EIP-3009 `exact` scheme on Base USDC (`base` / `eip155:8453`). `haven.fetch()` sends the payment under `PAYMENT-SIGNATURE` (the x402 v2 name), and on the EIP-3009 bridge also under `X-PAYMENT` (v1), so a merchant on either version reads it. **On erc7710 it sends the v2 name ALONE**: that payload is always x402 v2, and its header carries a whole delegation chain, so duplicating it overflows the merchant's header limit and the request is refused with HTTP 431. If you build the retry yourself, follow the same rule. Haven's older tx-hash proof helper remains exported for Haven-native integrations; it is a different payload that happens to have shared the v2 name, and it is not what `haven.fetch()` sends.
230
269
 
231
- For standard x402, the `x402-wallet` identity is the agent delegate wallet, because that is the wallet that signs and settles the merchant payment. Integrations that scope access by Haven wallet/Safe address should use a Haven-native flow instead of standard merchant x402.
270
+ For standard x402, the `x402-wallet` identity is the agent delegate wallet, because that is the wallet that signs and settles the merchant payment. Integrations that scope access by Haven wallet address should use a Haven-native flow instead of standard merchant x402.
232
271
 
233
272
  ## AI Agent Integration
234
273
 
@@ -562,6 +601,18 @@ original `tools/call`, setting BOTH `PAYMENT-SIGNATURE` (x402 v2) and `X-PAYMENT
562
601
  user intent so fresh merchant quotes or sessions do not become duplicate Haven
563
602
  payments.
564
603
 
604
+ If the merchant's session is gone by the time the paid retry arrives (it
605
+ expired, or the merchant restarted), a merchant that answers HTTP 404 with
606
+ JSON-RPC `-32001` **and** `error.data = { settled: false, next_action:
607
+ 'reinitialize_then_retry_same_payment_header' }` is stating its own guarantee
608
+ that nothing was settled; since #3171 `fetch()`, `payX402Quote()`,
609
+ `resumeX402Payment()` and `completeX402MerchantCall()` act on exactly that
610
+ shape — re-initialize once and resend the SAME payment header on the new
611
+ session. A bare `-32001` without the data, any other 404, a failed
612
+ re-initialize, or a second 404 is returned to you unchanged and, after
613
+ funding, recorded as a rejection as before: the SDK never resends on an
614
+ inference, only on the merchant's stated guarantee.
615
+
565
616
  See [`examples/mcp-x402-sse.ts`](./examples/mcp-x402-sse.ts) for a complete
566
617
  MCP flow with initialize, `mcp-session-id`, JSON-RPC `tools/call`, quote
567
618
  inspection, saved resume state, and final retry.