@haven_ai/sdk 0.3.0-alpha.0 → 0.5.0-alpha.1

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,17 @@ 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` and `verifySettlementChild` —
29
+ and loads in about a third of the time; use it when you need those helpers
30
+ without the HTTP client. A class imported from either entry is the same class.
31
+ `@haven_ai/sdk/test-support` (#3283) holds test fixture builders shared by
32
+ Haven's own packages; it is not a signing API, nothing at runtime imports it,
33
+ and it carries no semver guarantee.
34
+
24
35
  ## Quick Start
25
36
 
26
37
  ```typescript
@@ -79,6 +90,7 @@ shows up in your Haven dashboard activity feed.
79
90
  - The delegate key signs payment payloads locally. Haven's backend never receives it.
80
91
  - The agent's on-chain budget delegation enforces the agent budget: budget, recipient and expiry are checked by audited caveat enforcers at redemption, not by an off-chain rules engine.
81
92
  - `getAllowances()` / `get_allowances` is the right path for budget, remaining amount, reset period, or "what can I spend?" questions.
93
+ - `checkFunds()` / `haven_check_funds` answers the different question of whether the account actually holds funds behind the budget — as a sufficiency signal (`covered` true/false/null), never as a balance.
82
94
  - If an API key is exposed or lost, rotate it from the Haven agent detail page. The new key is shown once and the old key stops working.
83
95
  - If a delegate key is exposed or lost, a delegation-rail agent is **re-keyed** rather than replaced — same agent, new signing key, budget remainder carried. See [Replacing an agent's signing key](../../docs/product/agent-key-rotation.md).
84
96
 
@@ -105,8 +117,18 @@ const intent = await haven.createIntent({
105
117
  to: '0xabc...',
106
118
  })
107
119
 
108
- // Step 2: Sign the hash (or sign externally)
109
- const signature = haven.sign(intent.signData.hash)
120
+ // Step 2: Sign externally. On the delegation rail (the only rail that pays)
121
+ // the account validates the EIP-712 typed data in intent.signData.typed_data,
122
+ // never the bare hash — a signature over signData.hash is rejected on-chain
123
+ // (AA24). Sign that typed data with your delegate key (viem signTypedData),
124
+ // after checking it with assertUserOpTypedDataBinding(typed_data, hash) AND
125
+ // assertBoundDirectPaymentUserOp(typed_data, yourDelegateAddress) — the
126
+ // first proves the typed data matches the hash, the second that it is your
127
+ // own account redeeming your budget delegation, not a self-call (#3283).
128
+ // There is no public HavenClient method for this step yet; pay() does both
129
+ // for you. HavenClient.sign(hash) and signUserOpTypedDataForDelegation are
130
+ // verbatim primitives: they check nothing.
131
+ const signature = await signTypedDataWithYourDelegateKey(intent.signData.typed_data)
110
132
 
111
133
  // Step 3: Submit the signature
112
134
  await haven.submitSignature(intent.paymentId, signature)
@@ -146,6 +168,23 @@ if (apiResponse.status === 402) {
146
168
  }
147
169
  ```
148
170
 
171
+ **MCP merchants, both dialects (#3118).** A merchant may serve x402 over MCP
172
+ in Haven's HTTP layering (HTTP 402 with the `PAYMENT-REQUIRED` header or JSON
173
+ body, payment in `PAYMENT-SIGNATURE` / `X-PAYMENT`, settlement in
174
+ `PAYMENT-RESPONSE`) or in the official x402 MCP transport profile (a tool
175
+ result with `isError: true` and the `PaymentRequired` as `structuredContent`,
176
+ payment in `params._meta["x402/payment"]`, settlement in
177
+ `result._meta["x402/payment-response"]`). `fetch()`, `quoteX402()`,
178
+ `quoteMcpX402()` and `completeX402MerchantCall()` handle both: a payment-
179
+ required tool result quotes like a 402, the paid retry carries the header and
180
+ — for a `tools/call` body — the `_meta` object, and settlement is read from the
181
+ header first, the `_meta` second. An in-band refusal (an `isError` challenge on
182
+ the paid retry, or `success: false`) is a rejection even under HTTP 200. An
183
+ ordinary tool error, or a successful result that merely resembles a challenge,
184
+ is never paid for. The tool-result challenge is read only from a response
185
+ declaring `application/json` or `text/event-stream`; any other content type is
186
+ passed through untouched rather than buffered.
187
+
149
188
  ### Idempotency: what the key guarantees, and what it costs
150
189
 
151
190
  **Omit `idempotencyKey` and the SDK synthesises one** from the merchant's
@@ -227,7 +266,7 @@ const data = await response.json()
227
266
 
228
267
  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.
229
268
 
230
- 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.
269
+ 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.
231
270
 
232
271
  ## AI Agent Integration
233
272
 
@@ -561,6 +600,18 @@ original `tools/call`, setting BOTH `PAYMENT-SIGNATURE` (x402 v2) and `X-PAYMENT
561
600
  user intent so fresh merchant quotes or sessions do not become duplicate Haven
562
601
  payments.
563
602
 
603
+ If the merchant's session is gone by the time the paid retry arrives (it
604
+ expired, or the merchant restarted), a merchant that answers HTTP 404 with
605
+ JSON-RPC `-32001` **and** `error.data = { settled: false, next_action:
606
+ 'reinitialize_then_retry_same_payment_header' }` is stating its own guarantee
607
+ that nothing was settled; since #3171 `fetch()`, `payX402Quote()`,
608
+ `resumeX402Payment()` and `completeX402MerchantCall()` act on exactly that
609
+ shape — re-initialize once and resend the SAME payment header on the new
610
+ session. A bare `-32001` without the data, any other 404, a failed
611
+ re-initialize, or a second 404 is returned to you unchanged and, after
612
+ funding, recorded as a rejection as before: the SDK never resends on an
613
+ inference, only on the merchant's stated guarantee.
614
+
564
615
  See [`examples/mcp-x402-sse.ts`](./examples/mcp-x402-sse.ts) for a complete
565
616
  MCP flow with initialize, `mcp-session-id`, JSON-RPC `tools/call`, quote
566
617
  inspection, saved resume state, and final retry.