@haven_ai/sdk 0.1.37-alpha.0 → 0.2.1-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 +13 -6
- package/dist/index.cjs +263 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +400 -74
- package/dist/index.d.ts +400 -74
- package/dist/index.js +255 -45
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -110,9 +110,14 @@ var AgentPaymentNextAction = {
|
|
|
110
110
|
*/
|
|
111
111
|
PaymentWindowExpired: "payment_window_expired",
|
|
112
112
|
/**
|
|
113
|
-
* Stop and tell the user that the originating
|
|
113
|
+
* Stop and tell the user that the originating account needs to be funded or
|
|
114
114
|
* the agent's per-token allowance needs to be raised before the payment
|
|
115
115
|
* can succeed. A user approval will not fix this state on its own.
|
|
116
|
+
*
|
|
117
|
+
* #2908: the wire twin `fund_account_or_raise_allowance`
|
|
118
|
+
* ({@link AgentPaymentNextActionAccountAlias}) means the same thing; the
|
|
119
|
+
* server keeps emitting THIS value until #2914. Compare via
|
|
120
|
+
* {@link canonicalAgentPaymentNextAction}.
|
|
116
121
|
*/
|
|
117
122
|
FundSafeOrRaiseAllowance: "fund_safe_or_raise_allowance",
|
|
118
123
|
/**
|
|
@@ -120,19 +125,53 @@ var AgentPaymentNextAction = {
|
|
|
120
125
|
* settled to the merchant. The wallet owner should initiate a sweep to
|
|
121
126
|
* return those funds to the originating Safe.
|
|
122
127
|
*/
|
|
123
|
-
SweepStrandedFunds: "sweep_stranded_funds"
|
|
128
|
+
SweepStrandedFunds: "sweep_stranded_funds",
|
|
129
|
+
/**
|
|
130
|
+
* #2970: a `submitted` erc7710 x402 intent whose settlement window has
|
|
131
|
+
* passed with no on-chain settlement evidence Haven could verify. Distinct
|
|
132
|
+
* from {@link CheckStatusLater}, which this REPLACES once the window is
|
|
133
|
+
* past — but it is not futile: Haven's settlement sweep (120s tick) scans
|
|
134
|
+
* each candidate over its own window plus a 120s clock-skew allowance, so
|
|
135
|
+
* it can still attribute the settlement for a short while after this value
|
|
136
|
+
* first appears. Poll {@link CheckStatusLater}'s tool
|
|
137
|
+
* (`haven_get_payment_status`) once more, roughly two minutes later; if it
|
|
138
|
+
* still shows no evidence, tell the user the goods were delivered but
|
|
139
|
+
* Haven holds no verified settlement evidence for this payment. If the
|
|
140
|
+
* agent holds the merchant's real settlement transaction hash (from
|
|
141
|
+
* `PAYMENT-RESPONSE`'s `transaction` field, or a prior settle/complete
|
|
142
|
+
* result's `settlement_tx_hash`), report it with the hosted
|
|
143
|
+
* `haven_report_settlement_evidence` tool instead of waiting —
|
|
144
|
+
* `haven_report_x402_outcome` takes no hash and refuses a non-`confirmed`
|
|
145
|
+
* intent.
|
|
146
|
+
*/
|
|
147
|
+
AwaitingSettlementEvidence: "awaiting_settlement_evidence"
|
|
124
148
|
};
|
|
149
|
+
var AgentPaymentNextActionAccountAlias = {
|
|
150
|
+
/** Account-vocabulary twin of `fund_safe_or_raise_allowance`; same meaning. */
|
|
151
|
+
FundAccountOrRaiseAllowance: "fund_account_or_raise_allowance"
|
|
152
|
+
};
|
|
153
|
+
function canonicalAgentPaymentNextAction(value) {
|
|
154
|
+
if (value === AgentPaymentNextActionAccountAlias.FundAccountOrRaiseAllowance) {
|
|
155
|
+
return AgentPaymentNextAction.FundSafeOrRaiseAllowance;
|
|
156
|
+
}
|
|
157
|
+
return value;
|
|
158
|
+
}
|
|
159
|
+
function isFundAccountOrRaiseAllowance(value) {
|
|
160
|
+
return value === AgentPaymentNextAction.FundSafeOrRaiseAllowance || value === AgentPaymentNextActionAccountAlias.FundAccountOrRaiseAllowance;
|
|
161
|
+
}
|
|
125
162
|
var AgentPaymentFailureCode = {
|
|
126
163
|
/** A merchant-authoritative x402 price exceeds the caller's pre-funding max_amount cap. */
|
|
127
164
|
PriceExceedsMax: "PRICE_EXCEEDS_MAX",
|
|
128
165
|
/** The x402 funding/quote window expired before the signer or hosted settle step could finish. */
|
|
129
166
|
PaymentWindowExpired: "PAYMENT_WINDOW_EXPIRED",
|
|
130
|
-
/** The
|
|
167
|
+
/** The merchant rejected the paid retry. On eip3009 the funding leg had succeeded (sweep);
|
|
168
|
+
* on erc7710 there is no funding leg — nothing to sweep, follow the message (#2983). */
|
|
131
169
|
MerchantRejectedAfterFunding: "MERCHANT_REJECTED_AFTER_FUNDING",
|
|
132
170
|
/** #1300 review: funding is on-chain but the merchant never ANSWERED the
|
|
133
171
|
* paid retry within the timeout. NOT proof of rejection — the merchant
|
|
134
|
-
*
|
|
135
|
-
*
|
|
172
|
+
* may still settle late, so the guidance is verify-then-act. On eip3009
|
|
173
|
+
* the funding leg had succeeded (verify-then-sweep); on erc7710 there is
|
|
174
|
+
* no funding leg — nothing to sweep, follow the message (#3000). */
|
|
136
175
|
MerchantUnresponsiveAfterFunding: "MERCHANT_UNRESPONSIVE_AFTER_FUNDING",
|
|
137
176
|
/**
|
|
138
177
|
* #1307: the caller omitted merchant_url/tool_name (asking Haven to
|
|
@@ -158,7 +197,17 @@ var AgentPaymentFailureCode = {
|
|
|
158
197
|
* asset can represent (truncating it would silently change the user's cap).
|
|
159
198
|
* The fallback is the exact atomic `max_amount`.
|
|
160
199
|
*/
|
|
161
|
-
MaxAmountUnconvertible: "MAX_AMOUNT_UNCONVERTIBLE"
|
|
200
|
+
MaxAmountUnconvertible: "MAX_AMOUNT_UNCONVERTIBLE",
|
|
201
|
+
/**
|
|
202
|
+
* #2979: the merchant answered a `tools/call` probe with its own
|
|
203
|
+
* machine-readable "cannot settle right now" refusal (HTTP 503,
|
|
204
|
+
* `{ error: 'merchant_not_ready', reason_code, ... }`) instead of a 402
|
|
205
|
+
* challenge — e.g. its settlement wallet is out of gas. No 402 was ever
|
|
206
|
+
* issued and no payment was created; this is honest and (per
|
|
207
|
+
* `retry_after_s`, when present) usually transient, unlike a permanent
|
|
208
|
+
* endpoint miss.
|
|
209
|
+
*/
|
|
210
|
+
MerchantNotReady: "MERCHANT_NOT_READY"
|
|
162
211
|
};
|
|
163
212
|
var AgentPaymentRail = {
|
|
164
213
|
/** Standard Haven payment from the user's Safe through an approved delegate allowance. */
|
|
@@ -206,16 +255,18 @@ var AgentPaymentNextActionDescriptions = {
|
|
|
206
255
|
[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.",
|
|
207
256
|
[AgentPaymentNextAction.FundSafeOrRaiseAllowance]: "Stop and tell the user that the account needs to be funded or the agent budget raised before the payment can succeed.",
|
|
208
257
|
[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.",
|
|
209
|
-
[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."
|
|
258
|
+
[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.",
|
|
259
|
+
[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."
|
|
210
260
|
};
|
|
211
261
|
var AgentPaymentFailureCodeDescriptions = {
|
|
212
262
|
[AgentPaymentFailureCode.PriceExceedsMax]: "The merchant-authoritative x402 amount exceeds the caller's max_amount cap. No funding transfer was created; ask the user before retrying with a larger cap.",
|
|
213
263
|
[AgentPaymentFailureCode.PaymentWindowExpired]: "The x402 funding/quote window expired before the signer or hosted settle step could finish. Re-quote via haven_pay_mcp_tool with the same idempotency key to avoid duplicate funding.",
|
|
214
|
-
[AgentPaymentFailureCode.MerchantRejectedAfterFunding]: "The
|
|
215
|
-
[AgentPaymentFailureCode.MerchantUnresponsiveAfterFunding]: "The
|
|
264
|
+
[AgentPaymentFailureCode.MerchantRejectedAfterFunding]: "The merchant rejected the paid retry. eip3009: the funding leg succeeded \u2014 stop retrying the merchant and reconcile stranded delegate funds with haven_sweep_delegate. erc7710: no funding leg, nothing to sweep \u2014 follow the message (re-quote later, or check haven_get_payment_status after the window first).",
|
|
265
|
+
[AgentPaymentFailureCode.MerchantUnresponsiveAfterFunding]: "The merchant did not answer the paid retry before the timeout. The merchant may still settle late. eip3009: the funding leg succeeded \u2014 check haven_get_payment_status, retry haven_complete_mcp_tool once, sweep only if no settlement appears. erc7710: no funding leg, nothing to sweep, and haven_complete_mcp_tool has no erc7710 branch \u2014 do not retry it; check haven_get_payment_status after the payment window and re-quote only if it shows no settlement.",
|
|
216
266
|
[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.",
|
|
217
267
|
[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.",
|
|
218
|
-
[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."
|
|
268
|
+
[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.",
|
|
269
|
+
[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.'
|
|
219
270
|
};
|
|
220
271
|
var AgentPaymentWarningCode = {
|
|
221
272
|
/** No max_amount cap was supplied — the live quoted price was accepted as-is. */
|
|
@@ -246,7 +297,24 @@ var AgentPaymentWarningCode = {
|
|
|
246
297
|
* on-chain policy re-checks at redemption either way; this only says the
|
|
247
298
|
* guidance shown here may be optimistic.
|
|
248
299
|
*/
|
|
249
|
-
AllowanceReadOptimistic: "ALLOWANCE_READ_OPTIMISTIC"
|
|
300
|
+
AllowanceReadOptimistic: "ALLOWANCE_READ_OPTIMISTIC",
|
|
301
|
+
/**
|
|
302
|
+
* #2991: the quote tools' `expected_settlement_scheme` prediction of what
|
|
303
|
+
* `haven_prepare_catalog_purchase` / `haven_pay_mcp_tool` will actually
|
|
304
|
+
* select could not be computed — the agent's execution rail could not be
|
|
305
|
+
* read from Haven, so `expected_settlement_scheme` is `null` rather than a
|
|
306
|
+
* guess. `accepted_scheme` (the merchant's offer) is unaffected.
|
|
307
|
+
*/
|
|
308
|
+
X402SchemeUnknown: "X402_SCHEME_UNKNOWN",
|
|
309
|
+
/**
|
|
310
|
+
* #2968: the merchant answered 200 and handed over goods, but Haven holds NO
|
|
311
|
+
* on-chain evidence that the payment moved. `settled: false` beside this code
|
|
312
|
+
* is not a failure — it is the absence of proof, and the two must travel
|
|
313
|
+
* together so an agent can tell "the user has the goods" apart from "the
|
|
314
|
+
* money moved". Carries the intent's `expires_at`: after that instant the
|
|
315
|
+
* settlement can no longer land at all.
|
|
316
|
+
*/
|
|
317
|
+
SettlementUnconfirmed: "SETTLEMENT_UNCONFIRMED"
|
|
250
318
|
};
|
|
251
319
|
var AgentPaymentRailDescriptions = {
|
|
252
320
|
[AgentPaymentRail.Direct]: "Standard Haven payment from the user-controlled account, redeeming the agent's on-chain budget delegation.",
|
|
@@ -310,8 +378,18 @@ var MerchantTimeoutError = class extends HavenApiError {
|
|
|
310
378
|
};
|
|
311
379
|
var X402UnexpectedStatusError = class extends HavenApiError {
|
|
312
380
|
x402ErrorCode = "unexpected_non_402_status";
|
|
313
|
-
|
|
314
|
-
|
|
381
|
+
/**
|
|
382
|
+
* #2979: `body` is the merchant's own JSON, when the non-402 response
|
|
383
|
+
* carried one — e.g. the demo merchant's `/mcp` readiness gate answers
|
|
384
|
+
* `503 { error: 'merchant_not_ready', reason_code, ... }`. Optional and
|
|
385
|
+
* best-effort: a non-JSON or unreadable body leaves this `undefined`, same
|
|
386
|
+
* as before this field existed. Consumers key on it (not on the message
|
|
387
|
+
* string) to distinguish an honest, machine-readable merchant refusal from
|
|
388
|
+
* a genuine "this is not the x402 endpoint" miss, which otherwise look
|
|
389
|
+
* identical — both are just "some non-402 status".
|
|
390
|
+
*/
|
|
391
|
+
constructor(message, statusCode, body) {
|
|
392
|
+
super(message, statusCode, body);
|
|
315
393
|
this.name = "X402UnexpectedStatusError";
|
|
316
394
|
}
|
|
317
395
|
};
|
|
@@ -350,6 +428,17 @@ var HavenSigningError = class extends HavenError {
|
|
|
350
428
|
this.name = "HavenSigningError";
|
|
351
429
|
}
|
|
352
430
|
};
|
|
431
|
+
var HavenZeroSettlementHashError = class extends HavenError {
|
|
432
|
+
constructor(paymentId) {
|
|
433
|
+
super(
|
|
434
|
+
"settlement_tx_hash is the zero hash (0x00\u202600), which is never a real settlement transaction \u2014 refused before any report was sent.",
|
|
435
|
+
"ZERO_SETTLEMENT_HASH",
|
|
436
|
+
400,
|
|
437
|
+
paymentId
|
|
438
|
+
);
|
|
439
|
+
this.name = "HavenZeroSettlementHashError";
|
|
440
|
+
}
|
|
441
|
+
};
|
|
353
442
|
var SignerRefusalCode = {
|
|
354
443
|
/** `SUPPORTED_X402_EXPECTED_VERSIONS` in `@haven_ai/signer` does not include the received version. */
|
|
355
444
|
UnsupportedExpectedContextVersion: "UNSUPPORTED_EXPECTED_CONTEXT_VERSION",
|
|
@@ -1098,6 +1187,15 @@ var HavenApiTransport = class {
|
|
|
1098
1187
|
};
|
|
1099
1188
|
|
|
1100
1189
|
// src/payment-mappers.ts
|
|
1190
|
+
function mapParties(raw) {
|
|
1191
|
+
if (!raw) return void 0;
|
|
1192
|
+
return {
|
|
1193
|
+
treasuryAccount: raw.treasury_account,
|
|
1194
|
+
delegate: raw.delegate,
|
|
1195
|
+
delegateAccount: raw.delegate_account,
|
|
1196
|
+
merchant: raw.merchant
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1101
1199
|
function mapPaymentResult(raw, buildExplorerUrl2) {
|
|
1102
1200
|
return {
|
|
1103
1201
|
paymentId: raw.payment_id,
|
|
@@ -1128,12 +1226,16 @@ function mapPaymentStatusResult(raw) {
|
|
|
1128
1226
|
rail: raw.rail,
|
|
1129
1227
|
status: raw.status,
|
|
1130
1228
|
phase: raw.phase,
|
|
1131
|
-
|
|
1229
|
+
// #2908: the account-vocabulary alias collapses onto the canonical value
|
|
1230
|
+
// so every `=== AgentPaymentNextAction.X` downstream keeps working when
|
|
1231
|
+
// the server flips its emit at #2914.
|
|
1232
|
+
nextAction: canonicalAgentPaymentNextAction(raw.next_action),
|
|
1132
1233
|
amount: raw.amount,
|
|
1133
1234
|
token: raw.token,
|
|
1134
1235
|
resourceUrl: raw.resource_url,
|
|
1135
1236
|
merchantAddress: raw.merchant_address,
|
|
1136
1237
|
payerAddress: raw.payer_address ?? null,
|
|
1238
|
+
parties: mapParties(raw.parties),
|
|
1137
1239
|
txHash: raw.tx_hash,
|
|
1138
1240
|
expiresAt: raw.expires_at,
|
|
1139
1241
|
chainId: raw.chain_id,
|
|
@@ -1167,10 +1269,13 @@ function mapPaymentReceipt(raw) {
|
|
|
1167
1269
|
rail: raw.rail,
|
|
1168
1270
|
proofStatus: raw.proof_status,
|
|
1169
1271
|
txHash: raw.tx_hash,
|
|
1272
|
+
fundingTxHash: raw.funding_tx_hash ?? null,
|
|
1273
|
+
settlementTxHash: raw.settlement_tx_hash ?? null,
|
|
1170
1274
|
chainId: raw.chain_id,
|
|
1171
1275
|
resourceUrl: raw.resource_url,
|
|
1172
1276
|
merchantAddress: raw.merchant_address,
|
|
1173
1277
|
payerAddress: raw.payer_address,
|
|
1278
|
+
parties: mapParties(raw.parties),
|
|
1174
1279
|
settlementAddress: raw.settlement_address,
|
|
1175
1280
|
tokenSymbol: raw.token_symbol,
|
|
1176
1281
|
tokenAddress: raw.token_address,
|
|
@@ -1260,7 +1365,7 @@ function messageForState(label, status, paymentId, nextAction) {
|
|
|
1260
1365
|
function paymentStateFromRaw(label, raw) {
|
|
1261
1366
|
if (!raw.payment_id || !raw.status) return null;
|
|
1262
1367
|
const phase = raw.phase ?? phaseForStatus(raw.status);
|
|
1263
|
-
const nextAction = raw.next_action ?? nextActionForStatus(raw.status);
|
|
1368
|
+
const nextAction = canonicalAgentPaymentNextAction(raw.next_action) ?? nextActionForStatus(raw.status);
|
|
1264
1369
|
if (!phase || !nextAction) return null;
|
|
1265
1370
|
const amount = raw.amount ?? raw.requested ?? "";
|
|
1266
1371
|
const token = raw.token ?? "";
|
|
@@ -1583,6 +1688,20 @@ function verifyPaymentReceipt(receipt, recover = defaultRecover) {
|
|
|
1583
1688
|
return { verified: true, recoveredSigner: recovered };
|
|
1584
1689
|
}
|
|
1585
1690
|
|
|
1691
|
+
// src/account-naming.ts
|
|
1692
|
+
function readAccountAddress(raw) {
|
|
1693
|
+
return raw.account_address ?? raw.safe_address ?? void 0;
|
|
1694
|
+
}
|
|
1695
|
+
function readAccountId(raw) {
|
|
1696
|
+
return raw.account_id ?? raw.safe_id ?? void 0;
|
|
1697
|
+
}
|
|
1698
|
+
function accountAddressTwins(address) {
|
|
1699
|
+
return { accountAddress: address, safeAddress: address };
|
|
1700
|
+
}
|
|
1701
|
+
function readX402ReceiptPayer(raw) {
|
|
1702
|
+
return raw.payer ?? raw.account_address ?? raw.sign_data?.components?.payer_account ?? raw.safe_address ?? raw.sign_data?.components?.safe;
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1586
1705
|
// src/account-reads.ts
|
|
1587
1706
|
function safeBigInt(value) {
|
|
1588
1707
|
try {
|
|
@@ -1641,7 +1760,10 @@ var AccountReads = class {
|
|
|
1641
1760
|
const raw = await this.transport.get("/machine-payments/allowances");
|
|
1642
1761
|
return {
|
|
1643
1762
|
agentId: raw.agent_id,
|
|
1644
|
-
|
|
1763
|
+
// #2908: one mapper, both names, same value — `readAccountAddress`
|
|
1764
|
+
// prefers the server's `account_address` twin and falls back to
|
|
1765
|
+
// `safe_address` for a pre-#2907 server.
|
|
1766
|
+
...accountAddressTwins(readAccountAddress(raw)),
|
|
1645
1767
|
delegateAddress: raw.delegate_address,
|
|
1646
1768
|
chainId: raw.chain_id,
|
|
1647
1769
|
allowances: raw.allowances.map((allowance) => ({
|
|
@@ -1730,7 +1852,10 @@ var AccountReads = class {
|
|
|
1730
1852
|
id: raw.id,
|
|
1731
1853
|
name: raw.name,
|
|
1732
1854
|
status: raw.status,
|
|
1733
|
-
|
|
1855
|
+
// #2908: both camelCase names off whichever snake_case name the server
|
|
1856
|
+
// sent (new first). The hosted MCP's `haven_get_agent` spreads this
|
|
1857
|
+
// object, so this is also the hosted output's dual-emit point.
|
|
1858
|
+
...accountAddressTwins(readAccountAddress(raw)),
|
|
1734
1859
|
delegateAddress: raw.delegate_address,
|
|
1735
1860
|
chainId: raw.chain_id,
|
|
1736
1861
|
executionRail: raw.execution_rail === "delegation" ? "delegation" : "legacy"
|
|
@@ -1780,7 +1905,7 @@ var DelegateSweepApi = class {
|
|
|
1780
1905
|
const contract = createErc20Contract(sweepUsdcAddress(agent.chainId), ["function balanceOf(address) view returns (uint256)", "function transfer(address to, uint256 amount) returns (bool)"], wallet);
|
|
1781
1906
|
const balance2 = await contract.balanceOf(agent.delegateAddress);
|
|
1782
1907
|
if (balance2 > 0n) {
|
|
1783
|
-
const tx = await contract.transfer(agent.
|
|
1908
|
+
const tx = await contract.transfer(agent.accountAddress, balance2);
|
|
1784
1909
|
const { txHash, confirmation } = await waitForSweepTx(tx);
|
|
1785
1910
|
transfers.push({ asset: "USDC", amount: format(balance2, 6), amountAtomic: balance2.toString(), txHash, explorerUrl: this.options.buildExplorerUrl(agent.chainId, txHash), confirmation });
|
|
1786
1911
|
}
|
|
@@ -1790,12 +1915,12 @@ var DelegateSweepApi = class {
|
|
|
1790
1915
|
const fee = await provider.getFeeData();
|
|
1791
1916
|
const send = balance - (fee.maxFeePerGas ?? fee.gasPrice ?? 1000000n) * 21000n * 2n;
|
|
1792
1917
|
if (send > 0n) {
|
|
1793
|
-
const tx = await wallet.sendTransaction({ to: agent.
|
|
1918
|
+
const tx = await wallet.sendTransaction({ to: agent.accountAddress, value: send });
|
|
1794
1919
|
const { txHash, confirmation } = await waitForSweepTx(tx);
|
|
1795
1920
|
transfers.push({ asset: "ETH", amount: format(send, 18), amountAtomic: send.toString(), txHash, explorerUrl: this.options.buildExplorerUrl(agent.chainId, txHash), confirmation });
|
|
1796
1921
|
}
|
|
1797
1922
|
}
|
|
1798
|
-
return { fromAddress: agent.delegateAddress, toAddress: agent.
|
|
1923
|
+
return { fromAddress: agent.delegateAddress, toAddress: agent.accountAddress, chainId: agent.chainId, transfers, unconfirmed: transfers.some((t) => t.confirmation === "unconfirmed") };
|
|
1799
1924
|
}
|
|
1800
1925
|
prepareSweep() {
|
|
1801
1926
|
return this.options.transport.post("/machine-payments/sweep/prepare", {});
|
|
@@ -2189,7 +2314,7 @@ var X402FundingLeg = class {
|
|
|
2189
2314
|
const to = execResult?.to ?? raw.to ?? this.delegateAddress ?? "";
|
|
2190
2315
|
const explorerUrl = execResult?.explorer_url ?? raw.explorer_url ?? explorerUrlOrEmpty(chainId, txHash);
|
|
2191
2316
|
const merchantTo = execResult?.merchant_to ?? raw.merchant_to ?? option.payTo;
|
|
2192
|
-
const payer = raw
|
|
2317
|
+
const payer = readX402ReceiptPayer(raw);
|
|
2193
2318
|
return buildX402Receipt({
|
|
2194
2319
|
paymentId: raw.payment_id,
|
|
2195
2320
|
txHash,
|
|
@@ -2874,15 +2999,57 @@ var MerchantCompletion = class {
|
|
|
2874
2999
|
for (let attempt = 0; ; attempt += 1) {
|
|
2875
3000
|
try {
|
|
2876
3001
|
await this.post("/machine-payments/evidence", body);
|
|
2877
|
-
return;
|
|
3002
|
+
return { outcome: "confirmed" };
|
|
2878
3003
|
} catch (err) {
|
|
2879
|
-
const
|
|
2880
|
-
|
|
3004
|
+
const statusCode = err instanceof HavenApiError ? err.statusCode : void 0;
|
|
3005
|
+
const retryable = statusCode === EVIDENCE_RETRYABLE_STATUS;
|
|
3006
|
+
if (!retryable) {
|
|
3007
|
+
return { outcome: "refused", statusCode: statusCode ?? 0 };
|
|
3008
|
+
}
|
|
3009
|
+
if (attempt >= EVIDENCE_RETRY_DELAYS_MS.length) {
|
|
3010
|
+
return { outcome: "retryable", statusCode };
|
|
3011
|
+
}
|
|
2881
3012
|
await this.sleep(EVIDENCE_RETRY_DELAYS_MS[attempt]);
|
|
2882
3013
|
}
|
|
2883
3014
|
}
|
|
2884
3015
|
}
|
|
3016
|
+
/**
|
|
3017
|
+
* #2972: report the merchant's REAL settlement transaction hash for an
|
|
3018
|
+
* erc7710 x402 payment out of band — the remedy #2970's guidance could not
|
|
3019
|
+
* name, because no hosted tool accepted a hash. An agent reaches this after
|
|
3020
|
+
* `haven_settle_mcp_tool` / `haven_complete_mcp_tool` answered
|
|
3021
|
+
* `DELIVERED_UNSETTLED` or `SETTLEMENT_PENDING`, or after
|
|
3022
|
+
* `haven_get_payment_status` reports `awaiting_settlement_evidence` — in
|
|
3023
|
+
* every one of those cases the agent may be holding the merchant's own
|
|
3024
|
+
* `PAYMENT-RESPONSE.transaction` while Haven has nothing.
|
|
3025
|
+
*
|
|
3026
|
+
* Reuses `reportEvidence` — same backend seam
|
|
3027
|
+
* (`POST /machine-payments/evidence` → `observeErc7710Settlement`,
|
|
3028
|
+
* fail-closed — see `settlement-observed.ts`), same three-outcome contract.
|
|
3029
|
+
* `resourceUrl` and `merchantStatus` are omitted: this call has no fresh
|
|
3030
|
+
* merchant HTTP exchange to read either from, and both are optional at the
|
|
3031
|
+
* backend (see the parameter doc on `reportEvidence`).
|
|
3032
|
+
*
|
|
3033
|
+
* The zero hash is refused HERE, client-side, before any network call —
|
|
3034
|
+
* never posted. `isZeroSettlementTxHash` is the same recognizer the #2970
|
|
3035
|
+
* gate uses, so a caller cannot "fix" a missing hash by reporting the demo
|
|
3036
|
+
* merchant's own marker and getting a different verdict than the settle
|
|
3037
|
+
* path already gave it.
|
|
3038
|
+
*/
|
|
3039
|
+
async reportSettlementEvidence(paymentId, settlementTxHash) {
|
|
3040
|
+
if (isZeroSettlementTxHash(settlementTxHash)) {
|
|
3041
|
+
throw new HavenZeroSettlementHashError(paymentId);
|
|
3042
|
+
}
|
|
3043
|
+
return this.reportEvidence({
|
|
3044
|
+
paymentId,
|
|
3045
|
+
rail: "x402",
|
|
3046
|
+
txHash: settlementTxHash
|
|
3047
|
+
});
|
|
3048
|
+
}
|
|
2885
3049
|
};
|
|
3050
|
+
function isZeroSettlementTxHash(hash) {
|
|
3051
|
+
return typeof hash === "string" && /^0x0+$/i.test(hash);
|
|
3052
|
+
}
|
|
2886
3053
|
function parseMerchantSettlement(header) {
|
|
2887
3054
|
if (!header) return {};
|
|
2888
3055
|
const parsed = parseProtocolReceiptHeader(header);
|
|
@@ -3384,7 +3551,7 @@ var HavenClient = class {
|
|
|
3384
3551
|
const query = params.size > 0 ? `?${params.toString()}` : "";
|
|
3385
3552
|
const raw = await this.get(`/catalog${query}`);
|
|
3386
3553
|
let entries = raw.entries.map(mapCatalogEntry);
|
|
3387
|
-
if (options.verified === "verified") entries = entries.filter((e) => e.
|
|
3554
|
+
if (options.verified === "verified") entries = entries.filter((e) => e.verifiedPayable === true);
|
|
3388
3555
|
if (options.verified === "operator") entries = entries.filter((e) => e.source === "operator");
|
|
3389
3556
|
return entries;
|
|
3390
3557
|
}
|
|
@@ -3521,9 +3688,16 @@ var HavenClient = class {
|
|
|
3521
3688
|
const request = snapshotX402Request(url, initialInit);
|
|
3522
3689
|
const response = await this.merchantTransport.fetch(url, initialInit);
|
|
3523
3690
|
if (response.status !== 402) {
|
|
3691
|
+
let body;
|
|
3692
|
+
try {
|
|
3693
|
+
body = await response.clone().json();
|
|
3694
|
+
} catch {
|
|
3695
|
+
body = void 0;
|
|
3696
|
+
}
|
|
3524
3697
|
throw new X402UnexpectedStatusError(
|
|
3525
3698
|
`Expected an x402 quote response with HTTP 402, got HTTP ${response.status}.`,
|
|
3526
|
-
response.status || 400
|
|
3699
|
+
response.status || 400,
|
|
3700
|
+
body
|
|
3527
3701
|
);
|
|
3528
3702
|
}
|
|
3529
3703
|
if (response.headers.get("MACHINE-PAYMENT-CHALLENGE")) {
|
|
@@ -3812,6 +3986,7 @@ var HavenClient = class {
|
|
|
3812
3986
|
} catch {
|
|
3813
3987
|
body = text;
|
|
3814
3988
|
}
|
|
3989
|
+
let evidenceOutcome;
|
|
3815
3990
|
if (!surfaced.ok) {
|
|
3816
3991
|
if (!input.noFundingLeg && fundingTxHash) {
|
|
3817
3992
|
await this.merchantCompletion.recordRetryRejected({
|
|
@@ -3831,9 +4006,10 @@ var HavenClient = class {
|
|
|
3831
4006
|
});
|
|
3832
4007
|
}
|
|
3833
4008
|
} else {
|
|
3834
|
-
const
|
|
4009
|
+
const rawEvidenceTxHash = input.noFundingLeg ? settlement.settlementTxHash ?? void 0 : fundingTxHash ?? void 0;
|
|
4010
|
+
const evidenceTxHash = rawEvidenceTxHash && !isZeroSettlementTxHash(rawEvidenceTxHash) ? rawEvidenceTxHash : void 0;
|
|
3835
4011
|
if (evidenceTxHash) {
|
|
3836
|
-
await this.merchantCompletion.reportEvidence({
|
|
4012
|
+
evidenceOutcome = await this.merchantCompletion.reportEvidence({
|
|
3837
4013
|
paymentId: evidenceContext.paymentId,
|
|
3838
4014
|
rail: "x402",
|
|
3839
4015
|
txHash: evidenceTxHash,
|
|
@@ -3851,7 +4027,8 @@ var HavenClient = class {
|
|
|
3851
4027
|
status: surfaced.status,
|
|
3852
4028
|
ok: surfaced.ok,
|
|
3853
4029
|
body,
|
|
3854
|
-
settlementTxHash: settlement.settlementTxHash ?? void 0
|
|
4030
|
+
settlementTxHash: settlement.settlementTxHash ?? void 0,
|
|
4031
|
+
evidenceOutcome
|
|
3855
4032
|
};
|
|
3856
4033
|
}
|
|
3857
4034
|
/**
|
|
@@ -3866,6 +4043,19 @@ var HavenClient = class {
|
|
|
3866
4043
|
async reportX402MerchantOutcome(input) {
|
|
3867
4044
|
return await this.merchantCompletion.reportMerchantOutcome(input);
|
|
3868
4045
|
}
|
|
4046
|
+
/**
|
|
4047
|
+
* #2972: report the merchant's real settlement transaction hash for an
|
|
4048
|
+
* erc7710 x402 payment — the remedy for `DELIVERED_UNSETTLED` /
|
|
4049
|
+
* `SETTLEMENT_PENDING` / `awaiting_settlement_evidence` when the agent
|
|
4050
|
+
* holds the hash (`PAYMENT-RESPONSE.transaction`, or a prior settle/
|
|
4051
|
+
* complete result's `settlement_tx_hash`) and Haven does not. See
|
|
4052
|
+
* `MerchantCompletion.reportSettlementEvidence` for the fail-closed
|
|
4053
|
+
* verification this posts into (`observeErc7710Settlement`) and the
|
|
4054
|
+
* client-side zero-hash refusal.
|
|
4055
|
+
*/
|
|
4056
|
+
async reportSettlementEvidence(paymentId, settlementTxHash) {
|
|
4057
|
+
return await this.merchantCompletion.reportSettlementEvidence(paymentId, settlementTxHash);
|
|
4058
|
+
}
|
|
3869
4059
|
/**
|
|
3870
4060
|
* GET /x402/:id/merchant-call-context — the settle-leg twin of #1263's
|
|
3871
4061
|
* sign-context fetch (#1307). Re-serves the stored merchant MCP-tool call
|
|
@@ -4022,13 +4212,13 @@ var toolDescriptions = {
|
|
|
4022
4212
|
summary: "Pay an inspected x402 quote. The delegate key signs locally; Haven only validates and relays signed, on-chain-constrained payment transactions.",
|
|
4023
4213
|
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.",
|
|
4024
4214
|
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.",
|
|
4025
|
-
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_safe_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."
|
|
4215
|
+
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_safe_or_raise_allowance (or 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."
|
|
4026
4216
|
},
|
|
4027
4217
|
payX402OneShot: {
|
|
4028
4218
|
summary: "Fetch an x402 paid HTTP resource in a single call. Handles the full probe -> pay -> retry round trip and returns the merchant response.",
|
|
4029
4219
|
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.",
|
|
4030
4220
|
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.",
|
|
4031
|
-
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_safe_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."
|
|
4221
|
+
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_safe_or_raise_allowance (or 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."
|
|
4032
4222
|
},
|
|
4033
4223
|
resumeX402: {
|
|
4034
4224
|
summary: "Resume an x402 payment whose Haven-side authorization already succeeded but whose merchant retry did not complete.",
|
|
@@ -4040,8 +4230,8 @@ var toolDescriptions = {
|
|
|
4040
4230
|
// MACHINE-PAYMENT-CHALLENGE was never produced by anything besides the now
|
|
4041
4231
|
// deleted `/demo/mpp/*` route. Use the x402 fragments above instead.
|
|
4042
4232
|
getPaymentStatus: {
|
|
4043
|
-
summary: "Fetch structured Haven payment status
|
|
4044
|
-
behavior: "
|
|
4233
|
+
summary: "Fetch structured Haven payment status for agent recovery.",
|
|
4234
|
+
behavior: "State: phase, nextAction, rail, amount, merchant, resource, idempotency, message; parties: treasury/delegate/delegateAccount/merchant. awaiting_settlement_evidence: poll once, else unverified.",
|
|
4045
4235
|
nextActionGuidance: ""
|
|
4046
4236
|
},
|
|
4047
4237
|
getResumeState: {
|
|
@@ -4052,7 +4242,7 @@ var toolDescriptions = {
|
|
|
4052
4242
|
getAgent: {
|
|
4053
4243
|
summary: "Return the authenticated agent identity AND its live spend authority in one call: Haven wallet, delegate, chain, raw status, spend_authority_readiness, and per-token remaining allowance (atomic + human-readable). The recommended first call in a new session to confirm who you are and whether Haven will let you spend right now.",
|
|
4054
4244
|
selectionGuidance: "Use this as the one-shot orientation/bootstrap at the start of a session, or whenever you need to confirm identity together with whether the agent can spend right now. For a detailed per-token breakdown (configured vs spent vs reset window) use haven_get_allowances.",
|
|
4055
|
-
behavior: `Reads identity plus the live spend-authority snapshot in one shot \u2014 the agent's 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, so ask the owner to grant or raise the budget in Haven rather than waiting for an approval. allowances[] carries remainingAtomic and remainingDisplay per token. Identity fields
|
|
4245
|
+
behavior: `Reads identity plus the live spend-authority snapshot in one shot \u2014 the agent's 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, so ask the owner to grant or raise the budget in Haven rather than waiting for an approval. allowances[] carries remainingAtomic and remainingDisplay per token. Identity fields: id, name, status, accountAddress (safeAddress: deprecated alias, same value), delegateAddress, chainId.`,
|
|
4056
4246
|
nextActionGuidance: ""
|
|
4057
4247
|
},
|
|
4058
4248
|
getAllowances: {
|
|
@@ -4081,7 +4271,7 @@ var toolDescriptions = {
|
|
|
4081
4271
|
},
|
|
4082
4272
|
discoverTools: {
|
|
4083
4273
|
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.",
|
|
4084
|
-
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
|
|
4274
|
+
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.",
|
|
4085
4275
|
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.",
|
|
4086
4276
|
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.`
|
|
4087
4277
|
},
|
|
@@ -4092,9 +4282,9 @@ var toolDescriptions = {
|
|
|
4092
4282
|
nextActionGuidance: "Give the verify_token and the well-known instructions (from getCatalogSubmissionStatus) to the merchant so they can publish the proof line, then poll the submission status until it reaches verified_payable or failed."
|
|
4093
4283
|
},
|
|
4094
4284
|
sweep_delegate: {
|
|
4095
|
-
summary: "Sweep stranded USDC and/or ETH from the delegate wallet back to the originating
|
|
4285
|
+
summary: "Sweep stranded USDC and/or ETH from the delegate wallet back to the originating account.",
|
|
4096
4286
|
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.",
|
|
4097
|
-
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
|
|
4287
|
+
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.`,
|
|
4098
4288
|
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.'
|
|
4099
4289
|
},
|
|
4100
4290
|
send: {
|
|
@@ -4102,6 +4292,11 @@ var toolDescriptions = {
|
|
|
4102
4292
|
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.",
|
|
4103
4293
|
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.",
|
|
4104
4294
|
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."
|
|
4295
|
+
},
|
|
4296
|
+
reportSettlementEvidence: {
|
|
4297
|
+
summary: "Report an erc7710 payment's real settlement transaction hash so Haven can verify it on-chain and confirm the payment.",
|
|
4298
|
+
behavior: "Pass payment_id and settlement_tx_hash (0x + 64 hex chars) \u2014 from PAYMENT-RESPONSE or a prior settlement_tx_hash. Haven verifies on-chain before confirming; a zero, mismatched, or reverted hash is refused. Your own payments only.",
|
|
4299
|
+
nextActionGuidance: "code DELIVERED_UNSETTLED: did not verify, do not retry \u2014 poll haven_get_payment_status. code SETTLEMENT_PENDING (retryable:true): not mined or RPC unreachable \u2014 report the same hash again shortly."
|
|
4105
4300
|
}
|
|
4106
4301
|
};
|
|
4107
4302
|
|
|
@@ -4704,15 +4899,30 @@ present and surface \`message\` or \`error\` verbatim. Common cases:
|
|
|
4704
4899
|
Round the cap, or send an exact atomic \`max_amount\`.
|
|
4705
4900
|
- \`PAYMENT_WINDOW_EXPIRED\`: re-run the quote/prepare tool with the same
|
|
4706
4901
|
\`idempotency_key\`, then sign the fresh payload.
|
|
4902
|
+
- \`MERCHANT_NOT_READY\`: the merchant refused the quote with its own
|
|
4903
|
+
"cannot settle right now" signal (a 503 \`merchant_not_ready\` with a
|
|
4904
|
+
\`reason_code\`) instead of a 402. No payment was created. Tell the user;
|
|
4905
|
+
retry later (the message carries \`retry_after_s\` when the merchant gave
|
|
4906
|
+
one) \u2014 this is not a wrong or broken endpoint.
|
|
4707
4907
|
- \`MERCHANT_REJECTED_AFTER_FUNDING\`: the merchant refused the paid retry.
|
|
4708
|
-
Stop-and-sweep \u2014 stop retrying the
|
|
4709
|
-
\`mcp__haven__haven_sweep_delegate\` to recover stranded
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4908
|
+
On eip3009 (\`rail\` not \`erc7710\`): Stop-and-sweep \u2014 stop retrying the
|
|
4909
|
+
merchant and use \`mcp__haven__haven_sweep_delegate\` to recover stranded
|
|
4910
|
+
delegate funds. On erc7710 there is no funding leg and nothing to sweep:
|
|
4911
|
+
follow the message \u2014 it says whether the merchant declined to settle
|
|
4912
|
+
(re-quote later) or whether to check \`haven_get_payment_status\` after
|
|
4913
|
+
the payment window first.
|
|
4914
|
+
- \`MERCHANT_UNRESPONSIVE_AFTER_FUNDING\`: the merchant never answered the paid
|
|
4915
|
+
retry. This is NOT proof of rejection \u2014 the merchant may still settle late.
|
|
4916
|
+
On eip3009 (\`rail\` not \`erc7710\`), funding confirmed on-chain: Verify-then-sweep,
|
|
4917
|
+
never a blind sweep \u2014 check \`mcp__haven__haven_get_payment_status\`, retry
|
|
4714
4918
|
\`mcp__haven__haven_complete_mcp_tool\` ONCE, and only sweep with
|
|
4715
|
-
\`mcp__haven__haven_sweep_delegate\` if no settlement appears.
|
|
4919
|
+
\`mcp__haven__haven_sweep_delegate\` if no settlement appears. On erc7710
|
|
4920
|
+
there is no funding leg and nothing to sweep, and
|
|
4921
|
+
\`mcp__haven__haven_complete_mcp_tool\` has no erc7710 branch (it refuses a
|
|
4922
|
+
submitted intent) \u2014 do not retry it: the merchant may still redeem the
|
|
4923
|
+
settlement authorization within the payment window, so check
|
|
4924
|
+
\`mcp__haven__haven_get_payment_status\` after that window and re-quote only
|
|
4925
|
+
if it shows no settlement.
|
|
4716
4926
|
- Budget exceeded: tell the user how much remains (from
|
|
4717
4927
|
\`mcp__haven__haven_get_allowances\`) and that they can raise the budget in
|
|
4718
4928
|
Haven.
|
|
@@ -4837,6 +5047,7 @@ exports.AgentPaymentFailureCode = AgentPaymentFailureCode;
|
|
|
4837
5047
|
exports.AgentPaymentFailureCodeDescriptions = AgentPaymentFailureCodeDescriptions;
|
|
4838
5048
|
exports.AgentPaymentFailureCodeSchema = AgentPaymentFailureCodeSchema;
|
|
4839
5049
|
exports.AgentPaymentNextAction = AgentPaymentNextAction;
|
|
5050
|
+
exports.AgentPaymentNextActionAccountAlias = AgentPaymentNextActionAccountAlias;
|
|
4840
5051
|
exports.AgentPaymentNextActionDescriptions = AgentPaymentNextActionDescriptions;
|
|
4841
5052
|
exports.AgentPaymentNextActionSchema = AgentPaymentNextActionSchema;
|
|
4842
5053
|
exports.AgentPaymentPhase = AgentPaymentPhase;
|
|
@@ -4862,6 +5073,7 @@ exports.HavenPaymentStateError = HavenPaymentStateError;
|
|
|
4862
5073
|
exports.HavenSigningError = HavenSigningError;
|
|
4863
5074
|
exports.HavenTimeoutError = HavenTimeoutError;
|
|
4864
5075
|
exports.HavenUnsupportedSignerVersionError = HavenUnsupportedSignerVersionError;
|
|
5076
|
+
exports.HavenZeroSettlementHashError = HavenZeroSettlementHashError;
|
|
4865
5077
|
exports.MERCHANT_DISCOVERY_PATHS = MERCHANT_DISCOVERY_PATHS;
|
|
4866
5078
|
exports.MerchantTimeoutError = MerchantTimeoutError;
|
|
4867
5079
|
exports.RECEIPT_VERSION = RECEIPT_VERSION;
|
|
@@ -4883,10 +5095,12 @@ exports.X402_PAYMENT_HEADER_NAMES_SENT = X402_PAYMENT_HEADER_NAMES_SENT;
|
|
|
4883
5095
|
exports.X402_PAYMENT_REQUIRED_HEADER_NAME = X402_PAYMENT_REQUIRED_HEADER_NAME;
|
|
4884
5096
|
exports.X402_PAYMENT_RESPONSE_HEADER_NAME = X402_PAYMENT_RESPONSE_HEADER_NAME;
|
|
4885
5097
|
exports.X402_SETTLEMENT_FORWARD_MARGIN_SECONDS = X402_SETTLEMENT_FORWARD_MARGIN_SECONDS;
|
|
5098
|
+
exports.accountAddressTwins = accountAddressTwins;
|
|
4886
5099
|
exports.addressFromKey = addressFromKey;
|
|
4887
5100
|
exports.buildSweepAuthorizationMessage = buildSweepAuthorizationMessage;
|
|
4888
5101
|
exports.buildSweepTypedData = buildSweepTypedData;
|
|
4889
5102
|
exports.buildX402ExpectedMessage = buildX402ExpectedMessage;
|
|
5103
|
+
exports.canonicalAgentPaymentNextAction = canonicalAgentPaymentNextAction;
|
|
4890
5104
|
exports.compareNodeVersions = compareNodeVersions;
|
|
4891
5105
|
exports.composeDescription = composeDescription;
|
|
4892
5106
|
exports.connectorRerunCommand = connectorRerunCommand;
|
|
@@ -4900,11 +5114,16 @@ exports.encodePaymentProof = encodePaymentProof;
|
|
|
4900
5114
|
exports.havenTools = havenTools;
|
|
4901
5115
|
exports.isConnectorChannel = isConnectorChannel;
|
|
4902
5116
|
exports.isErc7710Option = isErc7710Option;
|
|
5117
|
+
exports.isFundAccountOrRaiseAllowance = isFundAccountOrRaiseAllowance;
|
|
4903
5118
|
exports.isSupportedNodeVersion = isSupportedNodeVersion;
|
|
4904
5119
|
exports.isSweepableChain = isSweepableChain;
|
|
5120
|
+
exports.isZeroSettlementTxHash = isZeroSettlementTxHash;
|
|
4905
5121
|
exports.normalizePaymentRequired = normalizePaymentRequired;
|
|
4906
5122
|
exports.parsePaymentRequired = parsePaymentRequired;
|
|
4907
5123
|
exports.parsePaymentRequiredResponse = parsePaymentRequiredResponse;
|
|
5124
|
+
exports.readAccountAddress = readAccountAddress;
|
|
5125
|
+
exports.readAccountId = readAccountId;
|
|
5126
|
+
exports.readX402ReceiptPayer = readX402ReceiptPayer;
|
|
4908
5127
|
exports.resolveConnectorChannel = resolveConnectorChannel;
|
|
4909
5128
|
exports.resolveTokenFromAddress = resolveTokenFromAddress;
|
|
4910
5129
|
exports.sameUrl = sameUrl;
|