@haven_ai/sdk 0.6.0-alpha.0 → 0.7.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 +22 -7
- package/dist/{base64-cD9kR4JL.d.cts → base64-_seD1uw8.d.cts} +184 -4
- package/dist/{base64-cD9kR4JL.d.ts → base64-_seD1uw8.d.ts} +184 -4
- package/dist/{chunk-S42QSWJY.cjs → chunk-3SVPI4LS.cjs} +93 -8
- package/dist/chunk-3SVPI4LS.cjs.map +1 -0
- package/dist/{chunk-CK4NLLMO.cjs → chunk-J6DQEBK2.cjs} +36 -29
- package/dist/chunk-J6DQEBK2.cjs.map +1 -0
- package/dist/{chunk-63GPTEEY.js → chunk-NNIT7WAI.js} +90 -9
- package/dist/chunk-NNIT7WAI.js.map +1 -0
- package/dist/{chunk-3WSEX3GW.js → chunk-R3RZJJWL.js} +11 -5
- package/dist/chunk-R3RZJJWL.js.map +1 -0
- package/dist/edge.cjs +275 -64
- package/dist/edge.cjs.map +1 -1
- package/dist/edge.d.cts +99 -2
- package/dist/edge.d.ts +99 -2
- package/dist/edge.js +204 -4
- package/dist/edge.js.map +1 -1
- package/dist/index.cjs +579 -348
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +118 -28
- package/dist/index.d.ts +118 -28
- package/dist/index.js +280 -70
- package/dist/index.js.map +1 -1
- package/dist/test-support.cjs +18 -18
- package/dist/test-support.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-3WSEX3GW.js.map +0 -1
- package/dist/chunk-63GPTEEY.js.map +0 -1
- package/dist/chunk-CK4NLLMO.cjs.map +0 -1
- package/dist/chunk-S42QSWJY.cjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { selectStandardPaymentOption, buildX402IdempotencyKey, x402AuthorizationAmount, assertBoundDirectPaymentUserOp, assertFundingLegPaysDelegate, HavenTypedDataRefusedError, assertOwnSettlementChild, parsePaymentRequiredResponse, x402PaymentHeaderNamesSent, SDK_CLIENT_IDENTITY, readClientUpdate, HAVEN_CLIENT_HEADER, resolveTokenFromAddress, isSweepableChain, sweepUsdcAddress, x402PaymentHeaderNamesFor, X402_PAYMENT_HEADER_NAMES, toStandardPaymentRequirements, decodeBase64Json, encodeBase64Json, x402V2PaymentEnvelope, selectX402SettlementScheme, selectErc7710PaymentOption, isErc7710Option, x402AssetTransferMethod, normalizePaymentRequired } from './chunk-
|
|
2
|
-
export { DEFAULT_NEXT_TOOL_BY_ACTION, ERC7710_ASSET_TRANSFER_METHOD, HAVEN_CLIENT_HEADER, HAVEN_MINIMUM_NODE_VERSION, HavenTypedDataRefusedError, MAX_TASK_BUDGET_TTL_SECONDS, NEXT_TOOL_SERVER_NAMES, NEXT_TOOL_SERVER_ROLES, SDK_CLIENT_IDENTITY, SDK_VERSION, SWEEP_BASE_CHAIN_ID, SWEEP_BASE_SEPOLIA_CHAIN_ID, SWEEP_BASE_SEPOLIA_USDC_ADDRESS, SWEEP_BASE_USDC_ADDRESS, TRANSFER_WITH_AUTHORIZATION_TYPES, TYPED_DATA_NOT_ALLOWED, X402PaymentHeaderValidationError, 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, assertBoundDirectPaymentUserOp, assertOwnTaskBudgetCloseUserOp, assertOwnTaskChild, buildSweepAuthorizationMessage, buildSweepTypedData, buildX402ExpectedMessage, compareNodeVersions, createNextStepBuilder, decodeBase64Json, decodeBase64Utf8, defaultNextToolFor, encodeBase64Json, encodeBase64Utf8, encodePaymentProof, hashDelegation, havenClientIdentity, isErc7710Option, isSupportedNodeVersion, isSweepableChain, isTaskChildTypedData, normalizePaymentRequired, parseNextTool, parsePaymentRequired, parsePaymentRequiredResponse, readClientUpdate, renderNextTool, resolveTokenFromAddress, selectErc7710PaymentOption, selectPaymentOption, selectStandardPaymentOption, selectX402SettlementScheme, sweepUsdcAddress, sweepUsdcDomain, toStandardPaymentRequirements, unsupportedNodeVersionMessage, validateStandardX402PaymentHeader, x402AssetTransferMethod, x402AuthorizationAmount, x402FacilitatorAddresses, x402V2PaymentEnvelope } from './chunk-
|
|
3
|
-
import { HavenSigningError, HavenError, DEFAULT_CONFIRMATION_TIMEOUT_MS, HavenApiError, assertUserOpTypedDataBinding, AgentPaymentRail, AgentPaymentPhase, HavenTimeoutError, X402UnexpectedStatusError, X402AlreadySettledError, AgentPaymentWarningCode, MerchantTimeoutError, AgentPaymentNextAction, HavenPaymentStateError, HavenZeroSettlementHashError, chainIdForNetwork } from './chunk-
|
|
4
|
-
export { AGENT_PAYMENT_FAILURE_CODE_VALUES, AGENT_PAYMENT_NEXT_ACTION_VALUES, AGENT_PAYMENT_PHASE_VALUES, AGENT_PAYMENT_RAIL_VALUES, AgentPaymentFailureCode, AgentPaymentFailureCodeDescriptions, AgentPaymentFailureCodeSchema, AgentPaymentNextAction, AgentPaymentNextActionDescriptions, AgentPaymentNextActionSchema, AgentPaymentPhase, AgentPaymentPhaseDescriptions, AgentPaymentPhaseSchema, AgentPaymentRail, AgentPaymentRailDescriptions, AgentPaymentRailSchema, AgentPaymentWarningCode, CONNECTOR_PACKAGE_NAME, DEFAULT_CONFIRMATION_TIMEOUT_MS, DIRECT_SIGN_CONTEXT_VERSION, ENTRY_POINT_V07, HAVEN_CONNECTOR_CHANNEL, HYBRID_DELEGATOR_DOMAIN_NAME, HYBRID_DELEGATOR_DOMAIN_VERSION, HavenApiError, HavenError, HavenPaymentStateError, HavenSigningError, HavenTimeoutError, HavenUnsupportedSignerVersionError, HavenUserOpBindingError, HavenZeroSettlementHashError, MerchantTimeoutError, PACKED_USER_OPERATION_FIELDS, ROOT_AUTHORITY, SIGNER_UPDATE_FALLBACK, SignerRefusalCode, X402AlreadySettledError, X402UnexpectedStatusError, assertUserOpTypedDataBinding, connectorRerunCommand, connectorSpec, deriveDelegateAccountAddress, isConnectorChannel, isPackedUserOperationTypedData, packedUserOperationHash, resolveConnectorChannel, signerUpdateFallback, verifySettlementChild } from './chunk-
|
|
5
|
-
import { hashTypedData } from 'viem';
|
|
1
|
+
import { selectStandardPaymentOption, buildX402IdempotencyKey, x402AuthorizationAmount, assertBoundDirectPaymentUserOp, assertFundingLegPaysDelegate, HavenTypedDataRefusedError, assertOwnSettlementChild, parsePaymentRequiredResponse, x402PaymentHeaderNamesSent, SDK_CLIENT_IDENTITY, readClientUpdate, HAVEN_CLIENT_HEADER, resolveTokenFromAddress, isSweepableChain, sweepUsdcAddress, x402PaymentHeaderNamesFor, X402_PAYMENT_HEADER_NAMES, toStandardPaymentRequirements, decodeBase64Json, encodeBase64Json, x402V2PaymentEnvelope, selectX402SettlementScheme, selectErc7710PaymentOption, isErc7710Option, x402AssetTransferMethod, normalizePaymentRequired } from './chunk-R3RZJJWL.js';
|
|
2
|
+
export { DEFAULT_NEXT_TOOL_BY_ACTION, ERC7710_ASSET_TRANSFER_METHOD, HAVEN_CLIENT_HEADER, HAVEN_MINIMUM_NODE_VERSION, HavenTypedDataRefusedError, MAX_TASK_BUDGET_TTL_SECONDS, NEXT_TOOL_SERVER_NAMES, NEXT_TOOL_SERVER_ROLES, SDK_CLIENT_IDENTITY, SDK_VERSION, SWEEP_BASE_CHAIN_ID, SWEEP_BASE_SEPOLIA_CHAIN_ID, SWEEP_BASE_SEPOLIA_USDC_ADDRESS, SWEEP_BASE_USDC_ADDRESS, TRANSFER_WITH_AUTHORIZATION_TYPES, TYPED_DATA_NOT_ALLOWED, X402PaymentHeaderValidationError, 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, assertBoundDirectPaymentUserOp, assertOwnTaskBudgetCloseUserOp, assertOwnTaskChild, buildSweepAuthorizationMessage, buildSweepTypedData, buildX402ExpectedMessage, compareNodeVersions, createNextStepBuilder, decodeBase64Json, decodeBase64Utf8, defaultNextToolFor, encodeBase64Json, encodeBase64Utf8, encodePaymentProof, hashDelegation, havenClientIdentity, isErc7710Option, isSupportedNodeVersion, isSweepableChain, isTaskChildTypedData, normalizePaymentRequired, parseNextTool, parsePaymentRequired, parsePaymentRequiredResponse, readClientUpdate, renderNextTool, resolveTokenBySymbol, resolveTokenFromAddress, selectErc7710PaymentOption, selectPaymentOption, selectStandardPaymentOption, selectX402SettlementScheme, sweepUsdcAddress, sweepUsdcDomain, toStandardPaymentRequirements, unsupportedNodeVersionMessage, validateStandardX402PaymentHeader, x402AssetTransferMethod, x402AuthorizationAmount, x402FacilitatorAddresses, x402V2PaymentEnvelope } from './chunk-R3RZJJWL.js';
|
|
3
|
+
import { HavenSigningError, HavenError, DEFAULT_CONFIRMATION_TIMEOUT_MS, HavenApiError, assertUserOpTypedDataBinding, AgentPaymentRail, AgentPaymentPhase, HavenTimeoutError, X402TaskBudgetMismatchError, X402UnexpectedStatusError, X402AlreadySettledError, AgentPaymentWarningCode, MerchantTimeoutError, AgentPaymentNextAction, HavenPaymentStateError, HavenZeroSettlementHashError, X402Erc7710AlreadySettledError, DELEGATION_MANAGER, chainIdForNetwork } from './chunk-NNIT7WAI.js';
|
|
4
|
+
export { AGENT_PAYMENT_FAILURE_CODE_VALUES, AGENT_PAYMENT_NEXT_ACTION_VALUES, AGENT_PAYMENT_PHASE_VALUES, AGENT_PAYMENT_RAIL_VALUES, AgentPaymentFailureCode, AgentPaymentFailureCodeDescriptions, AgentPaymentFailureCodeSchema, AgentPaymentNextAction, AgentPaymentNextActionDescriptions, AgentPaymentNextActionSchema, AgentPaymentPhase, AgentPaymentPhaseDescriptions, AgentPaymentPhaseSchema, AgentPaymentRail, AgentPaymentRailDescriptions, AgentPaymentRailSchema, AgentPaymentWarningCode, CONNECTOR_PACKAGE_NAME, DEFAULT_CONFIRMATION_TIMEOUT_MS, DIRECT_SIGN_CONTEXT_VERSION, ENTRY_POINT_V07, HAVEN_CONNECTOR_CHANNEL, HYBRID_DELEGATOR_DOMAIN_NAME, HYBRID_DELEGATOR_DOMAIN_VERSION, HavenApiError, HavenError, HavenPaymentStateError, HavenSigningError, HavenTimeoutError, HavenUnsupportedSignerVersionError, HavenUserOpBindingError, HavenZeroSettlementHashError, MerchantTimeoutError, PACKED_USER_OPERATION_FIELDS, ROOT_AUTHORITY, SIGNER_UPDATE_FALLBACK, SignerRefusalCode, TASK_SIGN_CONTEXT_VERSION, X402AlreadySettledError, X402Erc7710AlreadySettledError, X402TaskBudgetMismatchError, X402UnexpectedStatusError, assertUserOpTypedDataBinding, connectorRerunCommand, connectorSpec, connectorUpgradeCommand, deriveDelegateAccountAddress, isConnectorChannel, isPackedUserOperationTypedData, packedUserOperationHash, resolveConnectorChannel, signerUpdateFallback, verifySettlementChild } from './chunk-NNIT7WAI.js';
|
|
5
|
+
import { keccak256, toBytes, concatHex, hashTypedData, encodeAbiParameters } from 'viem';
|
|
6
6
|
import { ethers } from 'ethers';
|
|
7
7
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
8
8
|
import { exact } from 'x402/schemes';
|
|
@@ -161,7 +161,18 @@ function mapParties(raw) {
|
|
|
161
161
|
treasuryAccount: raw.treasury_account,
|
|
162
162
|
delegate: raw.delegate,
|
|
163
163
|
delegateAccount: raw.delegate_account,
|
|
164
|
-
merchant: raw.merchant
|
|
164
|
+
merchant: raw.merchant,
|
|
165
|
+
// #3332: additive — absent unless the wire shape carries it.
|
|
166
|
+
...raw.buyer ? {
|
|
167
|
+
buyer: {
|
|
168
|
+
legalName: raw.buyer.legal_name,
|
|
169
|
+
country: raw.buyer.country,
|
|
170
|
+
orgNumber: raw.buyer.org_number,
|
|
171
|
+
vatNumber: raw.buyer.vat_number,
|
|
172
|
+
viesStatus: raw.buyer.vies_status,
|
|
173
|
+
viesCheckedAt: raw.buyer.vies_checked_at
|
|
174
|
+
}
|
|
175
|
+
} : {}
|
|
165
176
|
};
|
|
166
177
|
}
|
|
167
178
|
function mapPaymentResult(raw, buildExplorerUrl2) {
|
|
@@ -205,6 +216,9 @@ function mapPaymentStatusResult(raw) {
|
|
|
205
216
|
expiresAt: raw.expires_at,
|
|
206
217
|
chainId: raw.chain_id,
|
|
207
218
|
message: raw.message,
|
|
219
|
+
// #3420: the delivered half of the settle vocabulary, additive — absent
|
|
220
|
+
// from the raw payload (older backend) stays absent here.
|
|
221
|
+
...raw.delivered === true ? { delivered: true } : {},
|
|
208
222
|
fee: raw.fee ? {
|
|
209
223
|
amount: raw.fee.amount,
|
|
210
224
|
token: raw.fee.token,
|
|
@@ -838,22 +852,93 @@ function selectJsonRpcResult(messages) {
|
|
|
838
852
|
return messages.find((message) => "result" in message || "error" in message) ?? messages.at(-1);
|
|
839
853
|
}
|
|
840
854
|
var RECEIPT_VERSION = "haven-receipt-1";
|
|
855
|
+
var DELEGATION_MANAGER_CHAIN_IDS = /* @__PURE__ */ new Set([8453, 84532]);
|
|
856
|
+
var EIP712_DOMAIN_TYPEHASH = keccak256(
|
|
857
|
+
toBytes("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
|
|
858
|
+
);
|
|
859
|
+
function delegationDomainSeparator(chainId) {
|
|
860
|
+
return keccak256(
|
|
861
|
+
encodeAbiParameters(
|
|
862
|
+
[{ type: "bytes32" }, { type: "bytes32" }, { type: "bytes32" }, { type: "uint256" }, { type: "address" }],
|
|
863
|
+
[
|
|
864
|
+
EIP712_DOMAIN_TYPEHASH,
|
|
865
|
+
keccak256(toBytes("DelegationManager")),
|
|
866
|
+
keccak256(toBytes("1")),
|
|
867
|
+
BigInt(chainId),
|
|
868
|
+
DELEGATION_MANAGER
|
|
869
|
+
]
|
|
870
|
+
)
|
|
871
|
+
);
|
|
872
|
+
}
|
|
873
|
+
function delegationDigest(structHash, chainId) {
|
|
874
|
+
return keccak256(concatHex(["0x1901", delegationDomainSeparator(chainId), structHash]));
|
|
875
|
+
}
|
|
841
876
|
function defaultRecover(hash, signature) {
|
|
842
877
|
return ethers.recoverAddress(hash, signature);
|
|
843
878
|
}
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
if (!signature) return { verified: false, reason: "missing_signature" };
|
|
879
|
+
var NOT_A_SIGNED_RECEIPT = { verified: false, reason: "not_a_signed_receipt" };
|
|
880
|
+
function match(delegate, hash, signature, recover) {
|
|
847
881
|
let recovered;
|
|
848
882
|
try {
|
|
849
|
-
recovered = recover(
|
|
883
|
+
recovered = recover(hash, signature);
|
|
850
884
|
} catch {
|
|
851
|
-
return {
|
|
885
|
+
return { ok: false, reason: "bad_signature" };
|
|
852
886
|
}
|
|
853
887
|
if (recovered.toLowerCase() !== delegate.toLowerCase()) {
|
|
854
|
-
return {
|
|
888
|
+
return { ok: false, reason: "signer_mismatch", recoveredSigner: recovered };
|
|
855
889
|
}
|
|
856
|
-
return {
|
|
890
|
+
return { ok: true, recoveredSigner: recovered };
|
|
891
|
+
}
|
|
892
|
+
function finish(result, verifiedOver) {
|
|
893
|
+
if (result.ok) {
|
|
894
|
+
return { verified: true, recoveredSigner: result.recoveredSigner, verifiedOver };
|
|
895
|
+
}
|
|
896
|
+
return result.recoveredSigner !== void 0 ? { verified: false, reason: result.reason, recoveredSigner: result.recoveredSigner } : { verified: false, reason: result.reason };
|
|
897
|
+
}
|
|
898
|
+
function verifyPaymentReceipt(receipt, recover = defaultRecover) {
|
|
899
|
+
if (typeof receipt !== "object" || receipt === null) return NOT_A_SIGNED_RECEIPT;
|
|
900
|
+
const source = receipt;
|
|
901
|
+
if (typeof source.authorization !== "object" || source.authorization === null) {
|
|
902
|
+
return NOT_A_SIGNED_RECEIPT;
|
|
903
|
+
}
|
|
904
|
+
const auth = source.authorization;
|
|
905
|
+
const { delegate, signHash: signHash2, signature, signatureScheme } = auth;
|
|
906
|
+
if (typeof delegate !== "string" || delegate === "" || typeof signHash2 !== "string" || signHash2 === "") {
|
|
907
|
+
return NOT_A_SIGNED_RECEIPT;
|
|
908
|
+
}
|
|
909
|
+
if (typeof signature !== "string" || signature === "") {
|
|
910
|
+
return { verified: false, reason: "missing_signature" };
|
|
911
|
+
}
|
|
912
|
+
const chainId = typeof source.onChain?.chainId === "number" ? source.onChain.chainId : void 0;
|
|
913
|
+
const digestVerifiable = chainId !== void 0 && DELEGATION_MANAGER_CHAIN_IDS.has(chainId);
|
|
914
|
+
if (signatureScheme === "eip712_userop") {
|
|
915
|
+
return { verified: false, reason: "not_verifiable_offline" };
|
|
916
|
+
}
|
|
917
|
+
if (signatureScheme === "eip712_delegation") {
|
|
918
|
+
if (!digestVerifiable) {
|
|
919
|
+
return { verified: false, reason: "not_verifiable_offline" };
|
|
920
|
+
}
|
|
921
|
+
let digest;
|
|
922
|
+
try {
|
|
923
|
+
digest = delegationDigest(signHash2, chainId);
|
|
924
|
+
} catch {
|
|
925
|
+
return { verified: false, reason: "bad_signature" };
|
|
926
|
+
}
|
|
927
|
+
return finish(match(delegate, digest, signature, recover), "delegation_digest");
|
|
928
|
+
}
|
|
929
|
+
const raw = match(delegate, signHash2, signature, recover);
|
|
930
|
+
if (raw.ok) return finish(raw, "sign_hash");
|
|
931
|
+
if (digestVerifiable) {
|
|
932
|
+
let digest;
|
|
933
|
+
try {
|
|
934
|
+
digest = delegationDigest(signHash2, chainId);
|
|
935
|
+
} catch {
|
|
936
|
+
return finish(raw, "sign_hash");
|
|
937
|
+
}
|
|
938
|
+
const delegated = match(delegate, digest, signature, recover);
|
|
939
|
+
if (delegated.ok) return finish(delegated, "delegation_digest");
|
|
940
|
+
}
|
|
941
|
+
return finish(raw, "sign_hash");
|
|
857
942
|
}
|
|
858
943
|
|
|
859
944
|
// src/account-reads.ts
|
|
@@ -879,6 +964,14 @@ function formatRemainingDisplay(tokenAddress, tokenSymbol, remainingAtomic) {
|
|
|
879
964
|
const token = resolveTokenFromAddress(tokenAddress);
|
|
880
965
|
return token ? `${formatAtomicAmount(safeBigInt(remainingAtomic), token.decimals)} ${tokenSymbol}` : `${remainingAtomic} ${tokenSymbol} (atomic; unknown decimals)`;
|
|
881
966
|
}
|
|
967
|
+
function formatTokenAmount(atomic, decimals) {
|
|
968
|
+
const value = safeBigInt(atomic);
|
|
969
|
+
if (value < 0n) return "0";
|
|
970
|
+
const digits = value.toString().padStart(decimals + 1, "0");
|
|
971
|
+
const intPart = digits.slice(0, digits.length - decimals) || "0";
|
|
972
|
+
const fracPart = digits.slice(digits.length - decimals).replace(/0+$/, "");
|
|
973
|
+
return fracPart ? `${intPart}.${fracPart}` : intPart;
|
|
974
|
+
}
|
|
882
975
|
function mapTaskBudget(raw) {
|
|
883
976
|
return {
|
|
884
977
|
id: raw.id,
|
|
@@ -912,6 +1005,13 @@ function summarizeTaskBudget(taskBudget) {
|
|
|
912
1005
|
expiresAt: taskBudget.expiresAt
|
|
913
1006
|
};
|
|
914
1007
|
}
|
|
1008
|
+
function compactReceipt(receipt) {
|
|
1009
|
+
const row = { ...receipt };
|
|
1010
|
+
delete row.challengePayload;
|
|
1011
|
+
delete row.selectedPayment;
|
|
1012
|
+
delete row.protocolReceiptPayload;
|
|
1013
|
+
return row;
|
|
1014
|
+
}
|
|
915
1015
|
var AccountReads = class {
|
|
916
1016
|
transport;
|
|
917
1017
|
getPaymentStatus;
|
|
@@ -1062,22 +1162,22 @@ var AccountReads = class {
|
|
|
1062
1162
|
try {
|
|
1063
1163
|
const tokenAddress = payment.asset ?? payment.x402?.asset ?? null;
|
|
1064
1164
|
if (!tokenAddress) return unavailable("the settled payment does not carry a resolvable token address", payment);
|
|
1065
|
-
const
|
|
1165
|
+
const match2 = allowanceResult.value.allowances.find(
|
|
1066
1166
|
(allowance) => allowance.tokenAddress.toLowerCase() === tokenAddress.toLowerCase()
|
|
1067
1167
|
);
|
|
1068
|
-
if (!
|
|
1069
|
-
const token = resolveTokenFromAddress(
|
|
1070
|
-
const remainingDisplay = token ? `${formatAtomicAmount(safeBigInt(
|
|
1168
|
+
if (!match2) return unavailable("no allowance/budget row matches the settled token", payment);
|
|
1169
|
+
const token = resolveTokenFromAddress(match2.tokenAddress);
|
|
1170
|
+
const remainingDisplay = token ? `${formatAtomicAmount(safeBigInt(match2.onchain.remaining), token.decimals)} ${match2.tokenSymbol}` : void 0;
|
|
1071
1171
|
const rail = agentResult.value.executionRail;
|
|
1072
1172
|
return {
|
|
1073
1173
|
payment,
|
|
1074
1174
|
allowance: {
|
|
1075
1175
|
rail,
|
|
1076
|
-
remaining_atomic:
|
|
1176
|
+
remaining_atomic: match2.onchain.remaining,
|
|
1077
1177
|
...remainingDisplay ? { remaining_display: remainingDisplay } : {},
|
|
1078
|
-
token_symbol:
|
|
1079
|
-
token_address:
|
|
1080
|
-
reset_period:
|
|
1178
|
+
token_symbol: match2.tokenSymbol,
|
|
1179
|
+
token_address: match2.tokenAddress,
|
|
1180
|
+
reset_period: match2.resetPeriodMin,
|
|
1081
1181
|
source: rail === "delegation" ? "active_delegations" : "allowance_module"
|
|
1082
1182
|
},
|
|
1083
1183
|
warnings: []
|
|
@@ -1097,8 +1197,13 @@ var AccountReads = class {
|
|
|
1097
1197
|
if (options.cursor) params.set("cursor", options.cursor);
|
|
1098
1198
|
const query = params.size > 0 ? `?${params.toString()}` : "";
|
|
1099
1199
|
const raw = await this.transport.get(`/machine-payments/receipts${query}`);
|
|
1200
|
+
const receipts = raw.receipts.map(mapPaymentReceipt);
|
|
1100
1201
|
return {
|
|
1101
|
-
|
|
1202
|
+
// #3423 (owner decision, opt-in): `compact` drops the three bulky
|
|
1203
|
+
// payload echoes (the merchant's full 402 challenge, the selected
|
|
1204
|
+
// option, and the merchant's PAYMENT-RESPONSE) from each row. The keys
|
|
1205
|
+
// are absent, not null, and the default shape is unchanged.
|
|
1206
|
+
receipts: options.compact ? receipts.map(compactReceipt) : receipts,
|
|
1102
1207
|
total: typeof raw.total === "number" ? raw.total : null,
|
|
1103
1208
|
hasMore: typeof raw.has_more === "boolean" ? raw.has_more : null,
|
|
1104
1209
|
nextCursor: typeof raw.next_cursor === "string" ? raw.next_cursor : null
|
|
@@ -1464,6 +1569,10 @@ function readX402ReceiptPayer(raw) {
|
|
|
1464
1569
|
}
|
|
1465
1570
|
|
|
1466
1571
|
// src/x402-funding-leg.ts
|
|
1572
|
+
function sameX402TaskBudget(a, b) {
|
|
1573
|
+
if (a === void 0 || b === void 0) return a === b;
|
|
1574
|
+
return a.toLowerCase() === b.toLowerCase();
|
|
1575
|
+
}
|
|
1467
1576
|
var X402FundingLeg = class {
|
|
1468
1577
|
delegateKey;
|
|
1469
1578
|
delegateAddress;
|
|
@@ -1489,20 +1598,35 @@ var X402FundingLeg = class {
|
|
|
1489
1598
|
this.assertSignableAuthorizationState = options.assertSignableAuthorizationState;
|
|
1490
1599
|
}
|
|
1491
1600
|
// ── Receipt cache ────────────────────────────────────────────────
|
|
1492
|
-
/**
|
|
1493
|
-
|
|
1601
|
+
/**
|
|
1602
|
+
* #3392: each cached receipt records the authorizing budget id(s) it was
|
|
1603
|
+
* created under (absent when none) — `taskBudgetId` since #3329, and since
|
|
1604
|
+
* #3330 `subBudgetId` (agent B's grant). A call naming a DIFFERENT
|
|
1605
|
+
* combination is refused here — before any network call — instead of
|
|
1606
|
+
* silently returning a receipt paid under another budget. Absent-vs-absent
|
|
1607
|
+
* and equal ids (case-insensitive) hit as before.
|
|
1608
|
+
*/
|
|
1609
|
+
cachedReceipt(idempotencyKey, taskBudgetId, subBudgetId) {
|
|
1494
1610
|
const cached = this.receiptCache.get(idempotencyKey);
|
|
1495
|
-
if (cached
|
|
1496
|
-
|
|
1611
|
+
if (!cached || cached.expiresAt <= Date.now()) return void 0;
|
|
1612
|
+
if (!sameX402TaskBudget(cached.taskBudgetId, taskBudgetId) || !sameX402TaskBudget(cached.subBudgetId, subBudgetId)) {
|
|
1613
|
+
throw new X402TaskBudgetMismatchError(
|
|
1614
|
+
`The x402 idempotency key '${idempotencyKey}' was already used under a different authorizing budget (task '${cached.taskBudgetId ?? "none"}' vs '${taskBudgetId ?? "none"}', sub '${cached.subBudgetId ?? "none"}' vs '${subBudgetId ?? "none"}'). Pass a new idempotencyKey to pay under this budget.`,
|
|
1615
|
+
idempotencyKey,
|
|
1616
|
+
cached.taskBudgetId,
|
|
1617
|
+
taskBudgetId
|
|
1618
|
+
);
|
|
1619
|
+
}
|
|
1620
|
+
return cached.receipt;
|
|
1497
1621
|
}
|
|
1498
|
-
cacheReceipt(idempotencyKey, paymentHeader, receipt) {
|
|
1622
|
+
cacheReceipt(idempotencyKey, paymentHeader, receipt, taskBudgetId, subBudgetId) {
|
|
1499
1623
|
const expiresAt = getPaymentHeaderValidBefore(paymentHeader);
|
|
1500
1624
|
if (expiresAt > Date.now()) {
|
|
1501
|
-
this.receiptCache.set(idempotencyKey, { expiresAt, receipt });
|
|
1625
|
+
this.receiptCache.set(idempotencyKey, { expiresAt, taskBudgetId, subBudgetId, receipt });
|
|
1502
1626
|
}
|
|
1503
1627
|
}
|
|
1504
1628
|
// ── Authorization ────────────────────────────────────────────────
|
|
1505
|
-
async authorize(paymentRequired, option, idempotencyKey, taskBudgetId) {
|
|
1629
|
+
async authorize(paymentRequired, option, idempotencyKey, taskBudgetId, subBudgetId) {
|
|
1506
1630
|
const raw = await this.post("/x402", {
|
|
1507
1631
|
url: paymentRequired.resource.url,
|
|
1508
1632
|
payTo: this.delegateAddress,
|
|
@@ -1518,7 +1642,12 @@ var X402FundingLeg = class {
|
|
|
1518
1642
|
settlementScheme: "eip3009",
|
|
1519
1643
|
// #3329: `/x402` bodies are camelCase (`X402AuthorizeRequest`) — do not
|
|
1520
1644
|
// switch this to `task_budget_id`, which is only the `POST /payments` key.
|
|
1521
|
-
...taskBudgetId ? { taskBudgetId } : {}
|
|
1645
|
+
...taskBudgetId ? { taskBudgetId } : {},
|
|
1646
|
+
// #3330: same camelCase channel one level deeper — agent B pays through
|
|
1647
|
+
// the sub-budget agent A granted it (`[funding, B grant, A parent-child,
|
|
1648
|
+
// A budget]`). Mutually exclusive with `taskBudgetId` server-side; the
|
|
1649
|
+
// backend refuses a body naming both.
|
|
1650
|
+
...subBudgetId ? { subBudgetId } : {}
|
|
1522
1651
|
});
|
|
1523
1652
|
const state = paymentStateFromRaw("x402 payment", raw);
|
|
1524
1653
|
const executedReplay = raw.success && raw.tx_hash ? "idempotency-collision" : state?.nextAction === AgentPaymentNextAction.RetryOriginalX402Request ? "approval-resume" : null;
|
|
@@ -1541,12 +1670,12 @@ var X402FundingLeg = class {
|
|
|
1541
1670
|
const paymentHeader = await this.createPaymentHeader(paymentRequired, option);
|
|
1542
1671
|
if (raw.success && raw.tx_hash) {
|
|
1543
1672
|
const receipt2 = this.receiptFromAuthorization(paymentRequired, option, paymentHeader, raw);
|
|
1544
|
-
this.cacheReceipt(idempotencyKey, paymentHeader, receipt2);
|
|
1673
|
+
this.cacheReceipt(idempotencyKey, paymentHeader, receipt2, taskBudgetId, subBudgetId);
|
|
1545
1674
|
return receipt2;
|
|
1546
1675
|
}
|
|
1547
1676
|
if (state?.nextAction === AgentPaymentNextAction.RetryOriginalX402Request) {
|
|
1548
1677
|
const receipt2 = this.receiptFromStatus(paymentRequired, option, paymentHeader, state);
|
|
1549
|
-
this.cacheReceipt(idempotencyKey, paymentHeader, receipt2);
|
|
1678
|
+
this.cacheReceipt(idempotencyKey, paymentHeader, receipt2, taskBudgetId, subBudgetId);
|
|
1550
1679
|
return receipt2;
|
|
1551
1680
|
}
|
|
1552
1681
|
this.assertSignableAuthorizationState("x402 payment", raw);
|
|
@@ -1569,7 +1698,7 @@ var X402FundingLeg = class {
|
|
|
1569
1698
|
execResult.chain_id ?? chainIdFromNetwork(option.network)
|
|
1570
1699
|
);
|
|
1571
1700
|
const receipt = this.receiptFromAuthorization(paymentRequired, option, paymentHeader, raw, execResult);
|
|
1572
|
-
this.cacheReceipt(idempotencyKey, paymentHeader, receipt);
|
|
1701
|
+
this.cacheReceipt(idempotencyKey, paymentHeader, receipt, taskBudgetId, subBudgetId);
|
|
1573
1702
|
return receipt;
|
|
1574
1703
|
}
|
|
1575
1704
|
// ── Header minting ───────────────────────────────────────────────
|
|
@@ -1835,11 +1964,27 @@ var X402Erc7710 = class {
|
|
|
1835
1964
|
...options.mcpCallContext ? { mcpCallContext: options.mcpCallContext } : {},
|
|
1836
1965
|
// #3329: `/x402` bodies are camelCase (`X402AuthorizeRequest`) — do not
|
|
1837
1966
|
// switch this to `task_budget_id`, which is only the `POST /payments` key.
|
|
1838
|
-
...options.taskBudgetId ? { taskBudgetId: options.taskBudgetId } : {}
|
|
1967
|
+
...options.taskBudgetId ? { taskBudgetId: options.taskBudgetId } : {},
|
|
1968
|
+
// #3330: same camelCase channel one level deeper; mutually exclusive
|
|
1969
|
+
// with `taskBudgetId` server-side.
|
|
1970
|
+
...options.subBudgetId ? { subBudgetId: options.subBudgetId } : {}
|
|
1839
1971
|
});
|
|
1840
1972
|
if (!raw.payment_id) {
|
|
1841
1973
|
throw new HavenApiError("No payment_id returned from x402/authorize", 500, raw);
|
|
1842
1974
|
}
|
|
1975
|
+
if (raw.status === "confirmed" && raw.tx_hash) {
|
|
1976
|
+
const requestedUrl = options.resourceUrl ?? paymentRequired.resource?.url;
|
|
1977
|
+
const samePurchase = typeof raw.to === "string" && raw.to.toLowerCase() === merchantPayTo.toLowerCase() && raw.resource_url === requestedUrl;
|
|
1978
|
+
if (!samePurchase) {
|
|
1979
|
+
throw new HavenApiError(
|
|
1980
|
+
`Idempotency key already belongs to confirmed payment ${raw.payment_id}, which is not this erc7710 purchase (different payee or resource). Nothing was signed and nothing was charged. Check that payment's status before buying again: a funded but undelivered EIP-3009 payment is recovered, not paid twice. Otherwise use a new idempotency key for this purchase.`,
|
|
1981
|
+
409,
|
|
1982
|
+
raw,
|
|
1983
|
+
raw.payment_id
|
|
1984
|
+
);
|
|
1985
|
+
}
|
|
1986
|
+
throw new X402Erc7710AlreadySettledError(raw.payment_id, raw.tx_hash, raw);
|
|
1987
|
+
}
|
|
1843
1988
|
const signData = raw.sign_data;
|
|
1844
1989
|
if (signData?.signature_scheme !== "eip712_delegation" || !signData.typed_data) {
|
|
1845
1990
|
throw new HavenApiError(
|
|
@@ -1870,10 +2015,20 @@ var X402Erc7710 = class {
|
|
|
1870
2015
|
* hosted boundary) is irrelevant here.
|
|
1871
2016
|
*/
|
|
1872
2017
|
async submit(paymentId, signature) {
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
{ signature }
|
|
1876
|
-
)
|
|
2018
|
+
let settled;
|
|
2019
|
+
try {
|
|
2020
|
+
settled = await this.post(`/x402/${paymentId}/settle`, { signature });
|
|
2021
|
+
} catch (err) {
|
|
2022
|
+
const body = err instanceof HavenApiError ? err.body : void 0;
|
|
2023
|
+
if (err instanceof HavenApiError && err.statusCode === 409 && body?.code === "payment_already_settled" && typeof body.tx_hash === "string") {
|
|
2024
|
+
throw new X402Erc7710AlreadySettledError(
|
|
2025
|
+
typeof body.payment_id === "string" ? body.payment_id : paymentId,
|
|
2026
|
+
body.tx_hash,
|
|
2027
|
+
body
|
|
2028
|
+
);
|
|
2029
|
+
}
|
|
2030
|
+
throw err;
|
|
2031
|
+
}
|
|
1877
2032
|
if (!settled.payment_header) {
|
|
1878
2033
|
throw new HavenApiError(
|
|
1879
2034
|
"x402 settle returned no payment_header \u2014 the merchant cannot be retried.",
|
|
@@ -2436,6 +2591,15 @@ var HavenClient = class {
|
|
|
2436
2591
|
pollingInterval;
|
|
2437
2592
|
chainRpcs;
|
|
2438
2593
|
inFlightX402 = /* @__PURE__ */ new Map();
|
|
2594
|
+
/**
|
|
2595
|
+
* #3392: the task budget each in-flight x402 entry was created under
|
|
2596
|
+
* (`undefined` = none). A call joining an in-flight entry under a
|
|
2597
|
+
* different budget is refused before any network call — never silently
|
|
2598
|
+
* charged to another budget's payment.
|
|
2599
|
+
*/
|
|
2600
|
+
inFlightX402TaskBudget = /* @__PURE__ */ new Map();
|
|
2601
|
+
/** #3330: the sub-budget each in-flight entry was created under, same pin as `inFlightX402TaskBudget`. */
|
|
2602
|
+
inFlightX402SubBudget = /* @__PURE__ */ new Map();
|
|
2439
2603
|
/**
|
|
2440
2604
|
* The EIP-3009 funding-leg lifecycle (#1618). The facade holds a reference
|
|
2441
2605
|
* and delegates; it does not reimplement any of it.
|
|
@@ -2559,7 +2723,10 @@ var HavenClient = class {
|
|
|
2559
2723
|
amount: request.amount,
|
|
2560
2724
|
to: request.to,
|
|
2561
2725
|
...request.idempotencyKey ? { idempotency_key: request.idempotencyKey } : {},
|
|
2562
|
-
...request.taskBudgetId ? { task_budget_id: request.taskBudgetId } : {}
|
|
2726
|
+
...request.taskBudgetId ? { task_budget_id: request.taskBudgetId } : {},
|
|
2727
|
+
// #3330: `POST /payments` is snake_case — agent B pays through the
|
|
2728
|
+
// sub-budget A granted it; the backend refuses a body naming both ids.
|
|
2729
|
+
...request.subBudgetId ? { sub_budget_id: request.subBudgetId } : {}
|
|
2563
2730
|
});
|
|
2564
2731
|
if (raw.status === "pending_approval") {
|
|
2565
2732
|
throwPaymentStateError("Payment", raw);
|
|
@@ -2623,7 +2790,10 @@ var HavenClient = class {
|
|
|
2623
2790
|
// #3329: `/x402` bodies are camelCase (`X402AuthorizeRequest`), unlike
|
|
2624
2791
|
// `POST /payments`'s snake_case `task_budget_id` above — the two
|
|
2625
2792
|
// surfaces use different conventions, so this key is NOT `task_budget_id`.
|
|
2626
|
-
...options.taskBudgetId ? { taskBudgetId: options.taskBudgetId } : {}
|
|
2793
|
+
...options.taskBudgetId ? { taskBudgetId: options.taskBudgetId } : {},
|
|
2794
|
+
// #3330: same camelCase channel one level deeper (`X402AuthorizeRequest`);
|
|
2795
|
+
// mutually exclusive with `taskBudgetId` server-side.
|
|
2796
|
+
...options.subBudgetId ? { subBudgetId: options.subBudgetId } : {}
|
|
2627
2797
|
});
|
|
2628
2798
|
if (raw.status !== "pending_signature") {
|
|
2629
2799
|
throwPaymentStateError("x402 payment", raw);
|
|
@@ -3003,7 +3173,12 @@ var HavenClient = class {
|
|
|
3003
3173
|
async listReceipts(options = {}) {
|
|
3004
3174
|
return this.accountReads.listReceipts(options);
|
|
3005
3175
|
}
|
|
3006
|
-
/**
|
|
3176
|
+
/**
|
|
3177
|
+
* One page of receipts with `total`, `hasMore` and `nextCursor` (#3128).
|
|
3178
|
+
* `compact: true` (#3423) drops each row's
|
|
3179
|
+
* `challengePayload`, `selectedPayment` and `protocolReceiptPayload`; the
|
|
3180
|
+
* default shape is unchanged.
|
|
3181
|
+
*/
|
|
3007
3182
|
async listReceiptsPage(options = {}) {
|
|
3008
3183
|
return this.accountReads.listReceiptsPage(options);
|
|
3009
3184
|
}
|
|
@@ -3154,12 +3329,30 @@ var HavenClient = class {
|
|
|
3154
3329
|
throw noCompatiblePaymentOptionError(paymentRequired.accepts);
|
|
3155
3330
|
}
|
|
3156
3331
|
const idempotencyKey = options.idempotencyKey ?? buildX402IdempotencyKey(paymentRequired, option);
|
|
3157
|
-
const cached = this.fundingLeg.cachedReceipt(idempotencyKey);
|
|
3332
|
+
const cached = this.fundingLeg.cachedReceipt(idempotencyKey, options.taskBudgetId, options.subBudgetId);
|
|
3158
3333
|
if (cached) return cached;
|
|
3159
3334
|
const inFlight = this.inFlightX402.get(idempotencyKey);
|
|
3160
|
-
if (inFlight)
|
|
3161
|
-
|
|
3335
|
+
if (inFlight) {
|
|
3336
|
+
if (!sameX402TaskBudget(this.inFlightX402TaskBudget.get(idempotencyKey), options.taskBudgetId) || !sameX402TaskBudget(this.inFlightX402SubBudget.get(idempotencyKey), options.subBudgetId)) {
|
|
3337
|
+
throw new X402TaskBudgetMismatchError(
|
|
3338
|
+
`The x402 idempotency key '${idempotencyKey}' has an in-flight payment created under a different authorizing budget (task '${this.inFlightX402TaskBudget.get(idempotencyKey) ?? "none"}' vs '${options.taskBudgetId ?? "none"}', sub '${this.inFlightX402SubBudget.get(idempotencyKey) ?? "none"}' vs '${options.subBudgetId ?? "none"}'). Pass a new idempotencyKey to pay under this budget.`,
|
|
3339
|
+
idempotencyKey,
|
|
3340
|
+
this.inFlightX402TaskBudget.get(idempotencyKey),
|
|
3341
|
+
options.taskBudgetId
|
|
3342
|
+
);
|
|
3343
|
+
}
|
|
3344
|
+
return inFlight;
|
|
3345
|
+
}
|
|
3346
|
+
const promise = this.fundingLeg.authorize(
|
|
3347
|
+
paymentRequired,
|
|
3348
|
+
option,
|
|
3349
|
+
idempotencyKey,
|
|
3350
|
+
options.taskBudgetId,
|
|
3351
|
+
options.subBudgetId
|
|
3352
|
+
);
|
|
3162
3353
|
this.inFlightX402.set(idempotencyKey, promise);
|
|
3354
|
+
this.inFlightX402TaskBudget.set(idempotencyKey, options.taskBudgetId);
|
|
3355
|
+
this.inFlightX402SubBudget.set(idempotencyKey, options.subBudgetId);
|
|
3163
3356
|
try {
|
|
3164
3357
|
return await promise;
|
|
3165
3358
|
} catch (err) {
|
|
@@ -3171,6 +3364,8 @@ var HavenClient = class {
|
|
|
3171
3364
|
throw err;
|
|
3172
3365
|
} finally {
|
|
3173
3366
|
this.inFlightX402.delete(idempotencyKey);
|
|
3367
|
+
this.inFlightX402TaskBudget.delete(idempotencyKey);
|
|
3368
|
+
this.inFlightX402SubBudget.delete(idempotencyKey);
|
|
3174
3369
|
}
|
|
3175
3370
|
}
|
|
3176
3371
|
/**
|
|
@@ -3306,7 +3501,7 @@ var HavenClient = class {
|
|
|
3306
3501
|
throw noCompatiblePaymentOptionError(input.paymentRequired.accepts);
|
|
3307
3502
|
}
|
|
3308
3503
|
const idempotencyKey = input.idempotencyKey ?? buildX402IdempotencyKey(input.paymentRequired, option);
|
|
3309
|
-
const cached = this.fundingLeg.cachedReceipt(idempotencyKey);
|
|
3504
|
+
const cached = this.fundingLeg.cachedReceipt(idempotencyKey, input.taskBudgetId);
|
|
3310
3505
|
if (cached) return cached;
|
|
3311
3506
|
const status = await this.getPaymentStatus(input.paymentId);
|
|
3312
3507
|
assertCanResumeX402(status, input.paymentRequired, option);
|
|
@@ -3324,7 +3519,7 @@ var HavenClient = class {
|
|
|
3324
3519
|
}
|
|
3325
3520
|
const paymentHeader = await this.fundingLeg.createPaymentHeader(input.paymentRequired, option);
|
|
3326
3521
|
const receipt = this.fundingLeg.receiptFromStatus(input.paymentRequired, option, paymentHeader, status);
|
|
3327
|
-
this.fundingLeg.cacheReceipt(idempotencyKey, paymentHeader, receipt);
|
|
3522
|
+
this.fundingLeg.cacheReceipt(idempotencyKey, paymentHeader, receipt, input.taskBudgetId);
|
|
3328
3523
|
return receipt;
|
|
3329
3524
|
}
|
|
3330
3525
|
async resumeX402Payment(input) {
|
|
@@ -3751,7 +3946,7 @@ var toolDescriptions = {
|
|
|
3751
3946
|
// deleted `/demo/mpp/*` route. Use the x402 fragments above instead.
|
|
3752
3947
|
getPaymentStatus: {
|
|
3753
3948
|
summary: "Fetch structured Haven payment status for agent recovery.",
|
|
3754
|
-
behavior: "State: phase, nextAction, rail, amount, merchant, resource, idempotency, message; parties: treasury/delegate/delegateAccount/merchant. awaiting_settlement_evidence: poll once, else unverified.",
|
|
3949
|
+
behavior: "State: phase, nextAction, rail, amount, merchant, resource, idempotency, message; delivered when the merchant answered; parties: treasury/delegate/delegateAccount/merchant. awaiting_settlement_evidence: poll once, else unverified. delivered_unverified: terminal, stop.",
|
|
3755
3950
|
nextActionGuidance: ""
|
|
3756
3951
|
},
|
|
3757
3952
|
getResumeState: {
|
|
@@ -3786,9 +3981,9 @@ var toolDescriptions = {
|
|
|
3786
3981
|
nextActionGuidance: ""
|
|
3787
3982
|
},
|
|
3788
3983
|
verifyReceipt: {
|
|
3789
|
-
summary: "Verify a payment receipt offline \u2014 confirm the agent
|
|
3790
|
-
selectionGuidance: "
|
|
3791
|
-
behavior: "
|
|
3984
|
+
summary: "Verify a signed payment receipt offline \u2014 confirm the agent delegate signed its authorisation.",
|
|
3985
|
+
selectionGuidance: "Takes the signed bundle from GET /payments/{id}/receipt, not a haven_list_receipts row \u2014 a row carries no signature and returns not_a_signed_receipt. No network.",
|
|
3986
|
+
behavior: "Never throws. An erc7710 bundle verifies over the delegation digest the delegate signed; a direct or eip3009 bundle returns not_verifiable_offline. verified true means only that the delegate signed the hash named by verifiedOver \u2014 payment facts are Haven-asserted and settlement is not proven; check settlementTxHash on an explorer.",
|
|
3792
3987
|
nextActionGuidance: ""
|
|
3793
3988
|
},
|
|
3794
3989
|
payMcpTool: {
|
|
@@ -4075,7 +4270,7 @@ Your user gives you a **budget on their own account** \u2014 not their wallet, a
|
|
|
4075
4270
|
|
|
4076
4271
|
Four of the six steps are your user's \u2014 each needs a human signature or a human decision. The rest is yours. If they already have a funded account, start at step 3.
|
|
4077
4272
|
|
|
4078
|
-
1. **HUMAN \u2014 create the account.** Name, email, password, then a passkey (Face ID / Touch ID) or a wallet. Never offer to enter any of it: you must not have their password, and the passkey is bound to their device. With a terminal,
|
|
4273
|
+
1. **HUMAN \u2014 create the account.** Name, email, password, then a passkey (Face ID / Touch ID) or a wallet. Never offer to enter any of it: you must not have their password, and the passkey is bound to their device. With a terminal, \`<packages.cli.one_liner> login --api <api-url>\` afterwards gets you a scoped session for steps 3-4 (that flag or \`HAVEN_API_URL\`, on the first command \u2014 the session then remembers the backend; **the CLI's built-in default is Haven's hosted production backend**, so on any other deployment an omitted flag connects you somewhere real and wrong rather than failing) \u2014 they approve a code in the browser, you never hold their password. That command is the manifest's \`packages.cli.one_liner\` (\`/.well-known/haven.json\`) \u2014 run it as given: \`packages.cli.channel\` serves the full spec. Do not hold the process open while you wait: under \`--json\`, pass \`--no-wait\` to get the link object back at once, then poll it with \`haven login --poll <device_code>\` \u2014 one round per invocation, exit 3 while it is still pending, 0 once approved. It can set up agents and read the account; it cannot sign, approve a budget, move funds, or rotate any agent's keys.
|
|
4079
4274
|
2. **HUMAN \u2014 fund it.** USDC only, no ETH: Haven sponsors the gas. \`/.well-known/haven.json\` names \`chains.default\` as the deployment's expected chain, but after login \`haven wallets funding\` prints the address, the amount and which chain in one place; confirm that chain before you message your user. Without a CLI session, the dashboard's funding card shows the address and amount and its Receive-funds screen names the chain; never assume one: a testnet deployment and production both call themselves Haven. Before you write that message, read the manifest: \`environment\` says whether this deployment is \`production\`, and each \`chains.supported\` entry says whether that chain is a \`testnet\`. Real money is at stake only on a non-testnet chain of a \`production\` deployment \u2014 tell your user which case theirs is.
|
|
4080
4275
|
3. **HUMAN \u2014 create the agent, set its budget**, and paste you the **setup prompt** it hands back. With a CLI session (step 1) you can do this step yourself: \`haven agents connect --name <n> --budget <amount> --token USDC --period <minutes>\` prints the same connector command and approval link; add \`--run\` to do step 4 too.
|
|
4081
4276
|
4. **YOU \u2014 run the connector command** in that prompt (below). It makes your signing key locally, registering only the public address.
|
|
@@ -4153,7 +4348,7 @@ Nothing here needs you to. Steps 1-3 are links: hand your user the full \`<host>
|
|
|
4153
4348
|
|
|
4154
4349
|
## If something breaks
|
|
4155
4350
|
|
|
4156
|
-
If a Haven result carries \`client_update\`, that client is out of date:
|
|
4351
|
+
If a Haven result carries \`client_update\`, that client is out of date: run its \`upgrade_command\` as given, then any repair line it prints, then retry. \`required: true\` means payments are refused until you do. What changed: [/releases](/releases).
|
|
4157
4352
|
|
|
4158
4353
|
## Vocabulary
|
|
4159
4354
|
|
|
@@ -4173,12 +4368,12 @@ Start by reading {{HAVEN_ORIGIN}}/for-agents.md \u2014 it is written for you and
|
|
|
4173
4368
|
|
|
4174
4369
|
Then:
|
|
4175
4370
|
|
|
4176
|
-
1. Run
|
|
4371
|
+
1. Run \`<packages.cli.one_liner> login\`. That command is the manifest's \`packages.cli.one_liner\` (\`/.well-known/haven.json\`) \u2014 run it as given: \`packages.cli.channel\` serves the full spec. It prints a link and a code and does not need my password \u2014 it must never ask for it. Give me the link straight away and wait for me to approve it in my browser.
|
|
4177
4372
|
2. Once I have approved, run \`haven agents connect --name <a name you choose> --budget <amount> --token USDC --period <minutes>\` with the budget I tell you. If I have not given you one, ask me before running it. Add \`--run\` to complete the connection in the same step.
|
|
4178
4373
|
3. ${AGENT_APPROVAL_RELAY_JSON_SENTENCE}
|
|
4179
4374
|
4. Once I have approved the budget, verify with the \`haven_get_agent\` tool: \`ready\` means you can pay, \`needs_approval\` means my approval has not landed yet.
|
|
4180
4375
|
|
|
4181
|
-
Two things only I can do: approving that budget with my passkey, and funding the account with USDC
|
|
4376
|
+
Two things only I can do: approving that budget with my passkey, and funding the account with USDC \u2014 no ETH, Haven sponsors the gas. Tell me if either is missing rather than working around it.
|
|
4182
4377
|
|
|
4183
4378
|
${AGENT_SECRET_HYGIENE_SENTENCE}`;
|
|
4184
4379
|
|
|
@@ -4322,28 +4517,43 @@ the budget in the Haven dashboard, or wait for the period reset.
|
|
|
4322
4517
|
4. Then FOLLOW THE RESPONSE'S GUIDANCE FIELDS: \`next_action\`, \`next_tool\`,
|
|
4323
4518
|
and \`next_arguments\` name the exact next call \u2014 act on those first; the
|
|
4324
4519
|
prose in this section is fallback and debugging detail. If the catalog
|
|
4325
|
-
|
|
4326
|
-
\`
|
|
4327
|
-
the
|
|
4520
|
+
row is a plain-HTTP x402 paywall (no MCP tool metadata), the response
|
|
4521
|
+
instead names \`mcp__haven__haven_quote_x402\` with the entry's resource
|
|
4522
|
+
URL as \`url\`. If an MCP row is degraded or has no tool name, the response
|
|
4523
|
+
instead names \`mcp__haven__haven_pay_mcp_tool\` (merchant URL, tool name,
|
|
4524
|
+
arguments) as the manual fallback.
|
|
4328
4525
|
|
|
4329
|
-
**Signing:**
|
|
4526
|
+
**Signing:** which signer tool to call depends on the settlement scheme the
|
|
4527
|
+
prepare/pay response already named as \`next_tool\` \u2014 follow that field, never
|
|
4528
|
+
hard-code a choice. **erc7710** (direct settlement \u2014 chosen per merchant when
|
|
4529
|
+
its 402 advertises \`assetTransferMethod: "erc7710"\` AND your account is on
|
|
4530
|
+
the delegation rail; never a blanket default): \`mcp__haven-signer__haven_sign\`
|
|
4531
|
+
with \`payment_id\` ONLY; the signer fetches the settlement child itself.
|
|
4532
|
+
**EIP-3009** (the bridge \u2014 used otherwise, whenever the merchant offers a
|
|
4533
|
+
standard entry): \`mcp__haven-signer__haven_sign_x402\` with \`payment_id\` ONLY \u2014
|
|
4330
4534
|
the local signer fetches the exact signing bytes AND \`payment_required\`
|
|
4331
|
-
itself, so never relay \`typed_data\` or the 402 blob yourself.
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4535
|
+
itself, so never relay \`typed_data\` or the 402 blob yourself. A merchant
|
|
4536
|
+
that advertises ONLY erc7710 against an account that is NOT on the
|
|
4537
|
+
delegation rail is refused outright \u2014 it does not fall back to EIP-3009. If
|
|
4538
|
+
the signer reports its fetched context carried no \`payment_required\` (older
|
|
4539
|
+
backend), re-call with \`payment_required\` added verbatim. Fallback for an
|
|
4540
|
+
older signer or backend: re-run the quote/prepare tool with the SAME
|
|
4541
|
+
\`idempotency_key\` plus \`include_signing_payload=true\`, then pass
|
|
4542
|
+
\`payload_hash\`, \`x402_expected\` (the nested \`x402.expected\` object), and
|
|
4337
4543
|
\`typed_data\`/\`typed_data_b64\` through unchanged.
|
|
4338
4544
|
|
|
4339
|
-
**Settle:** \`mcp__haven__haven_settle_mcp_tool\` with \`payment_id
|
|
4340
|
-
\`signature
|
|
4545
|
+
**Settle:** \`mcp__haven__haven_settle_mcp_tool\` with \`payment_id\` and
|
|
4546
|
+
\`signature\` always. On **erc7710**, pass no \`payment_header\` \u2014 Haven
|
|
4547
|
+
assembles it at settle, so there is nothing to build locally and no funding
|
|
4548
|
+
transaction to wait for. On **EIP-3009**, also pass \`payment_header\` (from
|
|
4549
|
+
\`haven_sign_x402\`) ONLY. Either way Haven rehydrates the merchant call
|
|
4341
4550
|
context (\`merchant_url\`, \`tool_name\`, \`arguments\`, \`mcp_transport\`)
|
|
4342
4551
|
server-side from \`payment_id\`. Pass those four fields explicitly only as a
|
|
4343
4552
|
version-skew fallback when Haven has no stored context for the id \u2014 both or
|
|
4344
|
-
none together, never just one.
|
|
4345
|
-
funding has not confirmed
|
|
4346
|
-
|
|
4553
|
+
none together, never just one. On **EIP-3009**, \`settled: false\` means
|
|
4554
|
+
funding has not confirmed; on **erc7710** it means the merchant delivered but
|
|
4555
|
+
settlement is not yet verified. Either way, follow the result's guidance
|
|
4556
|
+
fields and do not re-pay.
|
|
4347
4557
|
|
|
4348
4558
|
Step-by-step alternative (also key-safe; for an older signer or backend, or
|
|
4349
4559
|
when you already have a merchant URL and tool name instead of a
|
|
@@ -4533,6 +4743,6 @@ function sameUrl(a, b) {
|
|
|
4533
4743
|
}
|
|
4534
4744
|
}
|
|
4535
4745
|
|
|
4536
|
-
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_README_SECTION_MD, AGENT_SECRET_HYGIENE_SENTENCE, AGENT_WIRING_COLLISION_RELAY_SENTENCE, DISCOVERY_MAX_BYTES, HAVEN_AGENT_RUNBOOK_MD, HAVEN_SKILL_BODY_MD, HAVEN_SKILL_MD, HavenClient, HavenInsecureRetryTargetError, INSECURE_RETRY_TARGET_CODE, MERCHANT_DISCOVERY_PATHS, RECEIPT_VERSION, SKILL_FOLDER_NAME, addressFromKey, assertSecureX402RetryTarget, composeDescription, discoverMerchantMcpUrl, havenTools, isSecureX402RetryTarget, isZeroSettlementTxHash, readX402ReceiptPayer, resolveX402RetryTarget, sameUrl, signHash, signUserOpTypedDataForDelegation, toolDescriptions, verifyPaymentReceipt, verifySignature };
|
|
4746
|
+
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_README_SECTION_MD, AGENT_SECRET_HYGIENE_SENTENCE, AGENT_WIRING_COLLISION_RELAY_SENTENCE, DISCOVERY_MAX_BYTES, HAVEN_AGENT_RUNBOOK_MD, HAVEN_SKILL_BODY_MD, HAVEN_SKILL_MD, HavenClient, HavenInsecureRetryTargetError, INSECURE_RETRY_TARGET_CODE, MERCHANT_DISCOVERY_PATHS, RECEIPT_VERSION, SKILL_FOLDER_NAME, addressFromKey, assertSecureX402RetryTarget, composeDescription, discoverMerchantMcpUrl, formatTokenAmount, havenTools, isSecureX402RetryTarget, isZeroSettlementTxHash, readX402ReceiptPayer, resolveX402RetryTarget, sameUrl, signHash, signUserOpTypedDataForDelegation, toolDescriptions, verifyPaymentReceipt, verifySignature };
|
|
4537
4747
|
//# sourceMappingURL=index.js.map
|
|
4538
4748
|
//# sourceMappingURL=index.js.map
|