@haven_ai/sdk 0.1.1 → 0.1.3
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 +71 -16
- package/dist/index.cjs +864 -78
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +148 -5
- package/dist/index.d.ts +148 -5
- package/dist/index.js +860 -79
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
TypeScript SDK for [Haven](https://github.com/d-hinders/Haven-AI) — agent wallet infrastructure for the autonomous economy.
|
|
4
4
|
|
|
5
|
-
Haven
|
|
5
|
+
Haven lets AI agents request and sign payments within strict, user-approved on-chain guardrails. This SDK makes it straightforward to integrate Haven payment requests into any agent without giving Haven custody of user or agent keys.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -92,11 +92,18 @@ const result = await haven.waitForConfirmation(intent.paymentId)
|
|
|
92
92
|
|
|
93
93
|
## x402 Protocol Support
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
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
|
+
|
|
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:
|
|
96
98
|
|
|
97
99
|
```typescript
|
|
98
|
-
// Automatic — fetch() intercepts 402, pays, and retries
|
|
99
|
-
|
|
100
|
+
// Automatic — fetch() intercepts 402, pays, and retries.
|
|
101
|
+
// Use a stable idempotencyKey when one user intent may need manual approval.
|
|
102
|
+
const response = await haven.fetch(
|
|
103
|
+
'https://paid-api.example.com/data',
|
|
104
|
+
undefined,
|
|
105
|
+
{ idempotencyKey: 'paid-api-data-2026-05-22' },
|
|
106
|
+
)
|
|
100
107
|
const data = await response.json()
|
|
101
108
|
|
|
102
109
|
// Manual — parse and authorize the 402 yourself
|
|
@@ -105,7 +112,9 @@ import { parsePaymentRequiredResponse } from '@haven_ai/sdk'
|
|
|
105
112
|
const apiResponse = await fetch('https://paid-api.example.com/data')
|
|
106
113
|
if (apiResponse.status === 402) {
|
|
107
114
|
const paymentRequired = await parsePaymentRequiredResponse(apiResponse)
|
|
108
|
-
const receipt = await haven.authorizeX402(paymentRequired
|
|
115
|
+
const receipt = await haven.authorizeX402(paymentRequired, {
|
|
116
|
+
idempotencyKey: 'paid-api-data-2026-05-22',
|
|
117
|
+
})
|
|
109
118
|
// Retry with { 'X-PAYMENT': receipt.paymentHeader }
|
|
110
119
|
console.log(receipt.explorerUrl)
|
|
111
120
|
}
|
|
@@ -147,9 +156,10 @@ for (const block of response.content) {
|
|
|
147
156
|
|
|
148
157
|
| Tool | Description |
|
|
149
158
|
|------|-------------|
|
|
150
|
-
| `make_payment` |
|
|
151
|
-
| `get_payment_status` | Check the status of a
|
|
152
|
-
| `authorize_x402_payment` |
|
|
159
|
+
| `make_payment` | Request and sign a payment from the user-controlled Safe within approved limits |
|
|
160
|
+
| `get_payment_status` | Check the status of a payment intent or approval request |
|
|
161
|
+
| `authorize_x402_payment` | Authorize a policy-limited x402 payment and return a payment header for an HTTP 402 resource |
|
|
162
|
+
| `resume_x402_payment` | Resume an approved x402 payment and return a merchant payment header without creating a duplicate approval |
|
|
153
163
|
|
|
154
164
|
## Configuration
|
|
155
165
|
|
|
@@ -167,33 +177,78 @@ const haven = new HavenClient({
|
|
|
167
177
|
|
|
168
178
|
## Payments above the on-chain allowance
|
|
169
179
|
|
|
170
|
-
Haven's policy lives
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
and queues it for the wallet owner
|
|
180
|
+
Haven's policy lives on the Safe AllowanceModule (token, amount, reset period).
|
|
181
|
+
If an agent requests a payment above the remaining allowance, Haven does **not**
|
|
182
|
+
reject it — it returns HTTP 202 with `status: 'pending_approval'`, a
|
|
183
|
+
`payment_id`, `phase`, and `next_action`, then queues it for the wallet owner
|
|
184
|
+
to approve in the dashboard.
|
|
174
185
|
|
|
175
186
|
Surface that to the user: the payment isn't dead, it's waiting for a human to
|
|
176
|
-
sign off.
|
|
187
|
+
sign off. Check `getPaymentStatus(payment_id)` or the `get_payment_status`
|
|
188
|
+
tool later instead of retrying in a tight loop.
|
|
189
|
+
|
|
190
|
+
For x402, approval resume is explicit. If `authorizeX402()` or `haven.fetch()`
|
|
191
|
+
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
|
|
193
|
+
user approves, call `getPaymentStatus(payment_id)`. When Haven reports
|
|
194
|
+
`nextAction: 'retry_original_x402_request'`, call `resumeX402Payment()` with the
|
|
195
|
+
same user-intent idempotency key and the original x402 details.
|
|
177
196
|
|
|
178
197
|
```typescript
|
|
179
198
|
try {
|
|
180
199
|
await haven.pay({ token: 'USDC', amount: '500', to: '0xabc...' })
|
|
181
200
|
} catch (err) {
|
|
182
|
-
if (err instanceof
|
|
183
|
-
|
|
201
|
+
if (err instanceof HavenPaymentStateError && err.nextAction === 'wait_for_user_approval') {
|
|
202
|
+
console.log(err.paymentId, err.phase, err.nextAction)
|
|
184
203
|
console.log('Queued for owner approval — visible in the Haven dashboard.')
|
|
185
204
|
}
|
|
186
205
|
}
|
|
206
|
+
|
|
207
|
+
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
|
+
})
|
|
215
|
+
const data = await response.json()
|
|
216
|
+
}
|
|
187
217
|
```
|
|
188
218
|
|
|
219
|
+
For manual HTTP stacks, use `resumeAuthorizedX402()` to get the merchant header
|
|
220
|
+
without retrying the request for you:
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
const receipt = await haven.resumeAuthorizedX402({
|
|
224
|
+
paymentId: status.paymentId,
|
|
225
|
+
paymentRequired,
|
|
226
|
+
idempotencyKey: 'paid-api-data-2026-05-22',
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
await fetch('https://paid-api.example.com/data', {
|
|
230
|
+
headers: { 'X-PAYMENT': receipt.paymentHeader! },
|
|
231
|
+
})
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
For MCP/SSE x402 tools, keep the same MCP session and JSON-RPC payload where the
|
|
235
|
+
merchant requires it: initialize, retain `mcp-session-id`, send the original
|
|
236
|
+
`tools/call`, parse the 402 challenge, wait for approval if needed, then resume
|
|
237
|
+
with the same `payment_id` and retry the original `tools/call` with
|
|
238
|
+
`X-PAYMENT`. Use a stable `idempotencyKey` for the user intent so fresh merchant
|
|
239
|
+
quotes or sessions do not become duplicate Haven approval requests.
|
|
240
|
+
|
|
189
241
|
## Error Handling
|
|
190
242
|
|
|
191
243
|
```typescript
|
|
192
|
-
import { HavenApiError, HavenSigningError, HavenTimeoutError } from '@haven_ai/sdk'
|
|
244
|
+
import { HavenApiError, HavenPaymentStateError, HavenSigningError, HavenTimeoutError } from '@haven_ai/sdk'
|
|
193
245
|
|
|
194
246
|
try {
|
|
195
247
|
await haven.pay({ token: 'EURe', amount: '5.00', to: '0xabc...' })
|
|
196
248
|
} catch (err) {
|
|
249
|
+
if (err instanceof HavenPaymentStateError) {
|
|
250
|
+
console.log(err.paymentId, err.phase, err.nextAction)
|
|
251
|
+
}
|
|
197
252
|
if (err instanceof HavenApiError) {
|
|
198
253
|
console.log(err.statusCode, err.message) // API returned an error
|
|
199
254
|
}
|