@haven_ai/sdk 0.1.3 → 0.1.4
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 +115 -11
- package/dist/index.cjs +774 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +358 -7
- package/dist/index.d.ts +358 -7
- package/dist/index.js +763 -38
- 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/README.md
CHANGED
|
@@ -120,6 +120,26 @@ if (apiResponse.status === 402) {
|
|
|
120
120
|
}
|
|
121
121
|
```
|
|
122
122
|
|
|
123
|
+
For agents that need to inspect the price before paying, use the quote-first
|
|
124
|
+
path. `quoteX402()` probes the merchant and parses the HTTP 402 response, but it
|
|
125
|
+
does not create a Haven payment, approval request, signature, or on-chain
|
|
126
|
+
transaction.
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
const quote = await haven.quoteX402(
|
|
130
|
+
'https://paid-api.example.com/data',
|
|
131
|
+
undefined,
|
|
132
|
+
{ idempotencyKey: 'paid-api-data-2026-05-22' },
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
if (Number(quote.amount) > 0.05) {
|
|
136
|
+
throw new Error(`Price ${quote.amount} ${quote.token} is above the user cap`)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const response = await haven.payX402Quote(quote)
|
|
140
|
+
const data = await response.json()
|
|
141
|
+
```
|
|
142
|
+
|
|
123
143
|
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
144
|
|
|
125
145
|
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.
|
|
@@ -175,6 +195,62 @@ const haven = new HavenClient({
|
|
|
175
195
|
})
|
|
176
196
|
```
|
|
177
197
|
|
|
198
|
+
## OpenAPI
|
|
199
|
+
|
|
200
|
+
The backend serves an OpenAPI 3.1 contract at:
|
|
201
|
+
|
|
202
|
+
- Production: `https://havenbackend-production-8a00.up.railway.app/openapi.json`
|
|
203
|
+
- Local development: `http://localhost:3001/openapi.json`
|
|
204
|
+
|
|
205
|
+
The spec covers the agent-facing payment surface: agents, direct payments,
|
|
206
|
+
payment status, x402 authorization, MPP demo authorization, resume-state
|
|
207
|
+
rehydration, machine-payment receipts, and transactions. Its security scheme is
|
|
208
|
+
deliberate: the Haven API key identifies the agent, but payment authority still
|
|
209
|
+
requires an agent-held delegate signature and on-chain Safe allowance state.
|
|
210
|
+
|
|
211
|
+
## Agent payment state machine
|
|
212
|
+
|
|
213
|
+
Every payment or approval state returned by Haven includes:
|
|
214
|
+
|
|
215
|
+
- `phase`: where the Haven-side payment currently is.
|
|
216
|
+
- `nextAction`: the stable action an agent should take next.
|
|
217
|
+
- `rail`: which payment rail produced the state, such as `direct`, `x402`, or `mpp`.
|
|
218
|
+
- `message`: human-readable guidance for the same state.
|
|
219
|
+
|
|
220
|
+
The enum values and JSON Schema fragments are exported from `@haven_ai/sdk`:
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
import {
|
|
224
|
+
AgentPaymentNextAction,
|
|
225
|
+
AgentPaymentNextActionSchema,
|
|
226
|
+
AgentPaymentPhase,
|
|
227
|
+
AgentPaymentPhaseSchema,
|
|
228
|
+
AgentPaymentRail,
|
|
229
|
+
AgentPaymentRailSchema,
|
|
230
|
+
} from '@haven_ai/sdk'
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
```text
|
|
234
|
+
agent_signature_required -> payment_submitted -> payment_confirmed
|
|
235
|
+
|
|
|
236
|
+
v
|
|
237
|
+
user_approval_required -> user_execution_required -> funding_sent
|
|
238
|
+
|
|
|
239
|
+
v
|
|
240
|
+
waiting_for_additional_approvals
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
| `nextAction` | What the agent should do |
|
|
244
|
+
|--------------|--------------------------|
|
|
245
|
+
| `sign_and_submit_payment` | Sign with the delegate key and submit the payment to Haven. |
|
|
246
|
+
| `check_status_later` | Poll `getPaymentStatus(payment_id)` later. |
|
|
247
|
+
| `none` | Stop polling; no more action is needed for this payment id. |
|
|
248
|
+
| `wait_for_user_approval` | Tell the user the payment is waiting in Haven, then poll later. Do not create a duplicate payment. |
|
|
249
|
+
| `wait_for_user_to_complete_payment` | The user approved the request; wait for them to finish the funding payment. |
|
|
250
|
+
| `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. |
|
|
251
|
+
| `stop_and_tell_user` | Stop retrying and tell the user the payment failed or was rejected. |
|
|
252
|
+
| `request_again_if_user_still_wants_it` | The request expired; ask again only if the user still wants the payment. |
|
|
253
|
+
|
|
178
254
|
## Payments above the on-chain allowance
|
|
179
255
|
|
|
180
256
|
Haven's policy lives on the Safe AllowanceModule (token, amount, reset period).
|
|
@@ -189,33 +265,57 @@ tool later instead of retrying in a tight loop.
|
|
|
189
265
|
|
|
190
266
|
For x402, approval resume is explicit. If `authorizeX402()` or `haven.fetch()`
|
|
191
267
|
throws `HavenPaymentStateError` with `nextAction: 'wait_for_user_approval'`,
|
|
192
|
-
stop and tell the user the
|
|
268
|
+
stop and tell the user the Haven funding leg is waiting in Haven. Do not loop
|
|
269
|
+
and do not start a new merchant or MCP session. Pending x402 states include the
|
|
270
|
+
resource URL, merchant address, chain id, asset, network, atomic amount, and
|
|
271
|
+
idempotency key so agents can explain what is waiting for approval. After the
|
|
193
272
|
user approves, call `getPaymentStatus(payment_id)`. When Haven reports
|
|
194
273
|
`nextAction: 'retry_original_x402_request'`, call `resumeX402Payment()` with the
|
|
195
274
|
same user-intent idempotency key and the original x402 details.
|
|
196
275
|
|
|
276
|
+
When the agent used `quoteX402()` / `payX402Quote()`, pending approval errors
|
|
277
|
+
include a serializable `resumeState`. Persist it with the MCP session details
|
|
278
|
+
and pass it back to `resumeX402Payment()` after approval.
|
|
279
|
+
|
|
280
|
+
If the agent process restarts and only kept the `payment_id`, call
|
|
281
|
+
`getResumeState(payment_id)` after approval to rehydrate the stored x402/MPP
|
|
282
|
+
context from Haven, then pass that state to the matching resume helper. For
|
|
283
|
+
POST-based merchant or MCP calls, rebuild the live request details before
|
|
284
|
+
retrying; Haven stores payment context, not the agent's local request stream.
|
|
285
|
+
|
|
197
286
|
```typescript
|
|
287
|
+
let resumeState
|
|
198
288
|
try {
|
|
199
|
-
await haven.
|
|
289
|
+
await haven.payX402Quote(quote)
|
|
200
290
|
} catch (err) {
|
|
201
|
-
if (
|
|
291
|
+
if (
|
|
292
|
+
err instanceof HavenPaymentStateError &&
|
|
293
|
+
err.nextAction === AgentPaymentNextAction.WaitForUserApproval &&
|
|
294
|
+
err.resumeState
|
|
295
|
+
) {
|
|
296
|
+
resumeState = err.resumeState
|
|
202
297
|
console.log(err.paymentId, err.phase, err.nextAction)
|
|
203
|
-
console.log('Queued for owner approval
|
|
298
|
+
console.log('Queued for owner approval. Save resumeState and wait.')
|
|
204
299
|
}
|
|
205
300
|
}
|
|
206
301
|
|
|
207
302
|
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
|
-
})
|
|
303
|
+
if (status.nextAction === AgentPaymentNextAction.RetryOriginalX402Request) {
|
|
304
|
+
resumeState ??= await haven.getResumeState(status.paymentId)
|
|
305
|
+
const response = await haven.resumeX402Payment(resumeState)
|
|
215
306
|
const data = await response.json()
|
|
216
307
|
}
|
|
217
308
|
```
|
|
218
309
|
|
|
310
|
+
Think of manual approval x402 as two separate legs:
|
|
311
|
+
|
|
312
|
+
- Haven funding leg: the user may need to approve a Safe AllowanceModule transfer
|
|
313
|
+
to the agent delegate wallet. Status fields such as `phase`,
|
|
314
|
+
`nextAction`, and `txHash` describe this leg.
|
|
315
|
+
- Merchant x402 leg: after the funding leg is complete, the agent resumes the
|
|
316
|
+
same payment id and retries the original merchant request with `X-PAYMENT`.
|
|
317
|
+
Do not treat a new 402 probe or a new MCP session as a resume.
|
|
318
|
+
|
|
219
319
|
For manual HTTP stacks, use `resumeAuthorizedX402()` to get the merchant header
|
|
220
320
|
without retrying the request for you:
|
|
221
321
|
|
|
@@ -238,6 +338,10 @@ with the same `payment_id` and retry the original `tools/call` with
|
|
|
238
338
|
`X-PAYMENT`. Use a stable `idempotencyKey` for the user intent so fresh merchant
|
|
239
339
|
quotes or sessions do not become duplicate Haven approval requests.
|
|
240
340
|
|
|
341
|
+
See [`examples/mcp-x402-sse.ts`](./examples/mcp-x402-sse.ts) for a complete
|
|
342
|
+
MCP flow with initialize, `mcp-session-id`, JSON-RPC `tools/call`, quote
|
|
343
|
+
inspection, user approval, saved resume state, and final retry.
|
|
344
|
+
|
|
241
345
|
## Error Handling
|
|
242
346
|
|
|
243
347
|
```typescript
|