@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 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
- Haven natively supports the [x402](https://x402.org) payment protocol. 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:
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 request is waiting in Haven. Do not loop. After 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.pay({ token: 'USDC', amount: '500', to: '0xabc...' })
354
+ await haven.payX402Quote(quote)
200
355
  } catch (err) {
201
- if (err instanceof HavenPaymentStateError && err.nextAction === 'wait_for_user_approval') {
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 visible in the Haven dashboard.')
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 === 'retry_original_x402_request') {
209
- const response = await haven.resumeX402Payment({
210
- paymentId: status.paymentId,
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