@haven_ai/sdk 0.0.0-dev.202609141814.b7c3b83 → 0.0.0-dev.202609142053.7f40fec
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 +8 -3
- package/dist/index.cjs +40 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -9
- package/dist/index.d.ts +40 -9
- package/dist/index.js +40 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -85,9 +85,13 @@ shows up in your Haven dashboard activity feed.
|
|
|
85
85
|
## Step-by-Step API
|
|
86
86
|
|
|
87
87
|
Discovery and listing: `discoverTools({ verified?: 'any' | 'verified' | 'operator' })` returns the merged
|
|
88
|
-
catalog — operator-curated plus
|
|
89
|
-
`domainVerified` and `verifiedPayable`. `
|
|
90
|
-
|
|
88
|
+
catalog — operator-curated rows plus self-submitted directory entries (epic #1717), each with `source`,
|
|
89
|
+
`domainVerified` and `verifiedPayable`. `verified: 'verified'` (#2978) filters on the badge, not the
|
|
90
|
+
source: it returns any entry, from either source, whose endpoint Haven watched answer a live quote
|
|
91
|
+
probe (`verifiedPayable === true`); `domainVerified` stays true only for entries that also proved
|
|
92
|
+
domain ownership. `verified: 'operator'` filters on provenance alone. `submitCatalogEntry(resourceUrl)`
|
|
93
|
+
submits a merchant endpoint to the Verified Payable Directory (queue-only; the seller still must prove
|
|
94
|
+
domain ownership before listing),
|
|
91
95
|
and `getCatalogSubmissionStatus(id)` returns coarse status plus the ownership-proof instructions while
|
|
92
96
|
the submission can still prove ownership.
|
|
93
97
|
|
|
@@ -402,6 +406,7 @@ Hosted MCP and signer tools also return stable `code` values on recoverable x402
|
|
|
402
406
|
| `PRICE_EXCEEDS_MAX` | The merchant-authoritative x402 price is above the caller's spending cap. No funding transfer was created. | Tell the user the live price exceeded the cap and retry only after they confirm a higher one. Payloads carry `next_action: stop_and_tell_user` and `retry_with_new_quote: true` — the latter means any retry needs a fresh quote, not that one should be attempted unattended. |
|
|
403
407
|
| `AMBIGUOUS_MAX_AMOUNT` | Both `max_amount` (atomic units) and `max_amount_human` (whole tokens) were sent for one purchase. Nothing was contacted and nothing was spent. | Re-send with exactly one — `max_amount_human` for a cap the user stated in tokens, `max_amount` for an exact atomic figure. |
|
|
404
408
|
| `MAX_AMOUNT_UNCONVERTIBLE` | `max_amount_human` could not be converted against this quote's asset — its decimals are unknown to Haven, or the cap has more decimal places than the asset supports. Nothing was spent. | Round the cap to the asset's decimals, or re-send it as an exact atomic `max_amount`. |
|
|
409
|
+
| `MERCHANT_NOT_READY` | The merchant answered the quote probe with its own `503 { error: 'merchant_not_ready', reason_code, retry_after_s }` instead of a 402 — it cannot settle right now (e.g. its settlement wallet is out of gas). No payment was created. | Tell the user and retry later (`retry_after_s` in the message when given); do not treat it as a wrong endpoint. Payloads carry `next_action: stop_and_tell_user` and `retry_with_new_quote: true`. |
|
|
405
410
|
| `PAYMENT_WINDOW_EXPIRED` | The funding/quote window closed before `haven_x402_sign_header`, `haven_submit`, or `haven_complete_mcp_tool` could finish. | Re-run `haven_pay_mcp_tool` with the same `idempotency_key`, then sign and complete the fresh quote. Payloads include `retry_with_new_quote: true`. |
|
|
406
411
|
| `MERCHANT_REJECTED_AFTER_FUNDING` | Haven's funding leg succeeded, but the merchant rejected the paid retry. | Stop retrying the merchant and call `haven_sweep_delegate` so the user can recover stranded delegate USDC. |
|
|
407
412
|
|
package/dist/index.cjs
CHANGED
|
@@ -195,7 +195,17 @@ var AgentPaymentFailureCode = {
|
|
|
195
195
|
* asset can represent (truncating it would silently change the user's cap).
|
|
196
196
|
* The fallback is the exact atomic `max_amount`.
|
|
197
197
|
*/
|
|
198
|
-
MaxAmountUnconvertible: "MAX_AMOUNT_UNCONVERTIBLE"
|
|
198
|
+
MaxAmountUnconvertible: "MAX_AMOUNT_UNCONVERTIBLE",
|
|
199
|
+
/**
|
|
200
|
+
* #2979: the merchant answered a `tools/call` probe with its own
|
|
201
|
+
* machine-readable "cannot settle right now" refusal (HTTP 503,
|
|
202
|
+
* `{ error: 'merchant_not_ready', reason_code, ... }`) instead of a 402
|
|
203
|
+
* challenge — e.g. its settlement wallet is out of gas. No 402 was ever
|
|
204
|
+
* issued and no payment was created; this is honest and (per
|
|
205
|
+
* `retry_after_s`, when present) usually transient, unlike a permanent
|
|
206
|
+
* endpoint miss.
|
|
207
|
+
*/
|
|
208
|
+
MerchantNotReady: "MERCHANT_NOT_READY"
|
|
199
209
|
};
|
|
200
210
|
var AgentPaymentRail = {
|
|
201
211
|
/** Standard Haven payment from the user's Safe through an approved delegate allowance. */
|
|
@@ -253,7 +263,8 @@ var AgentPaymentFailureCodeDescriptions = {
|
|
|
253
263
|
[AgentPaymentFailureCode.MerchantUnresponsiveAfterFunding]: "The Haven funding leg succeeded, but the merchant did not answer the paid retry before the timeout. The merchant may still settle late \u2014 check haven_get_payment_status (and retry haven_complete_mcp_tool once) BEFORE sweeping; sweep only if no settlement appears.",
|
|
254
264
|
[AgentPaymentFailureCode.MerchantCallContextUnavailable]: "merchant_url/tool_name were omitted and no stored merchant call context is available for this payment_id. Re-send merchant_url, tool_name, arguments, and mcp_transport explicitly.",
|
|
255
265
|
[AgentPaymentFailureCode.AmbiguousMaxAmount]: "Both max_amount (atomic units) and max_amount_human (whole tokens) were supplied for one purchase. Nothing was contacted and nothing was spent. Re-send with exactly ONE: max_amount_human for a cap the user stated in tokens, max_amount for an exact atomic figure.",
|
|
256
|
-
[AgentPaymentFailureCode.MaxAmountUnconvertible]: "max_amount_human could not be converted to atomic units against this quote's asset \u2014 either its decimals are unknown to Haven or the cap has more decimal places than the asset supports. Nothing was spent. Round the cap, or re-send it as an exact atomic max_amount."
|
|
266
|
+
[AgentPaymentFailureCode.MaxAmountUnconvertible]: "max_amount_human could not be converted to atomic units against this quote's asset \u2014 either its decimals are unknown to Haven or the cap has more decimal places than the asset supports. Nothing was spent. Round the cap, or re-send it as an exact atomic max_amount.",
|
|
267
|
+
[AgentPaymentFailureCode.MerchantNotReady]: 'The merchant refused the probe with its own "cannot settle right now" signal instead of a 402 challenge. No payment was created. Often transient \u2014 retry later (see retry_after_s in the message, if given) rather than treating this as a broken or wrong endpoint.'
|
|
257
268
|
};
|
|
258
269
|
var AgentPaymentWarningCode = {
|
|
259
270
|
/** No max_amount cap was supplied — the live quoted price was accepted as-is. */
|
|
@@ -348,8 +359,18 @@ var MerchantTimeoutError = class extends HavenApiError {
|
|
|
348
359
|
};
|
|
349
360
|
var X402UnexpectedStatusError = class extends HavenApiError {
|
|
350
361
|
x402ErrorCode = "unexpected_non_402_status";
|
|
351
|
-
|
|
352
|
-
|
|
362
|
+
/**
|
|
363
|
+
* #2979: `body` is the merchant's own JSON, when the non-402 response
|
|
364
|
+
* carried one — e.g. the demo merchant's `/mcp` readiness gate answers
|
|
365
|
+
* `503 { error: 'merchant_not_ready', reason_code, ... }`. Optional and
|
|
366
|
+
* best-effort: a non-JSON or unreadable body leaves this `undefined`, same
|
|
367
|
+
* as before this field existed. Consumers key on it (not on the message
|
|
368
|
+
* string) to distinguish an honest, machine-readable merchant refusal from
|
|
369
|
+
* a genuine "this is not the x402 endpoint" miss, which otherwise look
|
|
370
|
+
* identical — both are just "some non-402 status".
|
|
371
|
+
*/
|
|
372
|
+
constructor(message, statusCode, body) {
|
|
373
|
+
super(message, statusCode, body);
|
|
353
374
|
this.name = "X402UnexpectedStatusError";
|
|
354
375
|
}
|
|
355
376
|
};
|
|
@@ -3509,7 +3530,7 @@ var HavenClient = class {
|
|
|
3509
3530
|
const query = params.size > 0 ? `?${params.toString()}` : "";
|
|
3510
3531
|
const raw = await this.get(`/catalog${query}`);
|
|
3511
3532
|
let entries = raw.entries.map(mapCatalogEntry);
|
|
3512
|
-
if (options.verified === "verified") entries = entries.filter((e) => e.
|
|
3533
|
+
if (options.verified === "verified") entries = entries.filter((e) => e.verifiedPayable === true);
|
|
3513
3534
|
if (options.verified === "operator") entries = entries.filter((e) => e.source === "operator");
|
|
3514
3535
|
return entries;
|
|
3515
3536
|
}
|
|
@@ -3646,9 +3667,16 @@ var HavenClient = class {
|
|
|
3646
3667
|
const request = snapshotX402Request(url, initialInit);
|
|
3647
3668
|
const response = await this.merchantTransport.fetch(url, initialInit);
|
|
3648
3669
|
if (response.status !== 402) {
|
|
3670
|
+
let body;
|
|
3671
|
+
try {
|
|
3672
|
+
body = await response.clone().json();
|
|
3673
|
+
} catch {
|
|
3674
|
+
body = void 0;
|
|
3675
|
+
}
|
|
3649
3676
|
throw new X402UnexpectedStatusError(
|
|
3650
3677
|
`Expected an x402 quote response with HTTP 402, got HTTP ${response.status}.`,
|
|
3651
|
-
response.status || 400
|
|
3678
|
+
response.status || 400,
|
|
3679
|
+
body
|
|
3652
3680
|
);
|
|
3653
3681
|
}
|
|
3654
3682
|
if (response.headers.get("MACHINE-PAYMENT-CHALLENGE")) {
|
|
@@ -4222,7 +4250,7 @@ var toolDescriptions = {
|
|
|
4222
4250
|
},
|
|
4223
4251
|
discoverTools: {
|
|
4224
4252
|
summary: "Step 1 of a purchase: discover payable services from Haven's curated merchant catalog \u2014 names, prices, and which pay tool to use next.",
|
|
4225
|
-
selectionGuidance: "Use this when the user asks what the agent can buy, pay for, or which paid services exist \u2014 or when you need a resource URL for a service the user described. Use verified=verified
|
|
4253
|
+
selectionGuidance: "Use this when the user asks what the agent can buy, pay for, or which paid services exist \u2014 or when you need a resource URL for a service the user described. Use verified=verified for entries Haven watched pass a live quote probe (operator-curated or self-submitted) \u2014 domain_verified is the only ownership claim; never treat these badges as proof of merchant honesty, quality, or reliability. Do NOT use for balance, budget, or spend-limit questions \u2014 use haven_get_allowances. Do NOT use to pay \u2014 each returned entry names the pay tool to use next.",
|
|
4226
4254
|
behavior: "Use each entry's suggested_tool field first \u2014 it names the exact next call. Read-only lookup against Haven's curated catalog; entries are periodically re-verified against the live merchant and degraded entries are flagged. Use category for a case-insensitive category filter (for example, VPN or vpn), or search for a product name, category, or description term. Returns name, description, price, rail, resource URL, tool_name, tool_arguments, suggested_tool, and the provenance badges source/domain_verified/verified_payable. The catalog price (price_display/price_atomic, marked price_is_indicative) is a last-verified hint, NOT authoritative \u2014 the real price comes from the merchant's live 402 at pay time. Never creates a payment, signature, or approval.",
|
|
4227
4255
|
nextActionGuidance: `Pick an entry and pay it with the tool named in suggested_tool, passing the entry's resource_url, tool_name, and tool_arguments for MCP merchants. Confirm the price from the live pay-tool result (not the catalog), and pass the user's cap as max_amount_human in whole tokens ("no more than 1 USDC" \u2192 max_amount_human: "1") \u2014 never convert it to atomic units by hand.`
|
|
4228
4256
|
},
|
|
@@ -4850,6 +4878,11 @@ present and surface \`message\` or \`error\` verbatim. Common cases:
|
|
|
4850
4878
|
Round the cap, or send an exact atomic \`max_amount\`.
|
|
4851
4879
|
- \`PAYMENT_WINDOW_EXPIRED\`: re-run the quote/prepare tool with the same
|
|
4852
4880
|
\`idempotency_key\`, then sign the fresh payload.
|
|
4881
|
+
- \`MERCHANT_NOT_READY\`: the merchant refused the quote with its own
|
|
4882
|
+
"cannot settle right now" signal (a 503 \`merchant_not_ready\` with a
|
|
4883
|
+
\`reason_code\`) instead of a 402. No payment was created. Tell the user;
|
|
4884
|
+
retry later (the message carries \`retry_after_s\` when the merchant gave
|
|
4885
|
+
one) \u2014 this is not a wrong or broken endpoint.
|
|
4853
4886
|
- \`MERCHANT_REJECTED_AFTER_FUNDING\`: the merchant refused the paid retry.
|
|
4854
4887
|
Stop-and-sweep \u2014 stop retrying the merchant and use
|
|
4855
4888
|
\`mcp__haven__haven_sweep_delegate\` to recover stranded delegate funds.
|