@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 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 request is waiting in Haven. Do not loop. After 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.pay({ token: 'USDC', amount: '500', to: '0xabc...' })
289
+ await haven.payX402Quote(quote)
200
290
  } catch (err) {
201
- if (err instanceof HavenPaymentStateError && err.nextAction === 'wait_for_user_approval') {
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 visible in the Haven dashboard.')
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 === '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
- })
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