@haven_ai/sdk 0.1.3 → 0.1.5
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 +181 -12
- package/dist/index.cjs +980 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +963 -48
- package/dist/index.js.map +1 -1
- package/examples/mcp-x402-sse.ts +149 -0
- package/examples/x402_openapi_python.py +114 -0
- package/package.json +4 -3
- package/dist/index.d.cts +0 -499
- package/dist/index.d.ts +0 -499
package/README.md
CHANGED
|
@@ -68,6 +68,15 @@ The agent will pay a tiny amount (~0.01 EURe on Gnosis Chain), receive the demo
|
|
|
68
68
|
| Gnosis Chain | `eip155:100` | EURe, USDC.e, xDAI |
|
|
69
69
|
| Base | `eip155:8453` | USDC, ETH |
|
|
70
70
|
|
|
71
|
+
## Credential Lifecycle
|
|
72
|
+
|
|
73
|
+
- The Haven API key identifies the agent. It is not payment authority.
|
|
74
|
+
- The delegate key signs payment payloads locally. Haven's backend never receives it.
|
|
75
|
+
- On-chain Safe AllowanceModule state enforces the agent budget.
|
|
76
|
+
- `getAllowances()` / `get_allowances` is the right path for budget, remaining amount, reset period, or "what can I spend?" questions.
|
|
77
|
+
- 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.
|
|
78
|
+
- If a delegate key is exposed or lost, pause or revoke the agent and create a new signing path.
|
|
79
|
+
|
|
71
80
|
## Step-by-Step API
|
|
72
81
|
|
|
73
82
|
For agents that need control over each step (e.g., external signing):
|
|
@@ -94,7 +103,7 @@ const result = await haven.waitForConfirmation(intent.paymentId)
|
|
|
94
103
|
|
|
95
104
|
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.
|
|
96
105
|
|
|
97
|
-
|
|
106
|
+
The SDK supports [x402](https://x402.org) client flows. When an API returns HTTP 402, the SDK evaluates the challenge against the agent's approved limits, uses the configured delegate key for the required signature, and retries automatically:
|
|
98
107
|
|
|
99
108
|
```typescript
|
|
100
109
|
// Automatic — fetch() intercepts 402, pays, and retries.
|
|
@@ -120,6 +129,26 @@ if (apiResponse.status === 402) {
|
|
|
120
129
|
}
|
|
121
130
|
```
|
|
122
131
|
|
|
132
|
+
For agents that need to inspect the price before paying, use the quote-first
|
|
133
|
+
path. `quoteX402()` probes the merchant and parses the HTTP 402 response, but it
|
|
134
|
+
does not create a Haven payment, approval request, signature, or on-chain
|
|
135
|
+
transaction.
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
const quote = await haven.quoteX402(
|
|
139
|
+
'https://paid-api.example.com/data',
|
|
140
|
+
undefined,
|
|
141
|
+
{ idempotencyKey: 'paid-api-data-2026-05-22' },
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
if (Number(quote.amount) > 0.05) {
|
|
145
|
+
throw new Error(`Price ${quote.amount} ${quote.token} is above the user cap`)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const response = await haven.payX402Quote(quote)
|
|
149
|
+
const data = await response.json()
|
|
150
|
+
```
|
|
151
|
+
|
|
123
152
|
Merchant-verified x402 retries use the official EIP-3009 `exact` scheme on Base USDC (`base` / `eip155:8453`) and send the payment as `X-PAYMENT`. Haven's older tx-hash proof helper remains exported for Haven-native integrations, but `haven.fetch()` does not send `PAYMENT-SIGNATURE`.
|
|
124
153
|
|
|
125
154
|
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.
|
|
@@ -158,8 +187,12 @@ for (const block of response.content) {
|
|
|
158
187
|
|------|-------------|
|
|
159
188
|
| `make_payment` | Request and sign a payment from the user-controlled Safe within approved limits |
|
|
160
189
|
| `get_payment_status` | Check the status of a payment intent or approval request |
|
|
190
|
+
| `get_allowances` | Read configured and on-chain allowance state, including spent and remaining allowance |
|
|
161
191
|
| `authorize_x402_payment` | Authorize a policy-limited x402 payment and return a payment header for an HTTP 402 resource |
|
|
162
192
|
| `resume_x402_payment` | Resume an approved x402 payment and return a merchant payment header without creating a duplicate approval |
|
|
193
|
+
| `authorize_machine_payment` | Authorize an internal Haven MPP demo challenge and return proof details |
|
|
194
|
+
|
|
195
|
+
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 Safe allowance state; the Haven API key identifies the agent but does not authorize spending by itself.
|
|
163
196
|
|
|
164
197
|
## Configuration
|
|
165
198
|
|
|
@@ -175,6 +208,114 @@ const haven = new HavenClient({
|
|
|
175
208
|
})
|
|
176
209
|
```
|
|
177
210
|
|
|
211
|
+
## OpenAPI
|
|
212
|
+
|
|
213
|
+
The backend serves an OpenAPI 3.1 contract at:
|
|
214
|
+
|
|
215
|
+
- Production: `https://havenbackend-production-8a00.up.railway.app/openapi.json`
|
|
216
|
+
- Local development: `http://localhost:3001/openapi.json`
|
|
217
|
+
|
|
218
|
+
The spec covers the agent-facing payment surface: agents, direct payments,
|
|
219
|
+
payment status, x402 authorization, MPP demo authorization, resume-state
|
|
220
|
+
rehydration, machine-payment receipts, and transactions. Its security scheme is
|
|
221
|
+
deliberate: the Haven API key identifies the agent, but payment authority still
|
|
222
|
+
requires an agent-held delegate signature and on-chain Safe allowance state.
|
|
223
|
+
|
|
224
|
+
## Agent payment state machine
|
|
225
|
+
|
|
226
|
+
Every payment or approval state returned by Haven includes:
|
|
227
|
+
|
|
228
|
+
- `phase`: where the Haven-side payment currently is.
|
|
229
|
+
- `nextAction`: the stable action an agent should take next.
|
|
230
|
+
- `rail`: which payment rail produced the state. Categorical values (`direct`, `x402`, `mpp`) appear on resume-state discriminators; granular values (`mpp_demo`, `mpp_crypto`, `stripe_deposit`, `spt`) appear on response bodies.
|
|
231
|
+
- `message`: human-readable guidance for the same state.
|
|
232
|
+
|
|
233
|
+
The enum values and JSON Schema fragments are exported from `@haven_ai/sdk`:
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
import {
|
|
237
|
+
AgentPaymentNextAction,
|
|
238
|
+
AgentPaymentNextActionSchema,
|
|
239
|
+
AgentPaymentPhase,
|
|
240
|
+
AgentPaymentPhaseSchema,
|
|
241
|
+
AgentPaymentRail,
|
|
242
|
+
AgentPaymentRailSchema,
|
|
243
|
+
} from '@haven_ai/sdk'
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Flow diagram
|
|
247
|
+
|
|
248
|
+
```text
|
|
249
|
+
┌─────────────────────────┐
|
|
250
|
+
│ agent_signature_required│
|
|
251
|
+
└──────────┬──────────────┘
|
|
252
|
+
│ sign_and_submit_payment
|
|
253
|
+
▼
|
|
254
|
+
┌─────────────────────────┐
|
|
255
|
+
│ payment_submitted │
|
|
256
|
+
└──────────┬──────────────┘
|
|
257
|
+
│ check_status_later
|
|
258
|
+
┌──────────┴──────────────┐
|
|
259
|
+
▼ ▼
|
|
260
|
+
┌────────────────────────┐ ┌──────────────────────┐
|
|
261
|
+
│ payment_confirmed (✔) │ │ user_approval_required│
|
|
262
|
+
└────────────────────────┘ └──────────┬───────────┘
|
|
263
|
+
│ wait_for_user_approval
|
|
264
|
+
┌─────────────────┴───────────────────┐
|
|
265
|
+
│ single-owner Safe │ multisig Safe
|
|
266
|
+
▼ ▼
|
|
267
|
+
┌──────────────────────────┐ ┌──────────────────────────────────┐
|
|
268
|
+
│ user_execution_required │ │ waiting_for_additional_approvals │
|
|
269
|
+
└──────────┬───────────────┘ └────────────────┬─────────────────┘
|
|
270
|
+
│ wait_for_user_to_complete_payment │ wait_for_user_approval
|
|
271
|
+
└─────────────────┬───────────────────┘
|
|
272
|
+
▼
|
|
273
|
+
┌───────────────────────┐
|
|
274
|
+
│ funding_sent │
|
|
275
|
+
└──────────┬────────────┘
|
|
276
|
+
│ retry_original_x402_request (x402)
|
|
277
|
+
│ none (direct)
|
|
278
|
+
▼
|
|
279
|
+
┌───────────────────────┐
|
|
280
|
+
│ executed (✔) │
|
|
281
|
+
└───────────────────────┘
|
|
282
|
+
|
|
283
|
+
Terminal from any non-confirmed phase:
|
|
284
|
+
rejected → stop_and_tell_user
|
|
285
|
+
failed → stop_and_tell_user
|
|
286
|
+
expired → request_again_if_user_still_wants_it
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### `phase` reference
|
|
290
|
+
|
|
291
|
+
| `phase` | Meaning | Terminal? |
|
|
292
|
+
|---------|---------|-----------|
|
|
293
|
+
| `agent_signature_required` | Haven prepared a payment intent; the agent must sign and submit. | no |
|
|
294
|
+
| `payment_submitted` | Haven received the signed payment; the agent should poll for confirmation. | no |
|
|
295
|
+
| `payment_confirmed` | Direct payment is confirmed on chain. | yes |
|
|
296
|
+
| `user_approval_required` | Payment exceeds remaining on-chain allowance; wallet owner must approve in Haven. | no |
|
|
297
|
+
| `user_execution_required` | Owner approved; the funding payment has not been sent yet (single-owner Safe). | no |
|
|
298
|
+
| `waiting_for_additional_approvals` | Funding payment was proposed and is waiting for the remaining multisig approvals. | no |
|
|
299
|
+
| `funding_sent` | Haven funding leg landed; the agent can continue the merchant/protocol leg. | no |
|
|
300
|
+
| `rejected` | Owner rejected the request. | yes |
|
|
301
|
+
| `expired` | Payment or approval request expired before completion. | yes |
|
|
302
|
+
| `failed` | Haven could not complete the payment. | yes |
|
|
303
|
+
|
|
304
|
+
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.
|
|
305
|
+
|
|
306
|
+
### `nextAction` reference
|
|
307
|
+
|
|
308
|
+
| `nextAction` | What the agent should do |
|
|
309
|
+
|--------------|--------------------------|
|
|
310
|
+
| `sign_and_submit_payment` | Sign with the delegate key and submit the payment to Haven. |
|
|
311
|
+
| `check_status_later` | Poll `getPaymentStatus(payment_id)` later. |
|
|
312
|
+
| `none` | Stop polling; no more action is needed for this payment id. |
|
|
313
|
+
| `wait_for_user_approval` | Tell the user the payment is waiting in Haven, then poll later. Do not create a duplicate payment. Same `nextAction` covers both the single-owner case (waiting for one owner to approve) and the multisig case (waiting for additional approvals after the first one). |
|
|
314
|
+
| `wait_for_user_to_complete_payment` | The user approved the request; wait for them to finish the funding payment. |
|
|
315
|
+
| `retry_original_x402_request` | Resume this payment id and retry the original x402 request with the merchant payment header. Do not start a new merchant session. |
|
|
316
|
+
| `stop_and_tell_user` | Stop retrying and tell the user the payment failed or was rejected. |
|
|
317
|
+
| `request_again_if_user_still_wants_it` | The request expired; ask again only if the user still wants the payment. |
|
|
318
|
+
|
|
178
319
|
## Payments above the on-chain allowance
|
|
179
320
|
|
|
180
321
|
Haven's policy lives on the Safe AllowanceModule (token, amount, reset period).
|
|
@@ -189,33 +330,57 @@ tool later instead of retrying in a tight loop.
|
|
|
189
330
|
|
|
190
331
|
For x402, approval resume is explicit. If `authorizeX402()` or `haven.fetch()`
|
|
191
332
|
throws `HavenPaymentStateError` with `nextAction: 'wait_for_user_approval'`,
|
|
192
|
-
stop and tell the user the
|
|
333
|
+
stop and tell the user the Haven funding leg is waiting in Haven. Do not loop
|
|
334
|
+
and do not start a new merchant or MCP session. Pending x402 states include the
|
|
335
|
+
resource URL, merchant address, chain id, asset, network, atomic amount, and
|
|
336
|
+
idempotency key so agents can explain what is waiting for approval. After the
|
|
193
337
|
user approves, call `getPaymentStatus(payment_id)`. When Haven reports
|
|
194
338
|
`nextAction: 'retry_original_x402_request'`, call `resumeX402Payment()` with the
|
|
195
339
|
same user-intent idempotency key and the original x402 details.
|
|
196
340
|
|
|
341
|
+
When the agent used `quoteX402()` / `payX402Quote()`, pending approval errors
|
|
342
|
+
include a serializable `resumeState`. Persist it with the MCP session details
|
|
343
|
+
and pass it back to `resumeX402Payment()` after approval.
|
|
344
|
+
|
|
345
|
+
If the agent process restarts and only kept the `payment_id`, call
|
|
346
|
+
`getResumeState(payment_id)` after approval to rehydrate the stored x402/MPP
|
|
347
|
+
context from Haven, then pass that state to the matching resume helper. For
|
|
348
|
+
POST-based merchant or MCP calls, rebuild the live request details before
|
|
349
|
+
retrying; Haven stores payment context, not the agent's local request stream.
|
|
350
|
+
|
|
197
351
|
```typescript
|
|
352
|
+
let resumeState
|
|
198
353
|
try {
|
|
199
|
-
await haven.
|
|
354
|
+
await haven.payX402Quote(quote)
|
|
200
355
|
} catch (err) {
|
|
201
|
-
if (
|
|
356
|
+
if (
|
|
357
|
+
err instanceof HavenPaymentStateError &&
|
|
358
|
+
err.nextAction === AgentPaymentNextAction.WaitForUserApproval &&
|
|
359
|
+
err.resumeState
|
|
360
|
+
) {
|
|
361
|
+
resumeState = err.resumeState
|
|
202
362
|
console.log(err.paymentId, err.phase, err.nextAction)
|
|
203
|
-
console.log('Queued for owner approval
|
|
363
|
+
console.log('Queued for owner approval. Save resumeState and wait.')
|
|
204
364
|
}
|
|
205
365
|
}
|
|
206
366
|
|
|
207
367
|
const status = await haven.getPaymentStatus('approval-or-payment-id')
|
|
208
|
-
if (status.nextAction ===
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
url: 'https://paid-api.example.com/data',
|
|
212
|
-
paymentRequired,
|
|
213
|
-
idempotencyKey: 'paid-api-data-2026-05-22',
|
|
214
|
-
})
|
|
368
|
+
if (status.nextAction === AgentPaymentNextAction.RetryOriginalX402Request) {
|
|
369
|
+
resumeState ??= await haven.getResumeState(status.paymentId)
|
|
370
|
+
const response = await haven.resumeX402Payment(resumeState)
|
|
215
371
|
const data = await response.json()
|
|
216
372
|
}
|
|
217
373
|
```
|
|
218
374
|
|
|
375
|
+
Think of manual approval x402 as two separate legs:
|
|
376
|
+
|
|
377
|
+
- Haven funding leg: the user may need to approve a Safe AllowanceModule transfer
|
|
378
|
+
to the agent delegate wallet. Status fields such as `phase`,
|
|
379
|
+
`nextAction`, and `txHash` describe this leg.
|
|
380
|
+
- Merchant x402 leg: after the funding leg is complete, the agent resumes the
|
|
381
|
+
same payment id and retries the original merchant request with `X-PAYMENT`.
|
|
382
|
+
Do not treat a new 402 probe or a new MCP session as a resume.
|
|
383
|
+
|
|
219
384
|
For manual HTTP stacks, use `resumeAuthorizedX402()` to get the merchant header
|
|
220
385
|
without retrying the request for you:
|
|
221
386
|
|
|
@@ -238,6 +403,10 @@ with the same `payment_id` and retry the original `tools/call` with
|
|
|
238
403
|
`X-PAYMENT`. Use a stable `idempotencyKey` for the user intent so fresh merchant
|
|
239
404
|
quotes or sessions do not become duplicate Haven approval requests.
|
|
240
405
|
|
|
406
|
+
See [`examples/mcp-x402-sse.ts`](./examples/mcp-x402-sse.ts) for a complete
|
|
407
|
+
MCP flow with initialize, `mcp-session-id`, JSON-RPC `tools/call`, quote
|
|
408
|
+
inspection, user approval, saved resume state, and final retry.
|
|
409
|
+
|
|
241
410
|
## Error Handling
|
|
242
411
|
|
|
243
412
|
```typescript
|