@haven_ai/sdk 0.5.0-alpha.1 → 0.6.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 +2 -1
- package/dist/{base64-BUMXmruY.d.cts → base64-cD9kR4JL.d.cts} +243 -16
- package/dist/{base64-BUMXmruY.d.ts → base64-cD9kR4JL.d.ts} +243 -16
- package/dist/{chunk-KQ7JXKTV.js → chunk-3WSEX3GW.js} +315 -6
- package/dist/chunk-3WSEX3GW.js.map +1 -0
- package/dist/{chunk-GLNG2GF7.js → chunk-63GPTEEY.js} +42 -6
- package/dist/chunk-63GPTEEY.js.map +1 -0
- package/dist/{chunk-QSSVOPWA.cjs → chunk-CK4NLLMO.cjs} +333 -17
- package/dist/chunk-CK4NLLMO.cjs.map +1 -0
- package/dist/{chunk-JKUIE45B.cjs → chunk-S42QSWJY.cjs} +42 -6
- package/dist/chunk-S42QSWJY.cjs.map +1 -0
- package/dist/edge.cjs +85 -57
- package/dist/edge.d.cts +24 -4
- package/dist/edge.d.ts +24 -4
- package/dist/edge.js +3 -3
- package/dist/index.cjs +485 -294
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +47 -3
- package/dist/index.d.ts +47 -3
- package/dist/index.js +192 -21
- 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-GLNG2GF7.js.map +0 -1
- package/dist/chunk-JKUIE45B.cjs.map +0 -1
- package/dist/chunk-KQ7JXKTV.js.map +0 -1
- package/dist/chunk-QSSVOPWA.cjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { selectStandardPaymentOption, buildX402IdempotencyKey, x402AuthorizationAmount, assertBoundDirectPaymentUserOp, 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, 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, buildSweepAuthorizationMessage, buildSweepTypedData, buildX402ExpectedMessage, compareNodeVersions, createNextStepBuilder, decodeBase64Json, decodeBase64Utf8, defaultNextToolFor, encodeBase64Json, encodeBase64Utf8, encodePaymentProof, havenClientIdentity, isErc7710Option, isSupportedNodeVersion, isSweepableChain, 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-
|
|
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-3WSEX3GW.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, resolveTokenFromAddress, selectErc7710PaymentOption, selectPaymentOption, selectStandardPaymentOption, selectX402SettlementScheme, sweepUsdcAddress, sweepUsdcDomain, toStandardPaymentRequirements, unsupportedNodeVersionMessage, validateStandardX402PaymentHeader, x402AssetTransferMethod, x402AuthorizationAmount, x402FacilitatorAddresses, x402V2PaymentEnvelope } from './chunk-3WSEX3GW.js';
|
|
3
|
+
import { HavenSigningError, HavenError, DEFAULT_CONFIRMATION_TIMEOUT_MS, HavenApiError, assertUserOpTypedDataBinding, AgentPaymentRail, AgentPaymentPhase, HavenTimeoutError, X402UnexpectedStatusError, X402AlreadySettledError, AgentPaymentWarningCode, MerchantTimeoutError, AgentPaymentNextAction, HavenPaymentStateError, HavenZeroSettlementHashError, chainIdForNetwork } from './chunk-63GPTEEY.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, X402AlreadySettledError, X402UnexpectedStatusError, assertUserOpTypedDataBinding, connectorRerunCommand, connectorSpec, deriveDelegateAccountAddress, isConnectorChannel, isPackedUserOperationTypedData, packedUserOperationHash, resolveConnectorChannel, signerUpdateFallback, verifySettlementChild } from './chunk-63GPTEEY.js';
|
|
5
5
|
import { hashTypedData } from 'viem';
|
|
6
6
|
import { ethers } from 'ethers';
|
|
7
7
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
@@ -232,17 +232,13 @@ function mapPaymentReceipt(raw) {
|
|
|
232
232
|
id: raw.id,
|
|
233
233
|
paymentId: raw.payment_id,
|
|
234
234
|
source: raw.rail,
|
|
235
|
-
rail: raw.rail,
|
|
236
235
|
paymentProofStatus: raw.proof_status,
|
|
237
|
-
proofStatus: raw.proof_status,
|
|
238
236
|
txHash: raw.tx_hash,
|
|
239
237
|
fundingTxHash: raw.funding_tx_hash ?? null,
|
|
240
238
|
settlementTxHash: raw.settlement_tx_hash ?? null,
|
|
241
239
|
chainId: raw.chain_id,
|
|
242
240
|
x402ResourceUrl: raw.resource_url,
|
|
243
|
-
resourceUrl: raw.resource_url,
|
|
244
241
|
x402MerchantAddress: raw.merchant_address,
|
|
245
|
-
merchantAddress: raw.merchant_address,
|
|
246
242
|
payerAddress: raw.payer_address,
|
|
247
243
|
parties: mapParties(raw.parties),
|
|
248
244
|
settlementAddress: raw.settlement_address,
|
|
@@ -883,6 +879,39 @@ function formatRemainingDisplay(tokenAddress, tokenSymbol, remainingAtomic) {
|
|
|
883
879
|
const token = resolveTokenFromAddress(tokenAddress);
|
|
884
880
|
return token ? `${formatAtomicAmount(safeBigInt(remainingAtomic), token.decimals)} ${tokenSymbol}` : `${remainingAtomic} ${tokenSymbol} (atomic; unknown decimals)`;
|
|
885
881
|
}
|
|
882
|
+
function mapTaskBudget(raw) {
|
|
883
|
+
return {
|
|
884
|
+
id: raw.id,
|
|
885
|
+
agentId: raw.agent_id,
|
|
886
|
+
chainId: raw.chain_id,
|
|
887
|
+
tokenAddress: raw.token_address,
|
|
888
|
+
recipientAddress: raw.recipient_address,
|
|
889
|
+
parentDelegationHash: raw.parent_delegation_hash,
|
|
890
|
+
delegationHash: raw.delegation_hash,
|
|
891
|
+
label: raw.label,
|
|
892
|
+
maxAtomic: raw.max_atomic,
|
|
893
|
+
status: raw.status,
|
|
894
|
+
expiresAt: raw.expires_at,
|
|
895
|
+
isExpired: raw.is_expired,
|
|
896
|
+
createdAt: raw.created_at,
|
|
897
|
+
openedAt: raw.opened_at,
|
|
898
|
+
closedAt: raw.closed_at,
|
|
899
|
+
closeTxHash: raw.close_tx_hash
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
function summarizeTaskBudget(taskBudget) {
|
|
903
|
+
const token = resolveTokenFromAddress(taskBudget.tokenAddress);
|
|
904
|
+
const maxDisplay = token ? `${formatAtomicAmount(safeBigInt(taskBudget.maxAtomic), token.decimals)} ${token.symbol}` : `${taskBudget.maxAtomic} (atomic; unknown decimals)`;
|
|
905
|
+
return {
|
|
906
|
+
id: taskBudget.id,
|
|
907
|
+
label: taskBudget.label,
|
|
908
|
+
tokenAddress: taskBudget.tokenAddress,
|
|
909
|
+
maxAtomic: taskBudget.maxAtomic,
|
|
910
|
+
maxDisplay,
|
|
911
|
+
recipientAddress: taskBudget.recipientAddress,
|
|
912
|
+
expiresAt: taskBudget.expiresAt
|
|
913
|
+
};
|
|
914
|
+
}
|
|
886
915
|
var AccountReads = class {
|
|
887
916
|
transport;
|
|
888
917
|
getPaymentStatus;
|
|
@@ -902,7 +931,11 @@ var AccountReads = class {
|
|
|
902
931
|
return request;
|
|
903
932
|
}
|
|
904
933
|
async getAgentSummary() {
|
|
905
|
-
const [agent, allowanceSummary] = await Promise.all([
|
|
934
|
+
const [agent, allowanceSummary, taskBudgets] = await Promise.all([
|
|
935
|
+
this.getAgent(),
|
|
936
|
+
this.getAllowances(),
|
|
937
|
+
this.listOpenTaskBudgetsSummary()
|
|
938
|
+
]);
|
|
906
939
|
const allowances = allowanceSummary.allowances.map((allowance) => {
|
|
907
940
|
return {
|
|
908
941
|
id: allowance.id,
|
|
@@ -916,7 +949,30 @@ var AccountReads = class {
|
|
|
916
949
|
};
|
|
917
950
|
});
|
|
918
951
|
const readiness = deriveReadiness(agent.status, allowances);
|
|
919
|
-
return { ...agent, readiness, spend_authority_readiness: readiness, allowances };
|
|
952
|
+
return { ...agent, readiness, spend_authority_readiness: readiness, allowances, taskBudgets };
|
|
953
|
+
}
|
|
954
|
+
/**
|
|
955
|
+
* #3329: `GET /task-budgets?status=open`, mapped to the condensed summary
|
|
956
|
+
* row `getAgentSummary()` carries. Fails SOFT per the #3093 rule (default
|
|
957
|
+
* every array wire key to `[]` so an absent/malformed key degrades instead
|
|
958
|
+
* of taking the route down): a 404 (a backend that predates #3329's
|
|
959
|
+
* route), ANY other transport error, an undefined/null body, or a missing
|
|
960
|
+
* or non-array `task_budgets` key all return `[]` rather than throwing —
|
|
961
|
+
* `getAgentSummary`'s identity + readiness + allowances fields are what
|
|
962
|
+
* callers depend on, and this read must never be why the whole summary
|
|
963
|
+
* fails.
|
|
964
|
+
*/
|
|
965
|
+
async listOpenTaskBudgetsSummary() {
|
|
966
|
+
try {
|
|
967
|
+
const raw = await this.transport.get(
|
|
968
|
+
"/task-budgets?status=open"
|
|
969
|
+
);
|
|
970
|
+
const rows = raw?.task_budgets;
|
|
971
|
+
if (!Array.isArray(rows)) return [];
|
|
972
|
+
return rows.map((row) => summarizeTaskBudget(mapTaskBudget(row)));
|
|
973
|
+
} catch {
|
|
974
|
+
return [];
|
|
975
|
+
}
|
|
920
976
|
}
|
|
921
977
|
async getAllowances() {
|
|
922
978
|
const raw = await this.transport.get("/machine-payments/allowances");
|
|
@@ -1446,7 +1502,7 @@ var X402FundingLeg = class {
|
|
|
1446
1502
|
}
|
|
1447
1503
|
}
|
|
1448
1504
|
// ── Authorization ────────────────────────────────────────────────
|
|
1449
|
-
async authorize(paymentRequired, option, idempotencyKey) {
|
|
1505
|
+
async authorize(paymentRequired, option, idempotencyKey, taskBudgetId) {
|
|
1450
1506
|
const raw = await this.post("/x402", {
|
|
1451
1507
|
url: paymentRequired.resource.url,
|
|
1452
1508
|
payTo: this.delegateAddress,
|
|
@@ -1459,7 +1515,10 @@ var X402FundingLeg = class {
|
|
|
1459
1515
|
// #1360: same explicit funding-leg declaration as createX402Intent —
|
|
1460
1516
|
// this local-key path derives payTo from the key (never stale), but the
|
|
1461
1517
|
// declaration keeps both writers of the 3009 shape loud-by-default.
|
|
1462
|
-
settlementScheme: "eip3009"
|
|
1518
|
+
settlementScheme: "eip3009",
|
|
1519
|
+
// #3329: `/x402` bodies are camelCase (`X402AuthorizeRequest`) — do not
|
|
1520
|
+
// switch this to `task_budget_id`, which is only the `POST /payments` key.
|
|
1521
|
+
...taskBudgetId ? { taskBudgetId } : {}
|
|
1463
1522
|
});
|
|
1464
1523
|
const state = paymentStateFromRaw("x402 payment", raw);
|
|
1465
1524
|
const executedReplay = raw.success && raw.tx_hash ? "idempotency-collision" : state?.nextAction === AgentPaymentNextAction.RetryOriginalX402Request ? "approval-resume" : null;
|
|
@@ -1494,7 +1553,10 @@ var X402FundingLeg = class {
|
|
|
1494
1553
|
if (!raw.sign_data?.hash) {
|
|
1495
1554
|
throw new HavenApiError("No sign_hash returned from x402/authorize", 500, raw);
|
|
1496
1555
|
}
|
|
1497
|
-
const sig = await this.signForData(raw.sign_data
|
|
1556
|
+
const sig = await this.signForData(raw.sign_data, {
|
|
1557
|
+
asset: option.asset,
|
|
1558
|
+
amount: x402AuthorizationAmount(option)
|
|
1559
|
+
});
|
|
1498
1560
|
const execResult = await this.post(
|
|
1499
1561
|
`/payments/${raw.payment_id}/sign`,
|
|
1500
1562
|
{ signature: sig }
|
|
@@ -1770,7 +1832,10 @@ var X402Erc7710 = class {
|
|
|
1770
1832
|
...selection.facilitatorAddresses ? { facilitatorAddresses: selection.facilitatorAddresses } : {},
|
|
1771
1833
|
// #1307/#1547: persisted so the settle leg can rehydrate the merchant
|
|
1772
1834
|
// call by payment_id on this scheme too, not only on the 3009 bridge.
|
|
1773
|
-
...options.mcpCallContext ? { mcpCallContext: options.mcpCallContext } : {}
|
|
1835
|
+
...options.mcpCallContext ? { mcpCallContext: options.mcpCallContext } : {},
|
|
1836
|
+
// #3329: `/x402` bodies are camelCase (`X402AuthorizeRequest`) — do not
|
|
1837
|
+
// switch this to `task_budget_id`, which is only the `POST /payments` key.
|
|
1838
|
+
...options.taskBudgetId ? { taskBudgetId: options.taskBudgetId } : {}
|
|
1774
1839
|
});
|
|
1775
1840
|
if (!raw.payment_id) {
|
|
1776
1841
|
throw new HavenApiError("No payment_id returned from x402/authorize", 500, raw);
|
|
@@ -2416,7 +2481,7 @@ var HavenClient = class {
|
|
|
2416
2481
|
x402Wallet: this.x402Wallet,
|
|
2417
2482
|
chainRpcs: this.chainRpcs,
|
|
2418
2483
|
post: (path, body) => this.post(path, body),
|
|
2419
|
-
signForData: (signData) => this.signForData(signData),
|
|
2484
|
+
signForData: (signData, fundingExpectation) => this.signForData(signData, void 0, fundingExpectation),
|
|
2420
2485
|
assertSignableAuthorizationState: (label, raw) => this.throwIfNonSignableAuthorizationState(label, raw)
|
|
2421
2486
|
});
|
|
2422
2487
|
this.merchantCompletion = new MerchantCompletion({
|
|
@@ -2493,7 +2558,8 @@ var HavenClient = class {
|
|
|
2493
2558
|
token: request.token,
|
|
2494
2559
|
amount: request.amount,
|
|
2495
2560
|
to: request.to,
|
|
2496
|
-
...request.idempotencyKey ? { idempotency_key: request.idempotencyKey } : {}
|
|
2561
|
+
...request.idempotencyKey ? { idempotency_key: request.idempotencyKey } : {},
|
|
2562
|
+
...request.taskBudgetId ? { task_budget_id: request.taskBudgetId } : {}
|
|
2497
2563
|
});
|
|
2498
2564
|
if (raw.status === "pending_approval") {
|
|
2499
2565
|
throwPaymentStateError("Payment", raw);
|
|
@@ -2553,7 +2619,11 @@ var HavenClient = class {
|
|
|
2553
2619
|
// the agent passes only payment_id. Bounded: the backend rejects >64KB,
|
|
2554
2620
|
// so an oversized blob is omitted here (signer falls back to the
|
|
2555
2621
|
// caller-supplied copy) rather than failing the intent.
|
|
2556
|
-
...new TextEncoder().encode(JSON.stringify(paymentRequired)).length <= 65536 ? { paymentRequired } : {}
|
|
2622
|
+
...new TextEncoder().encode(JSON.stringify(paymentRequired)).length <= 65536 ? { paymentRequired } : {},
|
|
2623
|
+
// #3329: `/x402` bodies are camelCase (`X402AuthorizeRequest`), unlike
|
|
2624
|
+
// `POST /payments`'s snake_case `task_budget_id` above — the two
|
|
2625
|
+
// surfaces use different conventions, so this key is NOT `task_budget_id`.
|
|
2626
|
+
...options.taskBudgetId ? { taskBudgetId: options.taskBudgetId } : {}
|
|
2557
2627
|
});
|
|
2558
2628
|
if (raw.status !== "pending_signature") {
|
|
2559
2629
|
throwPaymentStateError("x402 payment", raw);
|
|
@@ -2625,7 +2695,7 @@ var HavenClient = class {
|
|
|
2625
2695
|
* (#834) — the backend refuses those intents with HTTP 410 before any
|
|
2626
2696
|
* sign_data reaches a client, so encountering it here is a hard error too.
|
|
2627
2697
|
*/
|
|
2628
|
-
async signForData(signData, settlementExpectation) {
|
|
2698
|
+
async signForData(signData, settlementExpectation, fundingExpectation) {
|
|
2629
2699
|
if (!this.delegateKey) {
|
|
2630
2700
|
throw new HavenSigningError(
|
|
2631
2701
|
"Cannot sign without a delegateKey. Pass the private key in HavenClient config, or sign externally."
|
|
@@ -2648,6 +2718,13 @@ var HavenClient = class {
|
|
|
2648
2718
|
signData.typed_data,
|
|
2649
2719
|
this.delegateAddress
|
|
2650
2720
|
);
|
|
2721
|
+
if (fundingExpectation) {
|
|
2722
|
+
assertFundingLegPaysDelegate(signData.typed_data, {
|
|
2723
|
+
delegateAddress: this.delegateAddress,
|
|
2724
|
+
asset: fundingExpectation.asset,
|
|
2725
|
+
amount: fundingExpectation.amount
|
|
2726
|
+
});
|
|
2727
|
+
}
|
|
2651
2728
|
return signUserOpTypedDataForDelegation(this.delegateKey, signData.typed_data);
|
|
2652
2729
|
}
|
|
2653
2730
|
if (scheme === "eip712_delegation") {
|
|
@@ -2939,6 +3016,96 @@ var HavenClient = class {
|
|
|
2939
3016
|
async getReceipt(paymentId) {
|
|
2940
3017
|
return this.accountReads.getReceipt(paymentId);
|
|
2941
3018
|
}
|
|
3019
|
+
// ── Task Budgets (#3329) ────────────────────────────────────────────
|
|
3020
|
+
/**
|
|
3021
|
+
* Open a task budget: a self-delegated, time-boxed slice of the agent's own
|
|
3022
|
+
* budget delegation. Returns the pending row plus the child delegation's
|
|
3023
|
+
* `sign_data` — sign it (via `signForData` / `haven_sign` with
|
|
3024
|
+
* `task_budget_id`) and pass the signature to `submitTaskBudget` to make it
|
|
3025
|
+
* OPEN. Nothing is on-chain until then.
|
|
3026
|
+
*/
|
|
3027
|
+
async openTaskBudget(request) {
|
|
3028
|
+
const raw = await this.post("/task-budgets", {
|
|
3029
|
+
...request.tokenAddress ? { token_address: request.tokenAddress } : {},
|
|
3030
|
+
max_amount_atomic: request.maxAmountAtomic,
|
|
3031
|
+
ttl_seconds: request.ttlSeconds,
|
|
3032
|
+
...request.recipientAddress ? { recipient_address: request.recipientAddress } : {},
|
|
3033
|
+
...request.label ? { label: request.label } : {}
|
|
3034
|
+
});
|
|
3035
|
+
return {
|
|
3036
|
+
taskBudget: mapTaskBudget(raw.task_budget),
|
|
3037
|
+
signData: raw.sign_data,
|
|
3038
|
+
nextAction: raw.next_action,
|
|
3039
|
+
instructions: raw.instructions
|
|
3040
|
+
};
|
|
3041
|
+
}
|
|
3042
|
+
/** Read one task budget by id. */
|
|
3043
|
+
async getTaskBudget(id) {
|
|
3044
|
+
const raw = await this.get(`/task-budgets/${encodeURIComponent(id)}`);
|
|
3045
|
+
return mapTaskBudget(raw.task_budget);
|
|
3046
|
+
}
|
|
3047
|
+
/** List this agent's task budgets — `status: 'open'` (default; unexpired only) or `'all'`. */
|
|
3048
|
+
async listTaskBudgets(options = {}) {
|
|
3049
|
+
const query = options.status ? `?status=${encodeURIComponent(options.status)}` : "";
|
|
3050
|
+
const raw = await this.get(`/task-budgets${query}`);
|
|
3051
|
+
return raw.task_budgets.map(mapTaskBudget);
|
|
3052
|
+
}
|
|
3053
|
+
/**
|
|
3054
|
+
* The exact signing payload for a pending (purpose `open`) or closing
|
|
3055
|
+
* (purpose `close`) task budget — the `task_budget_id` counterpart of
|
|
3056
|
+
* `getResumeState` / the payment sign-context reads. `haven_sign` with
|
|
3057
|
+
* `task_budget_id` fetches this itself; call it directly only for manual
|
|
3058
|
+
* signing.
|
|
3059
|
+
*/
|
|
3060
|
+
async getTaskBudgetSignContext(id) {
|
|
3061
|
+
const raw = await this.get(`/task-budgets/${encodeURIComponent(id)}/sign-context`);
|
|
3062
|
+
if (raw.purpose === "close") {
|
|
3063
|
+
return {
|
|
3064
|
+
taskBudgetId: raw.task_budget_id,
|
|
3065
|
+
purpose: "close",
|
|
3066
|
+
taskSignContextVersion: raw.task_sign_context_version,
|
|
3067
|
+
typedData: raw.typed_data,
|
|
3068
|
+
userOperation: raw.user_operation,
|
|
3069
|
+
userOpHash: raw.user_op_hash,
|
|
3070
|
+
expected: raw.expected
|
|
3071
|
+
};
|
|
3072
|
+
}
|
|
3073
|
+
return {
|
|
3074
|
+
taskBudgetId: raw.task_budget_id,
|
|
3075
|
+
purpose: "open",
|
|
3076
|
+
taskSignContextVersion: raw.task_sign_context_version,
|
|
3077
|
+
typedData: raw.typed_data,
|
|
3078
|
+
expected: raw.expected
|
|
3079
|
+
};
|
|
3080
|
+
}
|
|
3081
|
+
/** Submit a signature for a pending (open) or closing task budget. */
|
|
3082
|
+
async submitTaskBudget(id, signature) {
|
|
3083
|
+
const raw = await this.post(
|
|
3084
|
+
`/task-budgets/${encodeURIComponent(id)}/submit`,
|
|
3085
|
+
{ signature }
|
|
3086
|
+
);
|
|
3087
|
+
return {
|
|
3088
|
+
taskBudget: mapTaskBudget(raw.task_budget),
|
|
3089
|
+
status: raw.status,
|
|
3090
|
+
...raw.close_tx_hash ? { closeTxHash: raw.close_tx_hash } : {}
|
|
3091
|
+
};
|
|
3092
|
+
}
|
|
3093
|
+
/**
|
|
3094
|
+
* Close a task budget early. A `pending` or already-expired `open` budget
|
|
3095
|
+
* closes immediately (nothing was ever signed or is on-chain). A live
|
|
3096
|
+
* `open` budget returns `sign_data` for an on-chain `disableDelegation`
|
|
3097
|
+
* UserOp — sign it (`haven_sign` with `task_budget_id`) and call
|
|
3098
|
+
* `submitTaskBudget`.
|
|
3099
|
+
*/
|
|
3100
|
+
async closeTaskBudget(id) {
|
|
3101
|
+
const raw = await this.post(`/task-budgets/${encodeURIComponent(id)}/close`, {});
|
|
3102
|
+
return {
|
|
3103
|
+
taskBudget: mapTaskBudget(raw.task_budget),
|
|
3104
|
+
status: raw.status,
|
|
3105
|
+
...raw.sign_data ? { signData: raw.sign_data } : {},
|
|
3106
|
+
...raw.next_action ? { nextAction: raw.next_action } : {}
|
|
3107
|
+
};
|
|
3108
|
+
}
|
|
2942
3109
|
/**
|
|
2943
3110
|
* Rehydrate the x402 resume-state bundle for a payment id (#1328: the MPP
|
|
2944
3111
|
* resume-state variant retired along with the rest of the mpp_demo surface).
|
|
@@ -2991,7 +3158,7 @@ var HavenClient = class {
|
|
|
2991
3158
|
if (cached) return cached;
|
|
2992
3159
|
const inFlight = this.inFlightX402.get(idempotencyKey);
|
|
2993
3160
|
if (inFlight) return inFlight;
|
|
2994
|
-
const promise = this.fundingLeg.authorize(paymentRequired, option, idempotencyKey);
|
|
3161
|
+
const promise = this.fundingLeg.authorize(paymentRequired, option, idempotencyKey, options.taskBudgetId);
|
|
2995
3162
|
this.inFlightX402.set(idempotencyKey, promise);
|
|
2996
3163
|
try {
|
|
2997
3164
|
return await promise;
|
|
@@ -3073,7 +3240,7 @@ var HavenClient = class {
|
|
|
3073
3240
|
async payX402Quote(quote, options = {}) {
|
|
3074
3241
|
const idempotencyKey = options.idempotencyKey ?? quote.idempotencyKey;
|
|
3075
3242
|
try {
|
|
3076
|
-
const receipt = await this.authorizeX402(quote.paymentRequired, { idempotencyKey });
|
|
3243
|
+
const receipt = await this.authorizeX402(quote.paymentRequired, { ...options, idempotencyKey });
|
|
3077
3244
|
return this.merchantCompletion.retryRequest(
|
|
3078
3245
|
quote.request.url,
|
|
3079
3246
|
requestInitFromSnapshot(quote.request),
|
|
@@ -3651,7 +3818,7 @@ var toolDescriptions = {
|
|
|
3651
3818
|
send: {
|
|
3652
3819
|
summary: "Send ETH or USDC directly from the agent's Haven wallet to a recipient address.",
|
|
3653
3820
|
selectionGuidance: "Use this for plain transfers \u2014 refunding a user, paying a freelancer, topping up a co-agent's wallet, or moving funds between addresses. Do NOT use for x402 paid endpoints \u2014 use haven_pay_x402 instead. Do NOT use for read-only allowance, budget, or what-can-I-spend questions \u2014 use haven_get_allowances. Do NOT use to check whether funds are held before sending \u2014 use haven_check_funds.",
|
|
3654
|
-
behavior: "Sends the 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 or any signing \u2014 never queued for a human to approve. The agent's signing key signs the account's typed data
|
|
3821
|
+
behavior: "Sends the 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 or any signing \u2014 never queued for a human to approve. The agent's signing key signs the account's typed data; a current signer refuses any that does not match the payment's hash (USEROP_BINDING_MISMATCH), an older signer signs it and the account rejects it on-chain. Haven never receives the key.",
|
|
3655
3822
|
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."
|
|
3656
3823
|
},
|
|
3657
3824
|
reportSettlementEvidence: {
|
|
@@ -3984,6 +4151,10 @@ Call \`haven_get_agent\`, one of the Haven MCP tools the connector wires into yo
|
|
|
3984
4151
|
|
|
3985
4152
|
Nothing here needs you to. Steps 1-3 are links: hand your user the full \`<host>/\u2026\` URL and ask them to say when it is done. Step 5 is a link only when your run reported one in \`approval.url\` \u2014 otherwise it is the tab they already have open, as above. Then poll \`haven_get_agent\` until it reads \`ready\`. Do not route around the sign-in wall \u2014 it makes the account theirs, not yours.
|
|
3986
4153
|
|
|
4154
|
+
## If something breaks
|
|
4155
|
+
|
|
4156
|
+
If a Haven result carries \`client_update\`, that client is out of date: update it \u2014 \`upgrade_command\` names the package and channel \u2014 then retry. \`required: true\` means payments are refused until you do. What changed: [/releases](/releases).
|
|
4157
|
+
|
|
3987
4158
|
## Vocabulary
|
|
3988
4159
|
|
|
3989
4160
|
| Term | What it is |
|