@haven_ai/sdk 0.1.5 → 0.1.7
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 +6 -13
- package/dist/index.cjs +64 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1081 -0
- package/dist/index.d.ts +1081 -0
- package/dist/index.js +64 -18
- package/dist/index.js.map +1 -1
- package/examples/x402_openapi_python.py +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -27,7 +27,15 @@ var AgentPaymentPhase = {
|
|
|
27
27
|
/** The payment or approval request expired before completion. */
|
|
28
28
|
Expired: "expired",
|
|
29
29
|
/** Haven could not complete the payment; the agent should stop and surface the failure. */
|
|
30
|
-
Failed: "failed"
|
|
30
|
+
Failed: "failed",
|
|
31
|
+
/**
|
|
32
|
+
* Pre-flight check determined the delegate's existing balance plus the
|
|
33
|
+
* remaining on-chain allowance cannot cover the requested amount, so no
|
|
34
|
+
* payment intent was created. Distinct from `UserApprovalRequired`: there
|
|
35
|
+
* is no approval that would fix this — the originating Safe needs more
|
|
36
|
+
* funds or the agent's per-token allowance needs to be raised first.
|
|
37
|
+
*/
|
|
38
|
+
InsufficientFunds: "insufficient_funds"
|
|
31
39
|
};
|
|
32
40
|
var AgentPaymentNextAction = {
|
|
33
41
|
/** Sign with the delegate key and submit the payment to Haven. */
|
|
@@ -45,7 +53,13 @@ var AgentPaymentNextAction = {
|
|
|
45
53
|
/** Stop retrying this payment and tell the user what happened. */
|
|
46
54
|
StopAndTellUser: "stop_and_tell_user",
|
|
47
55
|
/** Ask again only if the user still wants the payment after expiry. */
|
|
48
|
-
RequestAgainIfUserStillWantsIt: "request_again_if_user_still_wants_it"
|
|
56
|
+
RequestAgainIfUserStillWantsIt: "request_again_if_user_still_wants_it",
|
|
57
|
+
/**
|
|
58
|
+
* Stop and tell the user that the originating Safe needs to be funded or
|
|
59
|
+
* the agent's per-token allowance needs to be raised before the payment
|
|
60
|
+
* can succeed. A user approval will not fix this state on its own.
|
|
61
|
+
*/
|
|
62
|
+
FundSafeOrRaiseAllowance: "fund_safe_or_raise_allowance"
|
|
49
63
|
};
|
|
50
64
|
var AgentPaymentRail = {
|
|
51
65
|
/** Standard Haven payment from the user's Safe through an approved delegate allowance. */
|
|
@@ -76,7 +90,8 @@ var AgentPaymentPhaseDescriptions = {
|
|
|
76
90
|
[AgentPaymentPhase.FundingSent]: "The Haven funding leg was sent; the agent can continue the merchant/protocol leg.",
|
|
77
91
|
[AgentPaymentPhase.Rejected]: "The wallet owner rejected the request; the agent should stop and tell the user.",
|
|
78
92
|
[AgentPaymentPhase.Expired]: "The payment or approval request expired before completion.",
|
|
79
|
-
[AgentPaymentPhase.Failed]: "Haven could not complete the payment; the agent should stop and surface the failure."
|
|
93
|
+
[AgentPaymentPhase.Failed]: "Haven could not complete the payment; the agent should stop and surface the failure.",
|
|
94
|
+
[AgentPaymentPhase.InsufficientFunds]: "Pre-flight check determined the delegate balance plus the remaining on-chain allowance cannot cover the requested amount, so no payment was created. The originating Safe must be funded or the agent allowance raised before retrying."
|
|
80
95
|
};
|
|
81
96
|
var AgentPaymentNextActionDescriptions = {
|
|
82
97
|
[AgentPaymentNextAction.SignAndSubmitPayment]: "Sign with the delegate key and submit the payment to Haven.",
|
|
@@ -86,7 +101,8 @@ var AgentPaymentNextActionDescriptions = {
|
|
|
86
101
|
[AgentPaymentNextAction.WaitForUserToCompletePayment]: "Wait for the wallet owner to finish sending the approved funding payment.",
|
|
87
102
|
[AgentPaymentNextAction.RetryOriginalX402Request]: "Resume this payment id and retry the original x402 request with the merchant payment header.",
|
|
88
103
|
[AgentPaymentNextAction.StopAndTellUser]: "Stop retrying this payment and tell the user what happened.",
|
|
89
|
-
[AgentPaymentNextAction.RequestAgainIfUserStillWantsIt]: "Ask again only if the user still wants the payment after expiry."
|
|
104
|
+
[AgentPaymentNextAction.RequestAgainIfUserStillWantsIt]: "Ask again only if the user still wants the payment after expiry.",
|
|
105
|
+
[AgentPaymentNextAction.FundSafeOrRaiseAllowance]: "Stop and tell the user that the originating Safe needs to be funded or the agent allowance raised before the payment can succeed."
|
|
90
106
|
};
|
|
91
107
|
var AgentPaymentRailDescriptions = {
|
|
92
108
|
[AgentPaymentRail.Direct]: "Standard Haven payment from the user-controlled Safe through an approved delegate allowance.",
|
|
@@ -202,6 +218,7 @@ function verifySignature(hash, signature, expectedAddress) {
|
|
|
202
218
|
}
|
|
203
219
|
var BASE_USDC_ADDRESS = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
|
|
204
220
|
var X402_IDEMPOTENCY_BUCKET_MS = 3e5;
|
|
221
|
+
var DECIMAL_ATOMIC_AMOUNT_RE = /^[0-9]+$/;
|
|
205
222
|
function decodeBase64Json(value, label) {
|
|
206
223
|
try {
|
|
207
224
|
return JSON.parse(atob(value));
|
|
@@ -209,6 +226,12 @@ function decodeBase64Json(value, label) {
|
|
|
209
226
|
throw new Error(`Failed to decode ${label}`);
|
|
210
227
|
}
|
|
211
228
|
}
|
|
229
|
+
function isPositiveDecimalAtomicAmount(value) {
|
|
230
|
+
return DECIMAL_ATOMIC_AMOUNT_RE.test(value) && BigInt(value) > 0n;
|
|
231
|
+
}
|
|
232
|
+
function optionAuthorizationAmount(option) {
|
|
233
|
+
return option.maxAmountRequired ?? option.amount;
|
|
234
|
+
}
|
|
212
235
|
function normalizePaymentOption(value) {
|
|
213
236
|
const candidate = value;
|
|
214
237
|
if (!candidate || typeof candidate !== "object" || typeof candidate.scheme !== "string" || typeof candidate.network !== "string" || typeof candidate.asset !== "string" || typeof candidate.payTo !== "string") {
|
|
@@ -216,6 +239,10 @@ function normalizePaymentOption(value) {
|
|
|
216
239
|
}
|
|
217
240
|
const amount = typeof candidate.amount === "string" ? candidate.amount : typeof candidate.maxAmountRequired === "string" ? candidate.maxAmountRequired : null;
|
|
218
241
|
if (!amount) return null;
|
|
242
|
+
if (!isPositiveDecimalAtomicAmount(amount)) return null;
|
|
243
|
+
if (candidate.maxAmountRequired !== void 0 && (typeof candidate.maxAmountRequired !== "string" || !isPositiveDecimalAtomicAmount(candidate.maxAmountRequired))) {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
219
246
|
return {
|
|
220
247
|
scheme: candidate.scheme,
|
|
221
248
|
network: candidate.network,
|
|
@@ -316,11 +343,13 @@ function selectPaymentOption(accepts) {
|
|
|
316
343
|
for (const opt of accepts) {
|
|
317
344
|
if (opt.network in SUPPORTED_X402_NETWORKS) {
|
|
318
345
|
const networkTokens = NETWORK_TOKENS[opt.network];
|
|
319
|
-
if (networkTokens?.[opt.asset.toLowerCase()]
|
|
346
|
+
if (networkTokens?.[opt.asset.toLowerCase()] && isPositiveDecimalAtomicAmount(optionAuthorizationAmount(opt))) {
|
|
347
|
+
return opt;
|
|
348
|
+
}
|
|
320
349
|
}
|
|
321
350
|
}
|
|
322
351
|
for (const opt of accepts) {
|
|
323
|
-
if (opt.network in SUPPORTED_X402_NETWORKS) {
|
|
352
|
+
if (opt.network in SUPPORTED_X402_NETWORKS && isPositiveDecimalAtomicAmount(optionAuthorizationAmount(opt))) {
|
|
324
353
|
return opt;
|
|
325
354
|
}
|
|
326
355
|
}
|
|
@@ -329,14 +358,18 @@ function selectPaymentOption(accepts) {
|
|
|
329
358
|
function selectStandardPaymentOption(accepts) {
|
|
330
359
|
if (!accepts || accepts.length === 0) return null;
|
|
331
360
|
for (const opt of accepts) {
|
|
332
|
-
if (opt.scheme === "exact" && opt.network in STANDARD_X402_NETWORKS && opt.asset.toLowerCase() === BASE_USDC_ADDRESS) {
|
|
361
|
+
if (opt.scheme === "exact" && opt.network in STANDARD_X402_NETWORKS && opt.asset.toLowerCase() === BASE_USDC_ADDRESS && isPositiveDecimalAtomicAmount(optionAuthorizationAmount(opt))) {
|
|
333
362
|
return opt;
|
|
334
363
|
}
|
|
335
364
|
}
|
|
336
365
|
return null;
|
|
337
366
|
}
|
|
338
367
|
function x402AuthorizationAmount(option) {
|
|
339
|
-
|
|
368
|
+
const amount = optionAuthorizationAmount(option);
|
|
369
|
+
if (!isPositiveDecimalAtomicAmount(amount)) {
|
|
370
|
+
throw new Error("Invalid x402 amount: must be a positive decimal atomic amount");
|
|
371
|
+
}
|
|
372
|
+
return amount;
|
|
340
373
|
}
|
|
341
374
|
function buildX402ExpectedMessage(context) {
|
|
342
375
|
return `Haven x402 expected context v1
|
|
@@ -1176,7 +1209,7 @@ var HavenClient = class {
|
|
|
1176
1209
|
...initialInit,
|
|
1177
1210
|
headers: retryHeaders
|
|
1178
1211
|
});
|
|
1179
|
-
if (retryResponse.
|
|
1212
|
+
if (!retryResponse.ok) {
|
|
1180
1213
|
await this.recordMerchantRetryRejected({
|
|
1181
1214
|
rail: "x402",
|
|
1182
1215
|
paymentId: receipt.paymentId,
|
|
@@ -1189,8 +1222,8 @@ var HavenClient = class {
|
|
|
1189
1222
|
}
|
|
1190
1223
|
});
|
|
1191
1224
|
throw new HavenApiError(
|
|
1192
|
-
"x402 retry
|
|
1193
|
-
|
|
1225
|
+
"x402 retry failed after Haven funded the delegate wallet; reconciliation may be required.",
|
|
1226
|
+
retryResponse.status,
|
|
1194
1227
|
{
|
|
1195
1228
|
marker: "x402_retry_rejected_after_funding",
|
|
1196
1229
|
payment_id: receipt.paymentId,
|
|
@@ -1329,7 +1362,7 @@ var HavenClient = class {
|
|
|
1329
1362
|
...initialInit,
|
|
1330
1363
|
headers: retryHeaders
|
|
1331
1364
|
});
|
|
1332
|
-
if (retryResponse.
|
|
1365
|
+
if (!retryResponse.ok) {
|
|
1333
1366
|
await this.recordMerchantRetryRejected({
|
|
1334
1367
|
rail: receipt.rail,
|
|
1335
1368
|
paymentId: receipt.paymentId,
|
|
@@ -1341,8 +1374,8 @@ var HavenClient = class {
|
|
|
1341
1374
|
}
|
|
1342
1375
|
});
|
|
1343
1376
|
throw new HavenApiError(
|
|
1344
|
-
"Machine payment retry
|
|
1345
|
-
|
|
1377
|
+
"Machine payment retry failed after Haven sent the payment.",
|
|
1378
|
+
retryResponse.status,
|
|
1346
1379
|
{
|
|
1347
1380
|
marker: "machine_payment_retry_rejected_after_payment",
|
|
1348
1381
|
payment_id: receipt.paymentId,
|
|
@@ -2225,7 +2258,7 @@ var HavenClient = class {
|
|
|
2225
2258
|
};
|
|
2226
2259
|
}
|
|
2227
2260
|
mapPaymentReceipt(raw) {
|
|
2228
|
-
|
|
2261
|
+
const receipt = {
|
|
2229
2262
|
id: raw.id,
|
|
2230
2263
|
paymentId: raw.payment_id,
|
|
2231
2264
|
rail: raw.rail,
|
|
@@ -2252,6 +2285,13 @@ var HavenClient = class {
|
|
|
2252
2285
|
createdAt: raw.created_at,
|
|
2253
2286
|
updatedAt: raw.updated_at
|
|
2254
2287
|
};
|
|
2288
|
+
if ("payment_intent_id" in raw) {
|
|
2289
|
+
receipt.paymentIntentId = raw.payment_intent_id ?? null;
|
|
2290
|
+
}
|
|
2291
|
+
if ("approval_request_id" in raw) {
|
|
2292
|
+
receipt.approvalRequestId = raw.approval_request_id ?? null;
|
|
2293
|
+
}
|
|
2294
|
+
return receipt;
|
|
2255
2295
|
}
|
|
2256
2296
|
};
|
|
2257
2297
|
function sleep(ms) {
|
|
@@ -2300,13 +2340,19 @@ var toolDescriptions = {
|
|
|
2300
2340
|
quoteX402: {
|
|
2301
2341
|
summary: "Inspect an HTTP 402 x402 paid resource without creating a Haven payment, signature, approval, or on-chain transaction.",
|
|
2302
2342
|
behavior: "Probes the merchant directly and parses the 402 response. Pure read-only client behavior \u2014 Haven is not contacted.",
|
|
2303
|
-
nextActionGuidance: ""
|
|
2343
|
+
nextActionGuidance: "On success the returned quote is the input to haven_pay_x402_quote. Do not call the merchant again \u2014 Haven re-uses the captured request when paying."
|
|
2304
2344
|
},
|
|
2305
2345
|
payX402: {
|
|
2306
2346
|
summary: "Pay an inspected x402 quote. The delegate key signs locally; Haven only validates and relays signed, on-chain-constrained payment transactions.",
|
|
2307
2347
|
selectionGuidance: "Do not use this for read-only allowance, budget, spend-limit, remaining-amount, reset-period, or what-can-I-spend questions; use the allowance lookup tool instead.",
|
|
2308
2348
|
behavior: "Signs the EIP-3009 payment from the delegate wallet, asks Haven for a Safe AllowanceModule top-up if needed, and returns the merchant response or a pending-approval state.",
|
|
2309
|
-
nextActionGuidance: "If approval is needed, preserve the returned resume_state and wait for nextAction=retry_original_x402_request before resuming."
|
|
2349
|
+
nextActionGuidance: "If approval is needed, preserve the returned resume_state and wait for nextAction=retry_original_x402_request before resuming. If the response carries phase=insufficient_funds and nextAction=fund_safe_or_raise_allowance, the payment cannot be retried until the originating Safe is funded or the agent allowance raised \u2014 stop and tell the user the shortfall reported on the response."
|
|
2350
|
+
},
|
|
2351
|
+
payX402OneShot: {
|
|
2352
|
+
summary: "Fetch an x402 paid HTTP resource in a single call. Handles the full probe -> pay -> retry round trip and returns the merchant response.",
|
|
2353
|
+
selectionGuidance: "Prefer this over the quote+pay split when the agent just wants the paid resource and does not need to inspect the price first. If you already have a quote from haven_quote_x402, use haven_pay_x402_quote instead. Do not use for read-only allowance, budget, spend-limit, remaining-amount, reset-period, or what-can-I-spend questions; use the allowance lookup tool instead.",
|
|
2354
|
+
behavior: "Calls the URL, parses any HTTP 402 x402 challenge, signs the EIP-3009 payment from the delegate wallet, asks Haven for a Safe AllowanceModule top-up if needed, then retries the original request with the X-PAYMENT header and returns the merchant response. If the resource returns an MPP machine-payment challenge instead of standard x402, the MPP payment path is used automatically. If the resource returns a non-402 status, returns it unchanged without contacting Haven.",
|
|
2355
|
+
nextActionGuidance: "If approval is needed, preserve the returned resume_state or paymentId and call the resume tool once nextAction=retry_original_x402_request. If the response carries phase=insufficient_funds and nextAction=fund_safe_or_raise_allowance, the payment cannot be retried until the originating Safe is funded or the agent allowance raised \u2014 stop and tell the user the shortfall reported on the response."
|
|
2310
2356
|
},
|
|
2311
2357
|
resumeX402: {
|
|
2312
2358
|
summary: "Resume an x402 payment after the Haven wallet owner approved the funding step.",
|
|
@@ -2316,7 +2362,7 @@ var toolDescriptions = {
|
|
|
2316
2362
|
quoteMpp: {
|
|
2317
2363
|
summary: "Inspect a Haven MPP challenge or paid MPP URL without creating a Haven payment, signature, approval, or on-chain transaction.",
|
|
2318
2364
|
behavior: "Parses an MPP challenge envelope and returns a typed quote with rail tag, amount, asset, and merchant context. Pure read-only \u2014 Haven is not contacted.",
|
|
2319
|
-
nextActionGuidance: ""
|
|
2365
|
+
nextActionGuidance: "On success the returned quote is the input to haven_pay_mpp_challenge. Do not call the merchant again \u2014 Haven re-uses the captured request when paying."
|
|
2320
2366
|
},
|
|
2321
2367
|
payMpp: {
|
|
2322
2368
|
summary: "Pay an inspected MPP challenge. The delegate key signs locally; Haven only validates and relays signed, on-chain-constrained payment transactions.",
|