@haven_ai/sdk 0.1.30-alpha.0 → 0.1.31-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 +110 -91
- package/dist/index.cjs +175 -109
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +127 -37
- package/dist/index.d.ts +127 -37
- package/dist/index.js +175 -109
- package/dist/index.js.map +1 -1
- package/examples/mcp-x402-sse.ts +2 -2
- package/examples/x402_openapi_python.py +8 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -51,8 +51,9 @@ const response = await haven.fetch('https://your-x402-endpoint.example/resource'
|
|
|
51
51
|
const data = await response.json()
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
The payment fits within the agent's on-chain
|
|
55
|
-
|
|
54
|
+
The payment fits within the agent's on-chain budget — anything outside it is
|
|
55
|
+
declined before any money moves, never queued for you to approve later — and it
|
|
56
|
+
shows up in your Haven dashboard activity feed.
|
|
56
57
|
|
|
57
58
|
## Supported Networks & Tokens
|
|
58
59
|
|
|
@@ -65,13 +66,20 @@ approval if it exceeds it), and shows up in your Haven dashboard activity feed.
|
|
|
65
66
|
|
|
66
67
|
- The Haven API key identifies the agent. It is not payment authority.
|
|
67
68
|
- The delegate key signs payment payloads locally. Haven's backend never receives it.
|
|
68
|
-
-
|
|
69
|
+
- 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.
|
|
69
70
|
- `getAllowances()` / `get_allowances` is the right path for budget, remaining amount, reset period, or "what can I spend?" questions.
|
|
70
71
|
- 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.
|
|
71
|
-
- 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).
|
|
72
|
+
- 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).
|
|
72
73
|
|
|
73
74
|
## Step-by-Step API
|
|
74
75
|
|
|
76
|
+
Discovery and listing: `discoverTools({ verified?: 'any' | 'verified' | 'operator' })` returns the merged
|
|
77
|
+
catalog — operator-curated plus `verified_payable` directory entries (epic #1717), each with `source`,
|
|
78
|
+
`domainVerified` and `verifiedPayable`. `submitCatalogEntry(resourceUrl)` submits a merchant endpoint to
|
|
79
|
+
the Verified Payable Directory (queue-only; the seller still must prove domain ownership before listing),
|
|
80
|
+
and `getCatalogSubmissionStatus(id)` returns coarse status plus the ownership-proof instructions while
|
|
81
|
+
the submission can still prove ownership.
|
|
82
|
+
|
|
75
83
|
For agents that need control over each step (e.g., external signing):
|
|
76
84
|
|
|
77
85
|
```typescript
|
|
@@ -96,11 +104,11 @@ const result = await haven.waitForConfirmation(intent.paymentId)
|
|
|
96
104
|
|
|
97
105
|
Production merchant acceptance, facilitator, settlement, fiat, or acquiring functionality needs separate product and legal review under the repo's [CASP / MiCA guardrails](../../docs/regulatory/casp-risk-guardrails.md). The hosted x402 endpoint is an internal technical demo, not a merchant settlement product.
|
|
98
106
|
|
|
99
|
-
The SDK supports [x402](https://x402.org) client flows. When an API returns HTTP 402, the SDK evaluates the challenge against the agent's
|
|
107
|
+
The SDK supports [x402](https://x402.org) client flows. When an API returns HTTP 402, the SDK evaluates the challenge against the agent's on-chain budget, uses the configured delegate key for the required signature, and retries automatically:
|
|
100
108
|
|
|
101
109
|
```typescript
|
|
102
110
|
// Automatic — fetch() intercepts 402, pays, and retries.
|
|
103
|
-
// Use a stable idempotencyKey
|
|
111
|
+
// Use a stable idempotencyKey so one user intent stays one Haven payment.
|
|
104
112
|
const response = await haven.fetch(
|
|
105
113
|
'https://paid-api.example.com/data',
|
|
106
114
|
undefined,
|
|
@@ -172,7 +180,7 @@ delegate's balance, so an accidental key collision refuses on the weaker
|
|
|
172
180
|
for.
|
|
173
181
|
|
|
174
182
|
**Resuming is not affected by that.** When you are following the documented
|
|
175
|
-
|
|
183
|
+
resume flow — re-calling after a funding leg confirms, or calling
|
|
176
184
|
`resumeAuthorizedX402({ paymentId })` — you named the payment, so an
|
|
177
185
|
unverifiable balance lets the resume proceed as before. Only a balance
|
|
178
186
|
verified *absent* refuses there. The stricter default applies solely to the
|
|
@@ -184,8 +192,7 @@ no funding leg and no delegate balance to exhaust.
|
|
|
184
192
|
|
|
185
193
|
For agents that need to inspect the price before paying, use the quote-first
|
|
186
194
|
path. `quoteX402()` probes the merchant and parses the HTTP 402 response, but it
|
|
187
|
-
does not create a Haven payment,
|
|
188
|
-
transaction.
|
|
195
|
+
does not create a Haven payment, signature, or on-chain transaction.
|
|
189
196
|
|
|
190
197
|
```typescript
|
|
191
198
|
const quote = await haven.quoteX402(
|
|
@@ -238,13 +245,13 @@ for (const block of response.content) {
|
|
|
238
245
|
|
|
239
246
|
| Tool | Description |
|
|
240
247
|
|------|-------------|
|
|
241
|
-
| `make_payment` | Request and sign a payment from the user-controlled
|
|
242
|
-
| `get_payment_status` | Check the status of a payment intent
|
|
243
|
-
| `get_allowances` | Read configured and on-chain
|
|
248
|
+
| `make_payment` | Request and sign a payment from the user-controlled account within its on-chain budget |
|
|
249
|
+
| `get_payment_status` | Check the status of a payment intent |
|
|
250
|
+
| `get_allowances` | Read configured and on-chain budget state, including spent and remaining budget |
|
|
244
251
|
| `authorize_x402_payment` | Authorize a policy-limited x402 payment and return a payment header for an HTTP 402 resource |
|
|
245
|
-
| `resume_x402_payment` | Resume an
|
|
252
|
+
| `resume_x402_payment` | Resume an authorized x402 payment and return a merchant payment header without creating a duplicate payment |
|
|
246
253
|
|
|
247
|
-
Use `get_allowances` for allowance, budget, spend-limit, remaining amount, reset-period, or "what can I spend?" questions. Payment tools still require the agent-held delegate key and on-chain
|
|
254
|
+
Use `get_allowances` for allowance, budget, spend-limit, remaining amount, reset-period, or "what can I spend?" questions. Payment tools still require the agent-held delegate key and the on-chain budget delegation; the Haven API key identifies the agent but does not authorize spending by itself.
|
|
248
255
|
|
|
249
256
|
## Configuration
|
|
250
257
|
|
|
@@ -273,12 +280,12 @@ receipts, and transactions. `POST /machine-payments/authorize` (the legacy
|
|
|
273
280
|
internal MPP demo challenge flow) is retired — it now refuses unconditionally
|
|
274
281
|
with HTTP 410; use the x402 flow for agent-to-merchant payments. Its security
|
|
275
282
|
scheme is deliberate: the Haven API key identifies the agent, but payment
|
|
276
|
-
authority still requires an agent-held delegate signature and on-chain
|
|
277
|
-
|
|
283
|
+
authority still requires an agent-held delegate signature and an on-chain
|
|
284
|
+
budget delegation.
|
|
278
285
|
|
|
279
286
|
## Agent payment state machine
|
|
280
287
|
|
|
281
|
-
Every payment
|
|
288
|
+
Every payment state returned by Haven includes:
|
|
282
289
|
|
|
283
290
|
- `phase`: where the Haven-side payment currently is.
|
|
284
291
|
- `nextAction`: the stable action an agent should take next.
|
|
@@ -312,26 +319,21 @@ import {
|
|
|
312
319
|
│ payment_submitted │
|
|
313
320
|
└──────────┬──────────────┘
|
|
314
321
|
│ check_status_later
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
┌───────────────────────┐
|
|
331
|
-
│ funding_sent │
|
|
332
|
-
└──────────┬────────────┘
|
|
333
|
-
│ retry_original_x402_request (x402)
|
|
334
|
-
│ none (direct)
|
|
322
|
+
▼
|
|
323
|
+
┌─────────────────────────┐
|
|
324
|
+
│ payment_confirmed (✔) │
|
|
325
|
+
└─────────────────────────┘
|
|
326
|
+
|
|
327
|
+
(EIP-3009 bridge only. `funding_sent` is Haven's funding leg confirming —
|
|
328
|
+
value left the treasury and sits on the delegate EOA. `executed` is the
|
|
329
|
+
agent's own merchant retry succeeding; Haven has no phase for the merchant
|
|
330
|
+
leg itself. See the `retry_original_x402_request` row below.)
|
|
331
|
+
┌───────────────────────┐
|
|
332
|
+
│ funding_sent │
|
|
333
|
+
└──────────┬────────────┘
|
|
334
|
+
│ retry_original_x402_request (x402, after the
|
|
335
|
+
│ merchant-report grace window — #2145)
|
|
336
|
+
│ none (direct / erc7710)
|
|
335
337
|
▼
|
|
336
338
|
┌───────────────────────┐
|
|
337
339
|
│ executed (✔) │
|
|
@@ -354,12 +356,12 @@ x402 tool-window failures:
|
|
|
354
356
|
| `agent_signature_required` | Haven prepared a payment intent; the agent must sign and submit. | no |
|
|
355
357
|
| `payment_submitted` | Haven received the signed payment; the agent should poll for confirmation. | no |
|
|
356
358
|
| `payment_confirmed` | Direct payment is confirmed on chain. | yes |
|
|
357
|
-
| `user_approval_required` |
|
|
358
|
-
| `user_execution_required` |
|
|
359
|
-
| `waiting_for_additional_approvals` |
|
|
360
|
-
| `funding_sent` | Haven funding leg landed; the agent can continue the merchant/protocol leg. | no |
|
|
361
|
-
| `rejected` |
|
|
362
|
-
| `expired` | Payment
|
|
359
|
+
| `user_approval_required` | **No live rail produces it.** Described the retired Safe rail's approval queue; kept in the exported enum for wire compatibility only. A payment outside the budget is now declined outright — see [Payments outside the agent's budget](#payments-outside-the-agents-budget). | n/a |
|
|
360
|
+
| `user_execution_required` | **No live rail produces it.** Same retirement as above. | n/a |
|
|
361
|
+
| `waiting_for_additional_approvals` | **No live rail produces it.** Same retirement as above. | n/a |
|
|
362
|
+
| `funding_sent` | Haven funding leg landed; the agent can continue the merchant/protocol leg. Only the EIP-3009 bridge has a funding leg; erc7710 direct settlement has none. | no |
|
|
363
|
+
| `rejected` | The payment was rejected and cannot proceed. | yes |
|
|
364
|
+
| `expired` | Payment expired before completion. | yes |
|
|
363
365
|
| `failed` | Haven could not complete the payment. | yes |
|
|
364
366
|
|
|
365
367
|
The merchant settlement leg of x402 (and the MPP retry) is the agent's own request to the merchant — it does not have a Haven `phase`. The payment is `funding_sent` until the agent retries with `X-PAYMENT` (x402) or the MPP proof header; from Haven's perspective the payment becomes `executed` only after the agent successfully resumes.
|
|
@@ -371,9 +373,9 @@ The merchant settlement leg of x402 (and the MPP retry) is the agent's own reque
|
|
|
371
373
|
| `sign_and_submit_payment` | Sign with the delegate key and submit the payment to Haven. |
|
|
372
374
|
| `check_status_later` | Poll `getPaymentStatus(payment_id)` later. |
|
|
373
375
|
| `none` | Stop polling; no more action is needed for this payment id. |
|
|
374
|
-
| `wait_for_user_approval` |
|
|
375
|
-
| `wait_for_user_to_complete_payment` |
|
|
376
|
-
| `retry_original_x402_request` |
|
|
376
|
+
| `wait_for_user_approval` | **No longer produced — nothing maps to it.** Retired with the Safe rail's approval queue; kept in the exported enum for wire compatibility. The SDK's own status mapping now answers `stop_and_tell_user` for the statuses that used to yield this. |
|
|
377
|
+
| `wait_for_user_to_complete_payment` | **No longer produced — nothing maps to it.** Same retirement as above. |
|
|
378
|
+
| `retry_original_x402_request` | Haven's funding leg confirmed but no merchant response was ever recorded — most often because the process crashed between the funding confirmation and the merchant retry (a 15-minute grace window applies before this fires; a client-reported merchant rejection instead yields `sweep_stranded_funds`). Call `resumeX402Payment()` with the preserved `resumeState`, or rehydrate it first with `getResumeState(payment_id)`. Do not start a new payment for the same purchase. |
|
|
377
379
|
| `stop_and_tell_user` | Stop retrying and tell the user the payment failed or was rejected. |
|
|
378
380
|
| `request_again_if_user_still_wants_it` | The request expired; ask again only if the user still wants the payment. |
|
|
379
381
|
| `payment_window_expired` | The x402 funding/quote window expired. Re-quote the same paid MCP tool call with the same `idempotency_key`, then sign the fresh `payload_hash`. |
|
|
@@ -391,55 +393,70 @@ Hosted MCP and signer tools also return stable `code` values on recoverable x402
|
|
|
391
393
|
| `PAYMENT_WINDOW_EXPIRED` | The funding/quote window closed before `haven_x402_sign_header`, `haven_submit`, or `haven_complete_mcp_tool` could finish. | Re-run `haven_pay_mcp_tool` with the same `idempotency_key`, then sign and complete the fresh quote. Payloads include `retry_with_new_quote: true`. |
|
|
392
394
|
| `MERCHANT_REJECTED_AFTER_FUNDING` | Haven's funding leg succeeded, but the merchant rejected the paid retry. | Stop retrying the merchant and call `haven_sweep_delegate` so the user can recover stranded delegate USDC. |
|
|
393
395
|
|
|
394
|
-
## Payments
|
|
395
|
-
|
|
396
|
-
Haven's policy
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
396
|
+
## Payments outside the agent's budget
|
|
397
|
+
|
|
398
|
+
Haven's policy is the agent's on-chain budget delegation — a period budget, an
|
|
399
|
+
optional recipient pin, and an expiry, each enforced by an audited caveat
|
|
400
|
+
enforcer at redemption. There is no off-chain rules engine and **no approval
|
|
401
|
+
queue**: the queue-and-approve path belonged to the retired Safe rail, which now
|
|
402
|
+
answers HTTP 410 at every agent-payment entry point.
|
|
403
|
+
|
|
404
|
+
If an agent requests a payment outside that policy, Haven **declines it before
|
|
405
|
+
any money moves** — during prepare, before anything is written and before the
|
|
406
|
+
agent is asked to sign. `POST /payments` answers `403` when no active delegation
|
|
407
|
+
authorizes that token and recipient, and `502` when the on-chain caveat check
|
|
408
|
+
rejects the amount, recipient or expiry; the x402 authorize path answers `403
|
|
409
|
+
delegation_budget_exceeded`. In every case the SDK raises `HavenApiError` and no
|
|
410
|
+
`payment_id` exists to poll.
|
|
411
|
+
|
|
412
|
+
Surface that to the user as a decline, not a wait: **nothing will arrive later.**
|
|
413
|
+
The fix is for the wallet owner to grant or raise the budget in Haven, after
|
|
414
|
+
which the agent can request the payment again. Do not retry in a loop, and do
|
|
415
|
+
not poll `getPaymentStatus()` hoping for an approval.
|
|
416
|
+
|
|
417
|
+
### Resuming an x402 payment
|
|
418
|
+
|
|
419
|
+
Resume was triggered by *funding confirmation*, not by an approval, and applied
|
|
420
|
+
only to the EIP-3009 bridge — erc7710 direct settlement has no funding leg and
|
|
421
|
+
nothing to resume.
|
|
422
|
+
|
|
423
|
+
> **Resume is reachable again (#2145).** If the agent process crashes after
|
|
424
|
+
> Haven's funding leg confirms but before the merchant retry is recorded, a
|
|
425
|
+
> later `getPaymentStatus(payment_id)` reports
|
|
426
|
+
> `nextAction: 'retry_original_x402_request'` — Haven's funding confirmed but
|
|
427
|
+
> the merchant has likely not been paid. Gate on that structured field, not on
|
|
428
|
+
> message prose: call `resumeX402Payment()` with the preserved `resumeState`, or
|
|
429
|
+
> rehydrate it first via `getResumeState(payment_id)`. Any other `nextAction`
|
|
430
|
+
> means the payment is not ready to resume — do not call it speculatively.
|
|
431
|
+
>
|
|
432
|
+
> Meanwhile: the `payX402*` helpers perform the merchant retry themselves, so
|
|
433
|
+
> the ordinary in-flight path never needs resume. Only reach for
|
|
434
|
+
> `resumeX402Payment()` after seeing the trigger on a later status check —
|
|
435
|
+
> never speculatively, and never as a substitute for a fresh payment.
|
|
436
|
+
|
|
437
|
+
When the agent used `quoteX402()` / `payX402Quote()`, the thrown
|
|
438
|
+
`HavenPaymentStateError` includes a serializable `resumeState`. Persist it with
|
|
439
|
+
the MCP session details and pass it back to `resumeX402Payment()`.
|
|
419
440
|
|
|
420
441
|
If the agent process restarts and only kept the `payment_id`, call
|
|
421
|
-
`getResumeState(payment_id)`
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
442
|
+
`getResumeState(payment_id)` to rehydrate the stored x402/MPP context from
|
|
443
|
+
Haven, then pass that state to the matching resume helper. For POST-based
|
|
444
|
+
merchant or MCP calls, rebuild the live request details before retrying; Haven
|
|
445
|
+
stores payment context, not the agent's local request stream.
|
|
425
446
|
|
|
426
447
|
```typescript
|
|
427
448
|
let resumeState
|
|
428
449
|
try {
|
|
429
450
|
await haven.payX402Quote(quote)
|
|
430
451
|
} catch (err) {
|
|
431
|
-
if (
|
|
432
|
-
err instanceof HavenPaymentStateError &&
|
|
433
|
-
err.nextAction === AgentPaymentNextAction.WaitForUserApproval &&
|
|
434
|
-
err.resumeState
|
|
435
|
-
) {
|
|
452
|
+
if (err instanceof HavenPaymentStateError && err.resumeState) {
|
|
436
453
|
resumeState = err.resumeState
|
|
437
454
|
console.log(err.paymentId, err.phase, err.nextAction)
|
|
438
|
-
console.log('
|
|
455
|
+
console.log('Funding has not confirmed yet. Save resumeState and poll.')
|
|
439
456
|
}
|
|
440
457
|
}
|
|
441
458
|
|
|
442
|
-
const status = await haven.getPaymentStatus('
|
|
459
|
+
const status = await haven.getPaymentStatus('payment-id')
|
|
443
460
|
if (status.nextAction === AgentPaymentNextAction.RetryOriginalX402Request) {
|
|
444
461
|
resumeState ??= await haven.getResumeState(status.paymentId)
|
|
445
462
|
const response = await haven.resumeX402Payment(resumeState)
|
|
@@ -447,11 +464,12 @@ if (status.nextAction === AgentPaymentNextAction.RetryOriginalX402Request) {
|
|
|
447
464
|
}
|
|
448
465
|
```
|
|
449
466
|
|
|
450
|
-
Think of
|
|
467
|
+
Think of bridged x402 as two separate legs:
|
|
451
468
|
|
|
452
|
-
- Haven funding leg: the
|
|
453
|
-
|
|
454
|
-
`
|
|
469
|
+
- Haven funding leg: the account funds the agent delegate wallet by redeeming
|
|
470
|
+
the budget delegation. Status fields such as `phase`, `nextAction`, and
|
|
471
|
+
`txHash` describe this leg. It is automatic and bounded by the budget — no
|
|
472
|
+
human step.
|
|
455
473
|
- Merchant x402 leg: after the funding leg is complete, the agent resumes the
|
|
456
474
|
same payment id and retries the original merchant request with `X-PAYMENT`.
|
|
457
475
|
Do not treat a new 402 probe or a new MCP session as a resume.
|
|
@@ -473,14 +491,15 @@ await fetch('https://paid-api.example.com/data', {
|
|
|
473
491
|
|
|
474
492
|
For MCP/SSE x402 tools, keep the same MCP session and JSON-RPC payload where the
|
|
475
493
|
merchant requires it: initialize, retain `mcp-session-id`, send the original
|
|
476
|
-
`tools/call`, parse the 402 challenge, wait for
|
|
477
|
-
with the same `payment_id` and retry the
|
|
478
|
-
`X-PAYMENT`. Use a stable `idempotencyKey` for the
|
|
479
|
-
quotes or sessions do not become duplicate Haven
|
|
494
|
+
`tools/call`, parse the 402 challenge, wait for the funding leg to confirm if
|
|
495
|
+
the payment is bridged, then resume with the same `payment_id` and retry the
|
|
496
|
+
original `tools/call` with `X-PAYMENT`. Use a stable `idempotencyKey` for the
|
|
497
|
+
user intent so fresh merchant quotes or sessions do not become duplicate Haven
|
|
498
|
+
payments.
|
|
480
499
|
|
|
481
500
|
See [`examples/mcp-x402-sse.ts`](./examples/mcp-x402-sse.ts) for a complete
|
|
482
501
|
MCP flow with initialize, `mcp-session-id`, JSON-RPC `tools/call`, quote
|
|
483
|
-
inspection,
|
|
502
|
+
inspection, saved resume state, and final retry.
|
|
484
503
|
|
|
485
504
|
## Error Handling
|
|
486
505
|
|