@haven_ai/sdk 0.2.1-alpha.0 → 0.4.0-alpha.0
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 +1 -0
- package/dist/index.cjs +326 -89
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +397 -142
- package/dist/index.d.ts +397 -142
- package/dist/index.js +315 -84
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -112,12 +112,12 @@ var AgentPaymentNextAction = {
|
|
|
112
112
|
* the agent's per-token allowance needs to be raised before the payment
|
|
113
113
|
* can succeed. A user approval will not fix this state on its own.
|
|
114
114
|
*
|
|
115
|
-
* #
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
115
|
+
* #2914: the account-vocabulary spelling, and the only one — the
|
|
116
|
+
* pre-#2907 `fund_safe_or_raise_allowance` wire value (and the
|
|
117
|
+
* `AgentPaymentNextActionAccountAlias` seam #2908 added to bridge it) are
|
|
118
|
+
* retired along with the rest of the #2908 compatibility window.
|
|
119
119
|
*/
|
|
120
|
-
|
|
120
|
+
FundAccountOrRaiseAllowance: "fund_account_or_raise_allowance",
|
|
121
121
|
/**
|
|
122
122
|
* The delegate wallet may hold funds that were sent from the Safe but never
|
|
123
123
|
* settled to the merchant. The wallet owner should initiate a sweep to
|
|
@@ -144,19 +144,6 @@ var AgentPaymentNextAction = {
|
|
|
144
144
|
*/
|
|
145
145
|
AwaitingSettlementEvidence: "awaiting_settlement_evidence"
|
|
146
146
|
};
|
|
147
|
-
var AgentPaymentNextActionAccountAlias = {
|
|
148
|
-
/** Account-vocabulary twin of `fund_safe_or_raise_allowance`; same meaning. */
|
|
149
|
-
FundAccountOrRaiseAllowance: "fund_account_or_raise_allowance"
|
|
150
|
-
};
|
|
151
|
-
function canonicalAgentPaymentNextAction(value) {
|
|
152
|
-
if (value === AgentPaymentNextActionAccountAlias.FundAccountOrRaiseAllowance) {
|
|
153
|
-
return AgentPaymentNextAction.FundSafeOrRaiseAllowance;
|
|
154
|
-
}
|
|
155
|
-
return value;
|
|
156
|
-
}
|
|
157
|
-
function isFundAccountOrRaiseAllowance(value) {
|
|
158
|
-
return value === AgentPaymentNextAction.FundSafeOrRaiseAllowance || value === AgentPaymentNextActionAccountAlias.FundAccountOrRaiseAllowance;
|
|
159
|
-
}
|
|
160
147
|
var AgentPaymentFailureCode = {
|
|
161
148
|
/** A merchant-authoritative x402 price exceeds the caller's pre-funding max_amount cap. */
|
|
162
149
|
PriceExceedsMax: "PRICE_EXCEEDS_MAX",
|
|
@@ -251,7 +238,7 @@ var AgentPaymentNextActionDescriptions = {
|
|
|
251
238
|
[AgentPaymentNextAction.StopAndTellUser]: "Stop retrying this payment and tell the user what happened.",
|
|
252
239
|
[AgentPaymentNextAction.RequestAgainIfUserStillWantsIt]: "Ask again only if the user still wants the payment after expiry.",
|
|
253
240
|
[AgentPaymentNextAction.PaymentWindowExpired]: "The x402 funding/quote window expired. Re-quote with the same idempotency key before asking the signer to build a merchant payment header again.",
|
|
254
|
-
[AgentPaymentNextAction.
|
|
241
|
+
[AgentPaymentNextAction.FundAccountOrRaiseAllowance]: "Stop and tell the user that the account needs to be funded or the agent budget raised before the payment can succeed.",
|
|
255
242
|
[AgentPaymentNextAction.RetryWithExplicitContext]: "Retry the same tool call, this time passing merchant_url, tool_name, arguments, and mcp_transport explicitly \u2014 the server had no stored context to rehydrate for this payment id.",
|
|
256
243
|
[AgentPaymentNextAction.SweepStrandedFunds]: "Tell the user that funds may be stranded in the delegate wallet and prompt them to initiate a sweep in Haven to return them to the originating account.",
|
|
257
244
|
[AgentPaymentNextAction.AwaitingSettlementEvidence]: "The settlement window passed with no verified on-chain evidence yet. If you hold the merchant's real settlement transaction hash, report it with haven_report_settlement_evidence. Otherwise, Haven's settlement sweep may still attribute it within about two minutes \u2014 poll getPaymentStatus once more, then tell the user the goods were delivered but unverified if it still shows nothing."
|
|
@@ -719,7 +706,13 @@ function normalizePaymentOption(value) {
|
|
|
719
706
|
mimeType: candidate.mimeType,
|
|
720
707
|
asset: candidate.asset,
|
|
721
708
|
payTo: candidate.payTo,
|
|
722
|
-
|
|
709
|
+
// Keep the merchant offer intact for v2 `accepted` matching. Only the
|
|
710
|
+
// signing conversion may cap its authorization lifetime (#3117).
|
|
711
|
+
// Retain legacy fallback behavior for missing or unusable timeouts.
|
|
712
|
+
// Integer, because the authorize body types it `integer` and the child's
|
|
713
|
+
// timestamp caveat is built from it — the old clamp floored, so keeping a
|
|
714
|
+
// fractional value here would 400 a merchant that used to work (#3117).
|
|
715
|
+
maxTimeoutSeconds: typeof candidate.maxTimeoutSeconds === "number" && Number.isFinite(candidate.maxTimeoutSeconds) && candidate.maxTimeoutSeconds >= 1 ? Math.floor(candidate.maxTimeoutSeconds) : clampAuthorizationWindow(candidate.maxTimeoutSeconds),
|
|
723
716
|
extra: candidate.extra
|
|
724
717
|
};
|
|
725
718
|
}
|
|
@@ -870,6 +863,13 @@ function selectPaymentOption(accepts) {
|
|
|
870
863
|
return null;
|
|
871
864
|
}
|
|
872
865
|
var ERC7710_ASSET_TRANSFER_METHOD = "erc7710";
|
|
866
|
+
function isEip3009ConstructibleOption(option) {
|
|
867
|
+
const rawMethod = option.extra?.assetTransferMethod;
|
|
868
|
+
if (rawMethod !== void 0 && rawMethod !== "eip3009") return false;
|
|
869
|
+
const rawFlow = option.extra?.paymentFlow;
|
|
870
|
+
if (rawFlow !== void 0 && rawFlow !== "authorization") return false;
|
|
871
|
+
return true;
|
|
872
|
+
}
|
|
873
873
|
function x402AssetTransferMethod(option) {
|
|
874
874
|
const raw = option.extra?.assetTransferMethod;
|
|
875
875
|
return typeof raw === "string" ? raw : null;
|
|
@@ -890,7 +890,8 @@ function selectStandardPaymentOption(accepts) {
|
|
|
890
890
|
if (!accepts || accepts.length === 0) return null;
|
|
891
891
|
for (const opt of accepts) {
|
|
892
892
|
if (opt === null || typeof opt !== "object") continue;
|
|
893
|
-
if (!
|
|
893
|
+
if (!isEip3009ConstructibleOption(opt)) continue;
|
|
894
|
+
if (isPayableStandardOption(opt)) return opt;
|
|
894
895
|
}
|
|
895
896
|
return null;
|
|
896
897
|
}
|
|
@@ -899,6 +900,7 @@ function selectErc7710PaymentOption(accepts) {
|
|
|
899
900
|
for (const opt of accepts) {
|
|
900
901
|
if (opt === null || typeof opt !== "object") continue;
|
|
901
902
|
if (isErc7710Option(opt) && isPayableStandardOption(opt)) return opt;
|
|
903
|
+
if (!isEip3009ConstructibleOption(opt)) continue;
|
|
902
904
|
}
|
|
903
905
|
return null;
|
|
904
906
|
}
|
|
@@ -960,6 +962,11 @@ function toStandardPaymentRequirements(paymentRequired, option) {
|
|
|
960
962
|
if (option.scheme !== "exact") {
|
|
961
963
|
throw new Error(`Unsupported x402 scheme: ${option.scheme}`);
|
|
962
964
|
}
|
|
965
|
+
if (!isEip3009ConstructibleOption(option)) {
|
|
966
|
+
throw new Error(
|
|
967
|
+
"Unsupported x402 payment requirements: this SDK constructs EIP-3009 authorization payments only. The merchant advertised a different extra.assetTransferMethod or an unrecognized extra.paymentFlow."
|
|
968
|
+
);
|
|
969
|
+
}
|
|
963
970
|
return {
|
|
964
971
|
scheme: "exact",
|
|
965
972
|
network,
|
|
@@ -969,14 +976,11 @@ function toStandardPaymentRequirements(paymentRequired, option) {
|
|
|
969
976
|
mimeType: option.mimeType ?? paymentRequired.resource.mimeType ?? "application/octet-stream",
|
|
970
977
|
payTo: option.payTo,
|
|
971
978
|
asset: option.asset,
|
|
972
|
-
//
|
|
973
|
-
//
|
|
974
|
-
// `
|
|
975
|
-
//
|
|
976
|
-
//
|
|
977
|
-
// the library's `validBefore = now + this value` then carries enough
|
|
978
|
-
// slack to satisfy the facilitator's `validBefore ≥ now + maxTimeout`
|
|
979
|
-
// verify rule after our funding leg confirms.
|
|
979
|
+
// Signing-only policy (#715/#3117): parsed and directly supplied options
|
|
980
|
+
// share this cap before the library computes validBefore. Keep the offer
|
|
981
|
+
// echoed in `accepted` unchanged. The forward margin (#1256) adds time
|
|
982
|
+
// for funding/retry, but does not guarantee a merchant timeout above the
|
|
983
|
+
// total bounded lifetime can pass facilitator verification.
|
|
980
984
|
maxTimeoutSeconds: clampAuthorizationWindow(option.maxTimeoutSeconds) + X402_SETTLEMENT_FORWARD_MARGIN_SECONDS,
|
|
981
985
|
extra: option.extra
|
|
982
986
|
};
|
|
@@ -1224,10 +1228,7 @@ function mapPaymentStatusResult(raw) {
|
|
|
1224
1228
|
rail: raw.rail,
|
|
1225
1229
|
status: raw.status,
|
|
1226
1230
|
phase: raw.phase,
|
|
1227
|
-
|
|
1228
|
-
// so every `=== AgentPaymentNextAction.X` downstream keeps working when
|
|
1229
|
-
// the server flips its emit at #2914.
|
|
1230
|
-
nextAction: canonicalAgentPaymentNextAction(raw.next_action),
|
|
1231
|
+
nextAction: raw.next_action,
|
|
1231
1232
|
amount: raw.amount,
|
|
1232
1233
|
token: raw.token,
|
|
1233
1234
|
resourceUrl: raw.resource_url,
|
|
@@ -1363,7 +1364,7 @@ function messageForState(label, status, paymentId, nextAction) {
|
|
|
1363
1364
|
function paymentStateFromRaw(label, raw) {
|
|
1364
1365
|
if (!raw.payment_id || !raw.status) return null;
|
|
1365
1366
|
const phase = raw.phase ?? phaseForStatus(raw.status);
|
|
1366
|
-
const nextAction =
|
|
1367
|
+
const nextAction = raw.next_action ?? nextActionForStatus(raw.status);
|
|
1367
1368
|
if (!phase || !nextAction) return null;
|
|
1368
1369
|
const amount = raw.amount ?? raw.requested ?? "";
|
|
1369
1370
|
const token = raw.token ?? "";
|
|
@@ -1434,6 +1435,51 @@ function throwPaymentStateError(label, raw) {
|
|
|
1434
1435
|
throw new HavenApiError(message, statusCode, raw);
|
|
1435
1436
|
}
|
|
1436
1437
|
|
|
1438
|
+
// src/x402-retry-target.ts
|
|
1439
|
+
function resolveX402RetryTarget(input) {
|
|
1440
|
+
const requestUrl = input.requestUrl?.trim() || void 0;
|
|
1441
|
+
if (requestUrl) {
|
|
1442
|
+
return {
|
|
1443
|
+
url: requestUrl,
|
|
1444
|
+
source: "request",
|
|
1445
|
+
resourceUrlDiffersFromRequest: requestUrl !== input.resourceUrl
|
|
1446
|
+
};
|
|
1447
|
+
}
|
|
1448
|
+
return { url: input.resourceUrl, source: "resource" };
|
|
1449
|
+
}
|
|
1450
|
+
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "::1", "[::1]"]);
|
|
1451
|
+
var RESERVED_SUFFIXES = [".test", ".localhost", ".invalid", ".example"];
|
|
1452
|
+
var IPV4_LOOPBACK = /^127(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}$/;
|
|
1453
|
+
function isSecureX402RetryTarget(url) {
|
|
1454
|
+
let parsed;
|
|
1455
|
+
try {
|
|
1456
|
+
parsed = new URL(url);
|
|
1457
|
+
} catch {
|
|
1458
|
+
return false;
|
|
1459
|
+
}
|
|
1460
|
+
if (parsed.protocol === "https:") return true;
|
|
1461
|
+
if (parsed.protocol !== "http:") return false;
|
|
1462
|
+
const host = parsed.hostname.toLowerCase();
|
|
1463
|
+
if (LOOPBACK_HOSTS.has(host) || IPV4_LOOPBACK.test(host)) return true;
|
|
1464
|
+
return RESERVED_SUFFIXES.some((suffix) => host.endsWith(suffix));
|
|
1465
|
+
}
|
|
1466
|
+
var INSECURE_RETRY_TARGET_CODE = "INSECURE_RETRY_TARGET";
|
|
1467
|
+
var HavenInsecureRetryTargetError = class extends HavenError {
|
|
1468
|
+
constructor(url) {
|
|
1469
|
+
super(
|
|
1470
|
+
`Refusing to send a payment header to ${url}: the paid x402 retry must go to an https URL (or a loopback / reserved test host). The merchant's challenge declared this resource URL; retry with the https URL you quoted (pass it as \`url\`), and report a merchant whose challenge downgrades the scheme.`,
|
|
1471
|
+
INSECURE_RETRY_TARGET_CODE,
|
|
1472
|
+
400
|
|
1473
|
+
);
|
|
1474
|
+
this.url = url;
|
|
1475
|
+
this.name = "HavenInsecureRetryTargetError";
|
|
1476
|
+
}
|
|
1477
|
+
url;
|
|
1478
|
+
};
|
|
1479
|
+
function assertSecureX402RetryTarget(url) {
|
|
1480
|
+
if (!isSecureX402RetryTarget(url)) throw new HavenInsecureRetryTargetError(url);
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1437
1483
|
// src/mcp-merchant-transport.ts
|
|
1438
1484
|
var DEFAULT_MERCHANT_TIMEOUT = 3e5;
|
|
1439
1485
|
var MCP_NOTIFICATION_TIMEOUT = 1e4;
|
|
@@ -1591,6 +1637,7 @@ var McpMerchantTransport = class {
|
|
|
1591
1637
|
* overwrite, which is worse than the duplicate this change removes.
|
|
1592
1638
|
*/
|
|
1593
1639
|
async deliverPayment(url, init, paymentHeader) {
|
|
1640
|
+
assertSecureX402RetryTarget(url);
|
|
1594
1641
|
const headers = new Headers(init?.headers);
|
|
1595
1642
|
const send = x402PaymentHeaderNamesFor(paymentHeader);
|
|
1596
1643
|
for (const name of X402_PAYMENT_HEADER_NAMES) {
|
|
@@ -1686,20 +1733,6 @@ function verifyPaymentReceipt(receipt, recover = defaultRecover) {
|
|
|
1686
1733
|
return { verified: true, recoveredSigner: recovered };
|
|
1687
1734
|
}
|
|
1688
1735
|
|
|
1689
|
-
// src/account-naming.ts
|
|
1690
|
-
function readAccountAddress(raw) {
|
|
1691
|
-
return raw.account_address ?? raw.safe_address ?? void 0;
|
|
1692
|
-
}
|
|
1693
|
-
function readAccountId(raw) {
|
|
1694
|
-
return raw.account_id ?? raw.safe_id ?? void 0;
|
|
1695
|
-
}
|
|
1696
|
-
function accountAddressTwins(address) {
|
|
1697
|
-
return { accountAddress: address, safeAddress: address };
|
|
1698
|
-
}
|
|
1699
|
-
function readX402ReceiptPayer(raw) {
|
|
1700
|
-
return raw.payer ?? raw.account_address ?? raw.sign_data?.components?.payer_account ?? raw.safe_address ?? raw.sign_data?.components?.safe;
|
|
1701
|
-
}
|
|
1702
|
-
|
|
1703
1736
|
// src/account-reads.ts
|
|
1704
1737
|
function safeBigInt(value) {
|
|
1705
1738
|
try {
|
|
@@ -1719,6 +1752,10 @@ function deriveReadiness(status, allowances) {
|
|
|
1719
1752
|
if (status !== "active") return "revoked";
|
|
1720
1753
|
return allowances.some((allowance) => safeBigInt(allowance.remainingAtomic) > 0n) ? "ready" : "needs_approval";
|
|
1721
1754
|
}
|
|
1755
|
+
function formatRemainingDisplay(tokenAddress, tokenSymbol, remainingAtomic) {
|
|
1756
|
+
const token = resolveTokenFromAddress(tokenAddress);
|
|
1757
|
+
return token ? `${formatAtomicAmount(safeBigInt(remainingAtomic), token.decimals)} ${tokenSymbol}` : `${remainingAtomic} ${tokenSymbol} (atomic; unknown decimals)`;
|
|
1758
|
+
}
|
|
1722
1759
|
var AccountReads = class {
|
|
1723
1760
|
transport;
|
|
1724
1761
|
getPaymentStatus;
|
|
@@ -1740,12 +1777,12 @@ var AccountReads = class {
|
|
|
1740
1777
|
async getAgentSummary() {
|
|
1741
1778
|
const [agent, allowanceSummary] = await Promise.all([this.getAgent(), this.getAllowances()]);
|
|
1742
1779
|
const allowances = allowanceSummary.allowances.map((allowance) => {
|
|
1743
|
-
const token = resolveTokenFromAddress(allowance.tokenAddress);
|
|
1744
|
-
const remainingDisplay = token ? `${formatAtomicAmount(safeBigInt(allowance.onchain.remaining), token.decimals)} ${allowance.tokenSymbol}` : `${allowance.onchain.remaining} ${allowance.tokenSymbol} (atomic; unknown decimals)`;
|
|
1745
1780
|
return {
|
|
1781
|
+
id: allowance.id,
|
|
1746
1782
|
tokenSymbol: allowance.tokenSymbol,
|
|
1783
|
+
tokenAddress: allowance.tokenAddress,
|
|
1747
1784
|
remainingAtomic: allowance.onchain.remaining,
|
|
1748
|
-
remainingDisplay,
|
|
1785
|
+
remainingDisplay: allowance.remainingDisplay,
|
|
1749
1786
|
configuredAmount: allowance.configuredAmount,
|
|
1750
1787
|
resetPeriodMin: allowance.resetPeriodMin,
|
|
1751
1788
|
isResetPending: allowance.onchain.isResetPending
|
|
@@ -1758,10 +1795,13 @@ var AccountReads = class {
|
|
|
1758
1795
|
const raw = await this.transport.get("/machine-payments/allowances");
|
|
1759
1796
|
return {
|
|
1760
1797
|
agentId: raw.agent_id,
|
|
1761
|
-
//
|
|
1762
|
-
//
|
|
1763
|
-
//
|
|
1764
|
-
|
|
1798
|
+
// `account_address` is required on the wire contract, so the declared
|
|
1799
|
+
// type stays `string`; a server that omits it is off-contract and the
|
|
1800
|
+
// cast is the one place that case is allowed through as `undefined`
|
|
1801
|
+
// rather than a fabricated `''` (a present-but-blank address downstream
|
|
1802
|
+
// — the hosted MCP output spreads this object, and the sweep uses it as
|
|
1803
|
+
// a destination).
|
|
1804
|
+
accountAddress: raw.account_address,
|
|
1765
1805
|
delegateAddress: raw.delegate_address,
|
|
1766
1806
|
chainId: raw.chain_id,
|
|
1767
1807
|
allowances: raw.allowances.map((allowance) => ({
|
|
@@ -1770,6 +1810,7 @@ var AccountReads = class {
|
|
|
1770
1810
|
tokenSymbol: allowance.token_symbol,
|
|
1771
1811
|
configuredAmount: allowance.configured_amount,
|
|
1772
1812
|
resetPeriodMin: allowance.reset_period_min,
|
|
1813
|
+
remainingDisplay: formatRemainingDisplay(allowance.token_address, allowance.token_symbol, allowance.onchain.remaining),
|
|
1773
1814
|
onchain: {
|
|
1774
1815
|
amount: allowance.onchain.amount,
|
|
1775
1816
|
spent: allowance.onchain.spent,
|
|
@@ -1784,6 +1825,33 @@ var AccountReads = class {
|
|
|
1784
1825
|
}))
|
|
1785
1826
|
};
|
|
1786
1827
|
}
|
|
1828
|
+
/**
|
|
1829
|
+
* #3126 — the sufficiency signal behind {@link HavenBalanceCoverage}.
|
|
1830
|
+
*
|
|
1831
|
+
* Deliberately NOT a balance read: the endpoint answers whether the
|
|
1832
|
+
* account HOLDS at least the checked amount, as `covered
|
|
1833
|
+
* true/false/null`, and never returns the balance itself. The camelCase
|
|
1834
|
+
* mapping is permissive (raw fields flow through; the server owns the
|
|
1835
|
+
* wire shape, pinned by the backend's `expectMatchesSpec` assertion), so
|
|
1836
|
+
* an older server that has not deployed the endpoint surfaces its 404 as
|
|
1837
|
+
* a thrown error rather than a fabricated answer.
|
|
1838
|
+
*/
|
|
1839
|
+
async checkFunds(input) {
|
|
1840
|
+
const query = `token=${encodeURIComponent(input.token)}&amount_atomic=${encodeURIComponent(input.amountAtomic)}`;
|
|
1841
|
+
const raw = await this.transport.get(
|
|
1842
|
+
`/machine-payments/balance-coverage?${query}`
|
|
1843
|
+
);
|
|
1844
|
+
return {
|
|
1845
|
+
covered: raw.covered,
|
|
1846
|
+
...raw.coverage_error !== void 0 ? { coverageError: raw.coverage_error } : {},
|
|
1847
|
+
chainId: raw.chain_id,
|
|
1848
|
+
tokenAddress: raw.token_address,
|
|
1849
|
+
tokenSymbol: raw.token_symbol,
|
|
1850
|
+
checkedAmountAtomic: raw.checked_amount_atomic,
|
|
1851
|
+
budgetRemainingAtomic: raw.budget_remaining_atomic,
|
|
1852
|
+
...raw.budget_remaining_is_from_chain !== void 0 ? { budgetRemainingIsFromChain: raw.budget_remaining_is_from_chain } : {}
|
|
1853
|
+
};
|
|
1854
|
+
}
|
|
1787
1855
|
async getPostPurchaseAllowanceSummary(paymentId) {
|
|
1788
1856
|
const unavailable = (detail, payment2 = null) => ({
|
|
1789
1857
|
payment: payment2,
|
|
@@ -1835,10 +1903,23 @@ var AccountReads = class {
|
|
|
1835
1903
|
return unavailable(error instanceof Error ? error.message : String(error));
|
|
1836
1904
|
}
|
|
1837
1905
|
}
|
|
1906
|
+
/** The first page's receipts as a bare array — the pre-#3128 shape, kept for callers that never page. */
|
|
1838
1907
|
async listReceipts(options = {}) {
|
|
1839
|
-
|
|
1908
|
+
return (await this.listReceiptsPage(options)).receipts;
|
|
1909
|
+
}
|
|
1910
|
+
/** #3128: one page with `total`, `hasMore` and `nextCursor` — see {@link HavenPaymentReceiptsPage}. */
|
|
1911
|
+
async listReceiptsPage(options = {}) {
|
|
1912
|
+
const params = new URLSearchParams();
|
|
1913
|
+
if (options.limit) params.set("limit", String(options.limit));
|
|
1914
|
+
if (options.cursor) params.set("cursor", options.cursor);
|
|
1915
|
+
const query = params.size > 0 ? `?${params.toString()}` : "";
|
|
1840
1916
|
const raw = await this.transport.get(`/machine-payments/receipts${query}`);
|
|
1841
|
-
return
|
|
1917
|
+
return {
|
|
1918
|
+
receipts: raw.receipts.map(mapPaymentReceipt),
|
|
1919
|
+
total: typeof raw.total === "number" ? raw.total : null,
|
|
1920
|
+
hasMore: typeof raw.has_more === "boolean" ? raw.has_more : null,
|
|
1921
|
+
nextCursor: typeof raw.next_cursor === "string" ? raw.next_cursor : null
|
|
1922
|
+
};
|
|
1842
1923
|
}
|
|
1843
1924
|
async getReceipt(paymentId) {
|
|
1844
1925
|
const { receipt } = await this.transport.get(`/payments/${paymentId}/receipt`);
|
|
@@ -1850,10 +1931,10 @@ var AccountReads = class {
|
|
|
1850
1931
|
id: raw.id,
|
|
1851
1932
|
name: raw.name,
|
|
1852
1933
|
status: raw.status,
|
|
1853
|
-
//
|
|
1854
|
-
//
|
|
1855
|
-
//
|
|
1856
|
-
|
|
1934
|
+
// See the comment on `getAllowances` above: `account_address` is
|
|
1935
|
+
// required on the wire contract, so an omission here is off-contract
|
|
1936
|
+
// and comes through as `undefined` rather than a fabricated `''`.
|
|
1937
|
+
accountAddress: raw.account_address,
|
|
1857
1938
|
delegateAddress: raw.delegate_address,
|
|
1858
1939
|
chainId: raw.chain_id,
|
|
1859
1940
|
executionRail: raw.execution_rail === "delegation" ? "delegation" : "legacy"
|
|
@@ -2010,8 +2091,11 @@ function requestInitFromSnapshot(request) {
|
|
|
2010
2091
|
}
|
|
2011
2092
|
function noCompatiblePaymentOptionError(accepts) {
|
|
2012
2093
|
const erc7710Only = selectErc7710PaymentOption(accepts) !== null;
|
|
2094
|
+
const unsupportedOnly = !erc7710Only && accepts.some(
|
|
2095
|
+
(opt) => opt !== null && typeof opt === "object" && !isErc7710Option(opt) && (x402AssetTransferMethod(opt) !== null || opt.extra?.paymentFlow !== void 0)
|
|
2096
|
+
);
|
|
2013
2097
|
return new HavenApiError(
|
|
2014
|
-
"No compatible payment option found in x402 requirements. Haven supports standard x402 exact payments on Base USDC." + (erc7710Only ? " The only Haven-compatible option this merchant advertises is tagged extra.assetTransferMethod: 'erc7710' (direct settlement), which this EIP-3009 payment path cannot settle \u2014 the limitation is the settlement scheme, not the asset. Paying this merchant requires a delegation-rail erc7710 flow (settleX402Erc7710, or the hosted MCP purchase tools)." : ""),
|
|
2098
|
+
"No compatible payment option found in x402 requirements. Haven supports standard x402 exact payments on Base USDC." + (erc7710Only ? " The only Haven-compatible option this merchant advertises is tagged extra.assetTransferMethod: 'erc7710' (direct settlement), which this EIP-3009 payment path cannot settle \u2014 the limitation is the settlement scheme, not the asset. Paying this merchant requires a delegation-rail erc7710 flow (settleX402Erc7710, or the hosted MCP purchase tools)." : unsupportedOnly ? " Every option this merchant advertises asks for a transfer method or payment flow Haven cannot construct (for example extra.assetTransferMethod: 'permit2', or an unrecognized extra.paymentFlow). No payment intent was created and no funds moved." : ""),
|
|
2015
2099
|
400
|
|
2016
2100
|
);
|
|
2017
2101
|
}
|
|
@@ -2031,6 +2115,11 @@ function buildX402Quote(paymentRequired, request, idempotencyKey, mcpTransport)
|
|
|
2031
2115
|
request,
|
|
2032
2116
|
...mcpTransport ? { mcpTransport } : {},
|
|
2033
2117
|
resourceUrl: paymentRequired.resource.url,
|
|
2118
|
+
// #3097: the merchant's declaration vs the URL the caller quoted. The
|
|
2119
|
+
// paid retry goes to the caller's URL; a quote that says the two disagree
|
|
2120
|
+
// is how an agent sees a challenge that downgrades the scheme or moves
|
|
2121
|
+
// the host before it pays.
|
|
2122
|
+
resourceUrlDiffersFromRequest: paymentRequired.resource.url !== request.url,
|
|
2034
2123
|
description: paymentRequired.resource.description ?? option.description ?? null,
|
|
2035
2124
|
mimeType: paymentRequired.resource.mimeType ?? option.mimeType ?? null,
|
|
2036
2125
|
amountAtomic: x402AuthorizationAmount(option),
|
|
@@ -2185,6 +2274,13 @@ function assertCanResumeX402(status, paymentRequired, option) {
|
|
|
2185
2274
|
);
|
|
2186
2275
|
}
|
|
2187
2276
|
}
|
|
2277
|
+
|
|
2278
|
+
// src/account-naming.ts
|
|
2279
|
+
function readX402ReceiptPayer(raw) {
|
|
2280
|
+
return raw.payer ?? raw.account_address ?? raw.sign_data?.components?.payer_account;
|
|
2281
|
+
}
|
|
2282
|
+
|
|
2283
|
+
// src/x402-funding-leg.ts
|
|
2188
2284
|
var X402FundingLeg = class {
|
|
2189
2285
|
delegateKey;
|
|
2190
2286
|
delegateAddress;
|
|
@@ -3097,7 +3193,18 @@ function mapCatalogEntry(entry) {
|
|
|
3097
3193
|
verifiedAt: entry.verified_at,
|
|
3098
3194
|
source: entry.source,
|
|
3099
3195
|
domainVerified: entry.domain_verified,
|
|
3100
|
-
verifiedPayable: entry.verified_payable
|
|
3196
|
+
verifiedPayable: entry.verified_payable,
|
|
3197
|
+
// #3078: absent (older backend) or null (unresolved join) both mean "no
|
|
3198
|
+
// merchant known" — the public field is then absent, never null.
|
|
3199
|
+
...entry.merchant ? {
|
|
3200
|
+
merchant: {
|
|
3201
|
+
id: entry.merchant.id,
|
|
3202
|
+
slug: entry.merchant.slug,
|
|
3203
|
+
name: entry.merchant.name,
|
|
3204
|
+
listingStatus: entry.merchant.listing_status,
|
|
3205
|
+
isTestMerchant: entry.merchant.is_test_merchant
|
|
3206
|
+
}
|
|
3207
|
+
} : {}
|
|
3101
3208
|
};
|
|
3102
3209
|
}
|
|
3103
3210
|
var HavenClient = class {
|
|
@@ -3479,6 +3586,49 @@ var HavenClient = class {
|
|
|
3479
3586
|
async getAllowances() {
|
|
3480
3587
|
return this.accountReads.getAllowances();
|
|
3481
3588
|
}
|
|
3589
|
+
/**
|
|
3590
|
+
* #3126 — is the checked amount of the token actually HELD on the
|
|
3591
|
+
* agent's own account?
|
|
3592
|
+
*
|
|
3593
|
+
* This is the companion to {@link getAllowances}, not a variant of it:
|
|
3594
|
+
* allowances answer what the agent is PERMITTED to spend this period;
|
|
3595
|
+
* this answers whether the account HOLDS funds behind that permission,
|
|
3596
|
+
* as a sufficiency signal — `covered: true | false | null` — never as a
|
|
3597
|
+
* balance. `covered: null` means the chain read failed: treat it as
|
|
3598
|
+
* unverifiable, not as absence (`coverageError` says why). The account's
|
|
3599
|
+
* balance itself is deliberately not returned.
|
|
3600
|
+
*/
|
|
3601
|
+
async checkFunds(input) {
|
|
3602
|
+
return this.accountReads.checkFunds(input);
|
|
3603
|
+
}
|
|
3604
|
+
/**
|
|
3605
|
+
* `POST /machine-payments/budget-precheck` (#3054): ask Haven to decide —
|
|
3606
|
+
* server-side — whether `amountAtomic` of `token` fits the agent's
|
|
3607
|
+
* remaining delegation budget, the same compare the guided prepare used to
|
|
3608
|
+
* run locally over its allowances read.
|
|
3609
|
+
*
|
|
3610
|
+
* On insufficiency Haven refuses (403, `delegation_budget_exceeded`) and
|
|
3611
|
+
* the refusal reaches the `payment_refusals` ledger with
|
|
3612
|
+
* `source: 'hosted_prepare'` — the point of the endpoint. This method
|
|
3613
|
+
* surfaces that decision as a thrown {@link HavenApiError}; it does NOT
|
|
3614
|
+
* swallow it, because swallowing would turn a decided refusal into the
|
|
3615
|
+
* degrade-to-warning path and the ledger row would still land while the
|
|
3616
|
+
* purchase proceeded.
|
|
3617
|
+
*
|
|
3618
|
+
* camelCase body like the route family; the response mirrors the wire
|
|
3619
|
+
* (`sufficient`, `remaining_atomic`). `resourceUrl` is the merchant
|
|
3620
|
+
* resource being bought — the ledger dedupe window's discriminating
|
|
3621
|
+
* column — never this request's own URL.
|
|
3622
|
+
*/
|
|
3623
|
+
async precheckBudget(input) {
|
|
3624
|
+
return this.post("/machine-payments/budget-precheck", {
|
|
3625
|
+
chainId: input.chainId,
|
|
3626
|
+
token: input.token,
|
|
3627
|
+
amountAtomic: input.amountAtomic,
|
|
3628
|
+
...input.merchantTo !== void 0 ? { merchantTo: input.merchantTo } : {},
|
|
3629
|
+
...input.resourceUrl !== void 0 ? { resourceUrl: input.resourceUrl } : {}
|
|
3630
|
+
});
|
|
3631
|
+
}
|
|
3482
3632
|
/**
|
|
3483
3633
|
* Post-purchase allowance/budget summary for a settled payment (#1310).
|
|
3484
3634
|
*
|
|
@@ -3601,6 +3751,10 @@ var HavenClient = class {
|
|
|
3601
3751
|
async listReceipts(options = {}) {
|
|
3602
3752
|
return this.accountReads.listReceipts(options);
|
|
3603
3753
|
}
|
|
3754
|
+
/** #3128: one page of receipts with `total`, `hasMore` and `nextCursor`. */
|
|
3755
|
+
async listReceiptsPage(options = {}) {
|
|
3756
|
+
return this.accountReads.listReceiptsPage(options);
|
|
3757
|
+
}
|
|
3604
3758
|
/**
|
|
3605
3759
|
* Fetch the verifiable receipt bundle for a settled payment and verify it
|
|
3606
3760
|
* locally. The server's own verification is ignored — the receipt is verified
|
|
@@ -4210,13 +4364,13 @@ var toolDescriptions = {
|
|
|
4210
4364
|
summary: "Pay an inspected x402 quote. The delegate key signs locally; Haven only validates and relays signed, on-chain-constrained payment transactions.",
|
|
4211
4365
|
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.",
|
|
4212
4366
|
behavior: "Signs the payment locally and returns the merchant response. Settlement is either direct account-to-merchant with no funding leg, or a bridge that first redeems the agent's budget delegation to fund the delegate wallet for an EIP-3009 authorization. A payment outside the on-chain budget is declined before any money moves; nothing is queued for a human to approve later.",
|
|
4213
|
-
nextActionGuidance: "Preserve the returned resume_state \u2014 it identifies this payment if you need to ask about it later. This tool performs the merchant retry itself, so do not wait on a signal while the call is in flight. If the process crashes after this call and a later haven_get_payment_status reports nextAction=retry_original_x402_request, Haven's funding leg confirmed but no merchant response was ever recorded \u2014 call the resume tool with the preserved resume_state or payment_id instead of paying again. If the response carries phase=insufficient_funds and nextAction=
|
|
4367
|
+
nextActionGuidance: "Preserve the returned resume_state \u2014 it identifies this payment if you need to ask about it later. This tool performs the merchant retry itself, so do not wait on a signal while the call is in flight. If the process crashes after this call and a later haven_get_payment_status reports nextAction=retry_original_x402_request, Haven's funding leg confirmed but no merchant response was ever recorded \u2014 call the resume tool with the preserved resume_state or payment_id instead of paying again. If the response carries phase=insufficient_funds and nextAction=fund_account_or_raise_allowance, the payment cannot be retried until the account is funded or the agent budget raised \u2014 stop and tell the user the shortfall reported on the response."
|
|
4214
4368
|
},
|
|
4215
4369
|
payX402OneShot: {
|
|
4216
4370
|
summary: "Fetch an x402 paid HTTP resource in a single call. Handles the full probe -> pay -> retry round trip and returns the merchant response.",
|
|
4217
4371
|
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.",
|
|
4218
4372
|
behavior: "Calls the URL, parses any HTTP 402 x402 challenge, signs the payment locally, then retries the original request with the signed payment header (sent under PAYMENT-SIGNATURE, plus the legacy X-PAYMENT on the EIP-3009 path only) and returns the merchant response. Settlement is either direct account-to-merchant with no funding leg, or a bridge that first redeems the agent's budget delegation to fund the delegate wallet for an EIP-3009 authorization. A payment outside the on-chain budget is declined before any money moves; nothing is queued for a human to approve later. If the resource returns a non-402 status, returns it unchanged without contacting Haven.",
|
|
4219
|
-
nextActionGuidance: "Preserve the returned resume_state or paymentId \u2014 either identifies this payment if you need to ask about it later. This tool performs the merchant retry itself, so do not wait on a signal while the call is in flight. If the process crashes after this call and a later haven_get_payment_status reports nextAction=retry_original_x402_request, Haven's funding leg confirmed but no merchant response was ever recorded \u2014 call the resume tool with the preserved resume_state or payment_id instead of paying again. If the response carries phase=insufficient_funds and nextAction=
|
|
4373
|
+
nextActionGuidance: "Preserve the returned resume_state or paymentId \u2014 either identifies this payment if you need to ask about it later. This tool performs the merchant retry itself, so do not wait on a signal while the call is in flight. If the process crashes after this call and a later haven_get_payment_status reports nextAction=retry_original_x402_request, Haven's funding leg confirmed but no merchant response was ever recorded \u2014 call the resume tool with the preserved resume_state or payment_id instead of paying again. If the response carries phase=insufficient_funds and nextAction=fund_account_or_raise_allowance, the payment cannot be retried until the account is funded or the agent budget raised \u2014 stop and tell the user the shortfall reported on the response."
|
|
4220
4374
|
},
|
|
4221
4375
|
resumeX402: {
|
|
4222
4376
|
summary: "Resume an x402 payment whose Haven-side authorization already succeeded but whose merchant retry did not complete.",
|
|
@@ -4238,21 +4392,29 @@ var toolDescriptions = {
|
|
|
4238
4392
|
nextActionGuidance: ""
|
|
4239
4393
|
},
|
|
4240
4394
|
getAgent: {
|
|
4241
|
-
summary: "Return the authenticated agent identity AND its live spend authority in one call: Haven wallet, delegate, chain, raw status, spend_authority_readiness,
|
|
4242
|
-
selectionGuidance: "Use this as the
|
|
4243
|
-
behavior:
|
|
4395
|
+
summary: "Return the authenticated agent identity AND its live spend authority in one call: Haven wallet, delegate, chain, raw status, spend_authority_readiness, per-token remaining allowance (atomic + human-readable). The recommended first call in a new session.",
|
|
4396
|
+
selectionGuidance: "Use this as the session bootstrap, or to confirm identity together with whether the agent can spend right now. For per-token detail (configured vs spent vs reset window) use haven_get_allowances.",
|
|
4397
|
+
behavior: 'Reads identity plus the live spend-authority snapshot \u2014 the active on-chain budget delegation. spend_authority_readiness (readiness is a deprecated alias, same value) is "ready" when at least one token has remaining spend authority, "needs_approval" when the agent is active but has none, and "revoked" when the credential is not active. It covers hosted identity + on-chain spend authority ONLY \u2014 the hosted server cannot see the LOCAL signer, so "ready" does not mean the signer can start; verify the signer with a signer tool call or connect --doctor. An over-budget payment is declined before any money moves; there is no approval queue \u2014 ask the owner to grant or raise the budget in Haven. allowances[] carries id, tokenAddress, remainingAtomic, remainingDisplay per token. Identity fields: id, name, status, accountAddress, delegateAddress, chainId.',
|
|
4244
4398
|
nextActionGuidance: ""
|
|
4245
4399
|
},
|
|
4246
4400
|
getAllowances: {
|
|
4247
4401
|
summary: "Return configured and on-chain allowance state for the authenticated agent. On-chain allowance is the real spend gate.",
|
|
4248
|
-
selectionGuidance: "Use this when the user asks about allowance, budget, spend limit, remaining amount, remaining allowance, remaining budget, daily limit, reset period, what can I spend, or what the agent can still spend.",
|
|
4249
|
-
behavior: "Returns the per-token spend authority for the account: the active budget delegation (remaining = the period budget, which re-arms natively at the period boundary). An over-budget payment is declined before any money moves; nothing queues. Configured amounts from Haven are returned alongside.",
|
|
4402
|
+
selectionGuidance: "Use this when the user asks about allowance, budget, spend limit, remaining amount, remaining allowance, remaining budget, daily limit, reset period, what can I spend, or what the agent can still spend. For whether the account actually HOLDS funds behind the budget use haven_check_funds.",
|
|
4403
|
+
behavior: "Returns the per-token spend authority for the account: the active budget delegation (remaining = the period budget, which re-arms natively at the period boundary), each with id, onchain.remaining, remainingDisplay. An over-budget payment is declined before any money moves; nothing queues. Configured amounts from Haven are returned alongside.",
|
|
4250
4404
|
nextActionGuidance: ""
|
|
4251
4405
|
},
|
|
4406
|
+
// #3126 — the sufficiency signal, deliberately NOT a balance tool. The
|
|
4407
|
+
// constrained actor reads a boolean, never the treasury total.
|
|
4408
|
+
checkFunds: {
|
|
4409
|
+
summary: "Check whether the agent's account actually holds at least the given amount of a token \u2014 funds held, not spend permitted.",
|
|
4410
|
+
selectionGuidance: "Use this before attempting a payment when it matters whether the money is really there: allowance answers say what you are PERMITTED to spend, never whether the account HOLDS it. For allowance, budget, spend-limit, remaining-budget, reset-period, or what-can-I-spend questions use the allowance lookup tool instead.",
|
|
4411
|
+
behavior: "Returns covered: true (the account holds at least the checked amount), false (a live chain read reports less \u2014 the budget is backed by an empty account; stop and tell the user funds are missing), or null (the chain read failed \u2014 unverifiable, never treat it as absence; coverageError says why). The account balance itself is deliberately not returned: this is a sufficiency signal, not a balance read. budget_remaining_atomic is the permitted figure from the allowance lookup (the SDK spells it budgetRemainingAtomic), named so it can never be confused with holdings.",
|
|
4412
|
+
nextActionGuidance: "On covered=false, do not attempt the payment \u2014 tell the user the account is short and let them fund it; on covered=null, retry the check shortly or proceed knowing the payment may fail on-chain."
|
|
4413
|
+
},
|
|
4252
4414
|
listReceipts: {
|
|
4253
|
-
summary: "List
|
|
4254
|
-
selectionGuidance: "
|
|
4255
|
-
behavior: "
|
|
4415
|
+
summary: "List machine-payment receipts, newest first, by page.",
|
|
4416
|
+
selectionGuidance: "For transaction history or payment evidence; use the allowance tool instead for remaining allowance or what-can-I-spend questions.",
|
|
4417
|
+
behavior: "Page: { receipts, total, hasMore, nextCursor }; total 0 = none exist (no indexing delay); hasMore = cut at limit, send nextCursor as cursor. parties.treasuryAccount is Haven's authoritative payer. protocolReceiptPayload is the merchant's PAYMENT-RESPONSE, relayed verbatim: merchant-controlled, unverified, not Haven's record; payer may differ from payerAddress. Proof header values are omitted.",
|
|
4256
4418
|
nextActionGuidance: ""
|
|
4257
4419
|
},
|
|
4258
4420
|
verifyReceipt: {
|
|
@@ -4268,10 +4430,10 @@ var toolDescriptions = {
|
|
|
4268
4430
|
nextActionGuidance: "On a decline, report the reason to the user and ask them to raise the budget in Haven \u2014 there is no approval queue to wait on. This tool retries the merchant itself while it runs, so do not wait on a signal mid-call. If the process crashes after payment, a later haven_get_payment_status call may report nextAction=retry_original_x402_request \u2014 resume via haven_resume_x402_payment instead of paying again."
|
|
4269
4431
|
},
|
|
4270
4432
|
discoverTools: {
|
|
4271
|
-
summary: "Step 1 of a purchase: discover payable services from Haven's curated merchant catalog \u2014 names, prices,
|
|
4272
|
-
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
|
|
4273
|
-
behavior: "Use each entry's suggested_tool
|
|
4274
|
-
nextActionGuidance: `
|
|
4433
|
+
summary: "Step 1 of a purchase: discover payable services from Haven's curated merchant catalog \u2014 names, prices, the next call.",
|
|
4434
|
+
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 next tool to call.",
|
|
4435
|
+
behavior: "Use each entry's suggested_tool + suggested_arguments first; an entry without them says why. 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, the hint, 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.",
|
|
4436
|
+
nextActionGuidance: `Call suggested_tool with suggested_arguments VERBATIM (no hint: read suggested_tool_omitted_reason). Confirm the price from the live quote or pay result, not the catalog; if the next tool takes a cap, pass the user's cap as max_amount_human in whole tokens ("no more than 1 USDC" \u2192 max_amount_human: "1"), never atomic units by hand.`
|
|
4275
4437
|
},
|
|
4276
4438
|
submitCatalogEntry: {
|
|
4277
4439
|
summary: "Submit a merchant's payable (x402/MCP) endpoint to Haven's Verified Payable Directory for verification and listing.",
|
|
@@ -4281,13 +4443,13 @@ var toolDescriptions = {
|
|
|
4281
4443
|
},
|
|
4282
4444
|
sweep_delegate: {
|
|
4283
4445
|
summary: "Sweep stranded USDC and/or ETH from the delegate wallet back to the originating account.",
|
|
4284
|
-
selectionGuidance: "Use this when the user instructs you to recover stranded funds on the delegate wallet, or when a payment status returns nextAction=sweep_stranded_funds. Do NOT use for normal payments \u2014 use haven_pay_x402. Do NOT use to read balances only \u2014 use haven_get_allowances.",
|
|
4446
|
+
selectionGuidance: "Use this when the user instructs you to recover stranded funds on the delegate wallet, or when a payment status returns nextAction=sweep_stranded_funds. Do NOT use for normal payments \u2014 use haven_pay_x402. Do NOT use to read balances only \u2014 use haven_get_allowances, or haven_check_funds for whether the account holds funds.",
|
|
4285
4447
|
behavior: `Reads the delegate EOA's on-chain USDC and ETH balances. For each non-zero balance, signs and submits a transfer from the delegate EOA to the originating account (hardcoded destination). The delegate key signs locally \u2014 Haven never sees it and the backend never constructs signed transactions (CASP/MiCA Red Line #2). Returns tx hashes and recovered amounts. Returns an empty transfers list when nothing is stranded. Each transfer carries confirmation: "confirmed" (a receipt was seen \u2014 the funds are in the account) or "unconfirmed" (broadcast but not confirmed within 90 seconds \u2014 still in the mempool, may still land). The top-level unconfirmed flag is true when any transfer is unconfirmed.`,
|
|
4286
4448
|
nextActionGuidance: 'If transfers is non-empty, confirm the amounts with the user. Report a transfer as recovered ONLY when its confirmation is "confirmed". For an "unconfirmed" transfer, tell the user it was submitted but not yet confirmed, give them its txHash and explorerUrl to check, and do not re-run the sweep immediately \u2014 a re-run after it lands will simply find nothing stranded.'
|
|
4287
4449
|
},
|
|
4288
4450
|
send: {
|
|
4289
4451
|
summary: "Send ETH or USDC directly from the agent's Haven wallet to a recipient address.",
|
|
4290
|
-
selectionGuidance: "Use this for plain transfers \u2014 refunding a user, paying a freelancer, topping up a co-agent's wallet, or moving funds between addresses. Do NOT use for x402 paid endpoints \u2014 use haven_pay_x402 instead. Do NOT use for read-only allowance, budget, or what-can-I-spend questions \u2014 use haven_get_allowances.",
|
|
4452
|
+
selectionGuidance: "Use this for plain transfers \u2014 refunding a user, paying a freelancer, topping up a co-agent's wallet, or moving funds between addresses. Do NOT use for x402 paid endpoints \u2014 use haven_pay_x402 instead. Do NOT use for read-only allowance, budget, or what-can-I-spend questions \u2014 use haven_get_allowances. Do NOT use to check whether funds are held before sending \u2014 use haven_check_funds.",
|
|
4291
4453
|
behavior: "Sends the requested amount by redeeming the agent's on-chain budget delegation, account to recipient with no funding leg. Budget, recipient and expiry are enforced on-chain while the transfer is prepared, so a request outside them is declined before any money moves and before the agent is asked to sign \u2014 it is never queued for a human to approve later. The agent's signing key signs the account's typed data; Haven never receives the key.",
|
|
4292
4454
|
nextActionGuidance: "On a decline, report the reason to the user and ask them to grant or raise the budget in Haven \u2014 there is nothing to poll and no approval will arrive. After a successful send, poll haven_get_payment_status until nextAction=none."
|
|
4293
4455
|
},
|
|
@@ -4666,8 +4828,10 @@ the \`mcp__haven-signer__\` namespace and keep the delegate key on this machine.
|
|
|
4666
4828
|
That namespacing is Claude-family; other runtimes name the servers by their
|
|
4667
4829
|
own config keys (Codex: \`haven\`, \`haven_signer\`). Tool results carry the
|
|
4668
4830
|
exact next step (\`next_action\`, \`next_tool\`, \`next_arguments\`, plus the
|
|
4669
|
-
runtime-neutral \`next_tool_server\` + \`next_tool_name\`
|
|
4670
|
-
on that logical server, whatever your runtime calls it).
|
|
4831
|
+
runtime-neutral \`next_tool_server\` + \`next_tool_name\` + \`next_tool_server_role\`
|
|
4832
|
+
\u2014 the bare tool name on that logical server, whatever your runtime calls it).
|
|
4833
|
+
When no tool follows, \`next_tool\` is absent and \`next_tool_omitted_reason\`
|
|
4834
|
+
says why; that is a complete answer.
|
|
4671
4835
|
Follow those fields first; the prose below is fallback and orientation, not
|
|
4672
4836
|
the source of truth.
|
|
4673
4837
|
|
|
@@ -4754,6 +4918,13 @@ spending:
|
|
|
4754
4918
|
local signer; the signer is verified by calling any signer tool.
|
|
4755
4919
|
- \`mcp__haven__haven_get_allowances\` \u2014 detailed per-token breakdown
|
|
4756
4920
|
(configured, spent, reset window) when you need more than the summary.
|
|
4921
|
+
- \`mcp__haven__haven_check_funds\` \u2014 whether the account actually HOLDS at
|
|
4922
|
+
least a given amount of a token. Allowance answers above say what you are
|
|
4923
|
+
permitted to spend; this one says whether the money is really there,
|
|
4924
|
+
answered as \`covered\` true/false/null \u2014 never as a balance. On
|
|
4925
|
+
\`covered: false\`, stop and tell the user the account is short; on
|
|
4926
|
+
\`covered: null\` (the chain read failed), treat it as unverifiable rather
|
|
4927
|
+
than as absence.
|
|
4757
4928
|
|
|
4758
4929
|
Budgets reset on a period the user chose. If a payment exceeds the remaining
|
|
4759
4930
|
budget it is declined before any money moves \u2014 tell the user; they can raise
|
|
@@ -4882,8 +5053,10 @@ check on in-flight payments. Do not poll in a tight loop.
|
|
|
4882
5053
|
## Failure handling
|
|
4883
5054
|
|
|
4884
5055
|
Haven tool failures are shaped like \`{ success: false, code, message, ... }\`
|
|
4885
|
-
or older \`{ error, status, details? }\` responses.
|
|
4886
|
-
|
|
5056
|
+
or older \`{ error, status, details? }\` responses. A failure carries the same
|
|
5057
|
+
\`next_action\` / \`next_tool\` / \`next_arguments\` / \`next_tool_omitted_reason\`
|
|
5058
|
+
fields a success does; follow them first, then branch on \`code\` and surface
|
|
5059
|
+
\`message\` or \`error\` verbatim. Common cases:
|
|
4887
5060
|
|
|
4888
5061
|
- \`insufficient_funds\`: the Haven wallet doesn't hold enough of that token.
|
|
4889
5062
|
Suggest the user add funds in the Haven dashboard.
|
|
@@ -4944,6 +5117,64 @@ for that credential.
|
|
|
4944
5117
|
var SKILL_FOLDER_NAME = "haven-pay";
|
|
4945
5118
|
var HAVEN_SKILL_BODY_MD = HAVEN_SKILL_MD.replace(/^---\n[\s\S]*?\n---\n+/, "");
|
|
4946
5119
|
|
|
5120
|
+
// src/next-step.ts
|
|
5121
|
+
var NEXT_TOOL_SERVER_NAMES = {
|
|
5122
|
+
hosted: "haven",
|
|
5123
|
+
signer: "haven-signer"
|
|
5124
|
+
};
|
|
5125
|
+
var NEXT_TOOL_SERVER_ROLES = {
|
|
5126
|
+
haven: "hosted",
|
|
5127
|
+
"haven-signer": "signer"
|
|
5128
|
+
};
|
|
5129
|
+
function renderNextTool(role, name) {
|
|
5130
|
+
return `mcp__${NEXT_TOOL_SERVER_NAMES[role]}__${name}`;
|
|
5131
|
+
}
|
|
5132
|
+
function parseNextTool(literal) {
|
|
5133
|
+
const m = /^mcp__([a-z0-9-]+)__([a-z0-9_]+)$/.exec(literal);
|
|
5134
|
+
if (!m) return null;
|
|
5135
|
+
const role = NEXT_TOOL_SERVER_ROLES[m[1]];
|
|
5136
|
+
return { server: m[1], name: m[2], ...role ? { role } : {} };
|
|
5137
|
+
}
|
|
5138
|
+
var DEFAULT_NEXT_TOOL_BY_ACTION = {
|
|
5139
|
+
check_status_later: "haven_get_payment_status",
|
|
5140
|
+
sweep_stranded_funds: "haven_sweep_delegate"
|
|
5141
|
+
// `retry_original_x402_request` is NOT here: its only live emitter
|
|
5142
|
+
// (state-direct-recovery.ts, erc7710) names no tool on purpose — the retry
|
|
5143
|
+
// is the agent's own HTTP call — so a default would contradict the site.
|
|
5144
|
+
};
|
|
5145
|
+
function defaultNextToolFor(action) {
|
|
5146
|
+
return DEFAULT_NEXT_TOOL_BY_ACTION[action];
|
|
5147
|
+
}
|
|
5148
|
+
function createNextStepBuilder(targets) {
|
|
5149
|
+
return function nextStep(input) {
|
|
5150
|
+
const i = input;
|
|
5151
|
+
const base = {
|
|
5152
|
+
next_action: input.nextAction,
|
|
5153
|
+
safe_to_continue: input.safeToContinue,
|
|
5154
|
+
reason: input.reason
|
|
5155
|
+
};
|
|
5156
|
+
if (i.nextTool === null) {
|
|
5157
|
+
return { ...base, next_tool_omitted_reason: i.nextToolOmittedReason ?? "no next tool" };
|
|
5158
|
+
}
|
|
5159
|
+
const target = targets[i.nextTool];
|
|
5160
|
+
if (!target) {
|
|
5161
|
+
return { ...base, next_tool_omitted_reason: `${i.nextTool} is not a registered next-step target` };
|
|
5162
|
+
}
|
|
5163
|
+
const problem = target.validate(i.nextArguments);
|
|
5164
|
+
if (problem) {
|
|
5165
|
+
return { ...base, next_tool_omitted_reason: `next_arguments do not parse under ${i.nextTool}: ${problem}` };
|
|
5166
|
+
}
|
|
5167
|
+
return {
|
|
5168
|
+
...base,
|
|
5169
|
+
next_tool: renderNextTool(target.role, i.nextTool),
|
|
5170
|
+
next_tool_server: NEXT_TOOL_SERVER_NAMES[target.role],
|
|
5171
|
+
next_tool_name: i.nextTool,
|
|
5172
|
+
next_tool_server_role: target.role,
|
|
5173
|
+
next_arguments: i.nextArguments
|
|
5174
|
+
};
|
|
5175
|
+
};
|
|
5176
|
+
}
|
|
5177
|
+
|
|
4947
5178
|
// src/node-version.ts
|
|
4948
5179
|
var HAVEN_MINIMUM_NODE_VERSION = "22.0.0";
|
|
4949
5180
|
function parseNodeVersion(value) {
|
|
@@ -5027,6 +5258,6 @@ function sameUrl(a, b) {
|
|
|
5027
5258
|
}
|
|
5028
5259
|
}
|
|
5029
5260
|
|
|
5030
|
-
export { AGENT_APPROVAL_RELAY_JSON_SENTENCE, AGENT_APPROVAL_RELAY_PROSE_SENTENCE, AGENT_COMMAND_MODIFICATION_SENTENCE, AGENT_JSON_MODE_SENTENCE, AGENT_LOCAL_KEY_SENTENCE, AGENT_NETWORK_ACCESS_SENTENCE, AGENT_ONBOARDING_PROMPT, AGENT_PAYMENT_FAILURE_CODE_VALUES, AGENT_PAYMENT_NEXT_ACTION_VALUES, AGENT_PAYMENT_PHASE_VALUES, AGENT_PAYMENT_RAIL_VALUES, AGENT_README_SECTION_MD, AGENT_SECRET_HYGIENE_SENTENCE, AGENT_WIRING_COLLISION_RELAY_SENTENCE, AgentPaymentFailureCode, AgentPaymentFailureCodeDescriptions, AgentPaymentFailureCodeSchema, AgentPaymentNextAction,
|
|
5261
|
+
export { AGENT_APPROVAL_RELAY_JSON_SENTENCE, AGENT_APPROVAL_RELAY_PROSE_SENTENCE, AGENT_COMMAND_MODIFICATION_SENTENCE, AGENT_JSON_MODE_SENTENCE, AGENT_LOCAL_KEY_SENTENCE, AGENT_NETWORK_ACCESS_SENTENCE, AGENT_ONBOARDING_PROMPT, AGENT_PAYMENT_FAILURE_CODE_VALUES, AGENT_PAYMENT_NEXT_ACTION_VALUES, AGENT_PAYMENT_PHASE_VALUES, AGENT_PAYMENT_RAIL_VALUES, AGENT_README_SECTION_MD, AGENT_SECRET_HYGIENE_SENTENCE, AGENT_WIRING_COLLISION_RELAY_SENTENCE, AgentPaymentFailureCode, AgentPaymentFailureCodeDescriptions, AgentPaymentFailureCodeSchema, AgentPaymentNextAction, AgentPaymentNextActionDescriptions, AgentPaymentNextActionSchema, AgentPaymentPhase, AgentPaymentPhaseDescriptions, AgentPaymentPhaseSchema, AgentPaymentRail, AgentPaymentRailDescriptions, AgentPaymentRailSchema, AgentPaymentWarningCode, CONNECTOR_PACKAGE_NAME, DEFAULT_CONFIRMATION_TIMEOUT_MS, DEFAULT_NEXT_TOOL_BY_ACTION, DISCOVERY_MAX_BYTES, ERC7710_ASSET_TRANSFER_METHOD, HAVEN_AGENT_RUNBOOK_MD, HAVEN_CONNECTOR_CHANNEL, HAVEN_MINIMUM_NODE_VERSION, HAVEN_SKILL_BODY_MD, HAVEN_SKILL_MD, HavenApiError, HavenClient, HavenError, HavenInsecureRetryTargetError, HavenPaymentStateError, HavenSigningError, HavenTimeoutError, HavenUnsupportedSignerVersionError, HavenZeroSettlementHashError, INSECURE_RETRY_TARGET_CODE, MERCHANT_DISCOVERY_PATHS, MerchantTimeoutError, NEXT_TOOL_SERVER_NAMES, NEXT_TOOL_SERVER_ROLES, RECEIPT_VERSION, SIGNER_UPDATE_FALLBACK, SKILL_FOLDER_NAME, SWEEP_BASE_CHAIN_ID, SWEEP_BASE_SEPOLIA_CHAIN_ID, SWEEP_BASE_SEPOLIA_USDC_ADDRESS, SWEEP_BASE_USDC_ADDRESS, SignerRefusalCode, TRANSFER_WITH_AUTHORIZATION_TYPES, X402AlreadySettledError, X402PaymentHeaderValidationError, X402UnexpectedStatusError, X402_LEGACY_PAYMENT_HEADER_NAME, X402_MAX_AUTHORIZATION_WINDOW_SECONDS, X402_PAYMENT_HEADER_NAME, X402_PAYMENT_HEADER_NAMES_SENT, X402_PAYMENT_REQUIRED_HEADER_NAME, X402_PAYMENT_RESPONSE_HEADER_NAME, X402_SETTLEMENT_FORWARD_MARGIN_SECONDS, addressFromKey, assertSecureX402RetryTarget, buildSweepAuthorizationMessage, buildSweepTypedData, buildX402ExpectedMessage, compareNodeVersions, composeDescription, connectorRerunCommand, connectorSpec, createNextStepBuilder, decodeBase64Json, decodeBase64Utf8, defaultNextToolFor, discoverMerchantMcpUrl, encodeBase64Json, encodeBase64Utf8, encodePaymentProof, havenTools, isConnectorChannel, isErc7710Option, isSecureX402RetryTarget, isSupportedNodeVersion, isSweepableChain, isZeroSettlementTxHash, normalizePaymentRequired, parseNextTool, parsePaymentRequired, parsePaymentRequiredResponse, readX402ReceiptPayer, renderNextTool, resolveConnectorChannel, resolveTokenFromAddress, resolveX402RetryTarget, sameUrl, selectErc7710PaymentOption, selectPaymentOption, selectStandardPaymentOption, selectX402SettlementScheme, signHash, signUserOpTypedDataForDelegation, signerUpdateFallback, sweepUsdcAddress, sweepUsdcDomain, toStandardPaymentRequirements, toolDescriptions, unsupportedNodeVersionMessage, validateStandardX402PaymentHeader, verifyPaymentReceipt, verifySignature, x402AssetTransferMethod, x402AuthorizationAmount, x402FacilitatorAddresses, x402V2PaymentEnvelope };
|
|
5031
5262
|
//# sourceMappingURL=index.js.map
|
|
5032
5263
|
//# sourceMappingURL=index.js.map
|