@haven_ai/sdk 0.11.0-alpha.0 → 0.13.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 +37 -4
- package/dist/{base64-D4VZhgUg.d.ts → base64-C3mJNLYR.d.cts} +10 -2
- package/dist/{base64-DR4Gl8cD.d.cts → base64-CxzIc3KH.d.ts} +10 -2
- package/dist/{chunk-IKQIIMM6.cjs → chunk-4Z7PKLFD.cjs} +38 -37
- package/dist/chunk-4Z7PKLFD.cjs.map +1 -0
- package/dist/{chunk-34YJQFCM.js → chunk-CK6VRIPR.js} +3 -3
- package/dist/chunk-CK6VRIPR.js.map +1 -0
- package/dist/{chunk-ZDJ55LR7.js → chunk-CXQ42FOA.js} +7 -6
- package/dist/chunk-CXQ42FOA.js.map +1 -0
- package/dist/{chunk-MZKLAHBK.cjs → chunk-LZHCIK7L.cjs} +56 -4
- package/dist/chunk-LZHCIK7L.cjs.map +1 -0
- package/dist/{chunk-I7SL4A6X.js → chunk-MI7LVP56.js} +55 -5
- package/dist/chunk-MI7LVP56.js.map +1 -0
- package/dist/{chunk-DXBAN4SN.cjs → chunk-Z5MTKU6O.cjs} +4 -4
- package/dist/chunk-Z5MTKU6O.cjs.map +1 -0
- package/dist/edge.cjs +93 -93
- package/dist/edge.d.cts +11 -8
- package/dist/edge.d.ts +11 -8
- package/dist/edge.js +4 -4
- package/dist/index.cjs +456 -356
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -15
- package/dist/index.d.ts +26 -15
- package/dist/index.js +140 -48
- package/dist/index.js.map +1 -1
- package/dist/{receipt-CbD8L0WL.d.ts → receipt-CTOPh2fm.d.ts} +5 -4
- package/dist/{receipt-_bWCELEH.d.cts → receipt-DwRqGjGb.d.cts} +5 -4
- package/dist/test-support.cjs +58 -21
- package/dist/test-support.cjs.map +1 -1
- package/dist/test-support.d.cts +47 -3
- package/dist/test-support.d.ts +47 -3
- package/dist/test-support.js +37 -3
- package/dist/test-support.js.map +1 -1
- package/dist/{types-DKmvjXK6.d.cts → types-3vUBmumz.d.cts} +163 -2
- package/dist/{types-DKmvjXK6.d.ts → types-3vUBmumz.d.ts} +163 -2
- package/package.json +2 -2
- package/dist/chunk-34YJQFCM.js.map +0 -1
- package/dist/chunk-DXBAN4SN.cjs.map +0 -1
- package/dist/chunk-I7SL4A6X.js.map +0 -1
- package/dist/chunk-IKQIIMM6.cjs.map +0 -1
- package/dist/chunk-MZKLAHBK.cjs.map +0 -1
- package/dist/chunk-ZDJ55LR7.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { decodeBase64Json, selectStandardPaymentOption, buildX402IdempotencyKey, x402AuthorizationAmount, assertBoundDirectPaymentUserOp, assertFundingLegPaysDelegate, HavenTypedDataRefusedError, assertOwnSettlementChild, parsePaymentRequiredResponse, x402PaymentHeaderNamesSent, SDK_CLIENT_IDENTITY, readClientUpdate, HAVEN_CLIENT_HEADER, isSweepableChain, sweepUsdcAddress, x402PaymentHeaderNamesFor, X402_PAYMENT_HEADER_NAMES, toStandardPaymentRequirements, deriveX402PaymentNonce, encodeBase64Json, x402V2PaymentEnvelope, isErc7710Option, buildSignedTaxDeclaration, encodeTaxDeclarationHeader, selectX402SettlementScheme, selectErc7710PaymentOption, x402AssetTransferMethod, resolveTokenFromAddress, normalizePaymentRequired, X_TAX_DECLARATION_HEADER } 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_NONCE_TAG, 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, deriveX402PaymentNonce, 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, x402PaymentHeaderNamesFor, x402RetryHeadersFor, x402V2PaymentEnvelope } from './chunk-
|
|
3
|
-
import { verifyPaymentReceipt } from './chunk-
|
|
4
|
-
export { RECEIPT_VERSION, verifyPaymentReceipt } from './chunk-
|
|
5
|
-
import { HavenSigningError, HavenError, DEFAULT_CONFIRMATION_TIMEOUT_MS, HavenApiError, assertUserOpTypedDataBinding, AgentPaymentRail, AgentPaymentPhase, HavenTimeoutError, X402TaskBudgetMismatchError, X402UnexpectedStatusError, X402AlreadySettledError, MerchantTimeoutError, AgentPaymentWarningCode, AgentPaymentNextAction, HavenPaymentStateError, HavenZeroSettlementHashError, X402Erc7710AlreadySettledError, chainIdForNetwork } from './chunk-
|
|
6
|
-
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-
|
|
1
|
+
import { decodeBase64Json, selectStandardPaymentOption, buildX402IdempotencyKey, x402AuthorizationAmount, assertBoundDirectPaymentUserOp, assertFundingLegPaysDelegate, HavenTypedDataRefusedError, assertOwnSettlementChild, parsePaymentRequiredResponse, x402PaymentHeaderNamesSent, SDK_CLIENT_IDENTITY, readClientUpdate, HAVEN_CLIENT_HEADER, isSweepableChain, sweepUsdcAddress, x402PaymentHeaderNamesFor, X402_PAYMENT_HEADER_NAMES, toStandardPaymentRequirements, deriveX402PaymentNonce, encodeBase64Json, x402V2PaymentEnvelope, isErc7710Option, buildSignedTaxDeclaration, encodeTaxDeclarationHeader, selectX402SettlementScheme, selectErc7710PaymentOption, x402AssetTransferMethod, resolveTokenFromAddress, normalizePaymentRequired, X_TAX_DECLARATION_HEADER } from './chunk-CXQ42FOA.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_NONCE_TAG, 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, deriveX402PaymentNonce, 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, x402PaymentHeaderNamesFor, x402RetryHeadersFor, x402V2PaymentEnvelope } from './chunk-CXQ42FOA.js';
|
|
3
|
+
import { verifyPaymentReceipt } from './chunk-CK6VRIPR.js';
|
|
4
|
+
export { RECEIPT_VERSION, verifyPaymentReceipt } from './chunk-CK6VRIPR.js';
|
|
5
|
+
import { HavenSigningError, HavenError, DEFAULT_CONFIRMATION_TIMEOUT_MS, HavenApiError, assertUserOpTypedDataBinding, AgentPaymentRail, AgentPaymentPhase, HavenTimeoutError, X402TaskBudgetMismatchError, X402UnexpectedStatusError, X402AlreadySettledError, MerchantTimeoutError, AgentPaymentWarningCode, AgentPaymentNextAction, FundingConfirmationUnknownError, FundingRevertedError, HavenPaymentStateError, HavenZeroSettlementHashError, X402Erc7710AlreadySettledError, chainIdForNetwork } from './chunk-MI7LVP56.js';
|
|
6
|
+
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, FundingConfirmationUnknownError, FundingRevertedError, 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-MI7LVP56.js';
|
|
7
7
|
import { hashTypedData } from 'viem';
|
|
8
8
|
import { ethers } from 'ethers';
|
|
9
9
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
@@ -143,12 +143,16 @@ var HavenApiTransport = class {
|
|
|
143
143
|
return data;
|
|
144
144
|
} catch (err) {
|
|
145
145
|
if (err instanceof HavenApiError) throw err;
|
|
146
|
+
const failureCause = { kind: "no_answer", method };
|
|
146
147
|
if (err instanceof Error && err.name === "AbortError") {
|
|
147
|
-
throw new HavenApiError(`Request to ${path} timed out`, 408);
|
|
148
|
+
throw new HavenApiError(`Request to ${path} timed out`, 408, void 0, void 0, failureCause);
|
|
148
149
|
}
|
|
149
150
|
throw new HavenApiError(
|
|
150
151
|
`Request to ${path} failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
151
|
-
0
|
|
152
|
+
0,
|
|
153
|
+
void 0,
|
|
154
|
+
void 0,
|
|
155
|
+
failureCause
|
|
152
156
|
);
|
|
153
157
|
} finally {
|
|
154
158
|
clearTimeout(timeout);
|
|
@@ -282,6 +286,10 @@ function mapPaymentStatusResult(raw) {
|
|
|
282
286
|
// carried through even when `null` (a real, known answer).
|
|
283
287
|
...raw.settlement_scheme !== void 0 ? { settlementScheme: raw.settlement_scheme } : {},
|
|
284
288
|
...raw.merchant_settlement_recorded === true ? { merchantSettlementRecorded: true } : {},
|
|
289
|
+
// #4530: the recorded transaction itself — absent from the raw payload
|
|
290
|
+
// (older backend, or nothing recorded) stays absent here, so the field
|
|
291
|
+
// never claims a hash the backend did not record.
|
|
292
|
+
...typeof raw.merchant_settlement_tx_hash === "string" ? { merchantSettlementTxHash: raw.merchant_settlement_tx_hash } : {},
|
|
285
293
|
// #3494: additive, mirrors the backend's own `status === 'failed'` gate —
|
|
286
294
|
// absent from the raw payload (older backend, or any non-failed status)
|
|
287
295
|
// stays absent here.
|
|
@@ -354,6 +362,30 @@ function mapPaymentReceipt(raw) {
|
|
|
354
362
|
if ("approval_request_id" in raw) {
|
|
355
363
|
receipt.approvalRequestId = raw.approval_request_id ?? null;
|
|
356
364
|
}
|
|
365
|
+
if ("settlement_scheme" in raw) {
|
|
366
|
+
receipt.settlementScheme = raw.settlement_scheme ?? null;
|
|
367
|
+
}
|
|
368
|
+
if ("budget_delegation_hash" in raw) {
|
|
369
|
+
receipt.budgetDelegationHash = raw.budget_delegation_hash ?? null;
|
|
370
|
+
}
|
|
371
|
+
if ("delivery_quality" in raw) {
|
|
372
|
+
receipt.deliveryQuality = raw.delivery_quality ?? null;
|
|
373
|
+
}
|
|
374
|
+
if ("delivery_note" in raw) {
|
|
375
|
+
receipt.deliveryNote = raw.delivery_note ?? null;
|
|
376
|
+
}
|
|
377
|
+
if ("delivery_reported_at" in raw) {
|
|
378
|
+
receipt.deliveryReportedAt = raw.delivery_reported_at ?? null;
|
|
379
|
+
}
|
|
380
|
+
if ("delivery_reference" in raw) {
|
|
381
|
+
receipt.deliveryReference = raw.delivery_reference ?? null;
|
|
382
|
+
}
|
|
383
|
+
if ("purchase_group_id" in raw) {
|
|
384
|
+
receipt.purchaseGroupId = raw.purchase_group_id ?? null;
|
|
385
|
+
}
|
|
386
|
+
if ("parent_payment_id" in raw) {
|
|
387
|
+
receipt.parentPaymentId = raw.parent_payment_id ?? null;
|
|
388
|
+
}
|
|
357
389
|
return receipt;
|
|
358
390
|
}
|
|
359
391
|
|
|
@@ -473,7 +505,7 @@ function throwPaymentStateError(label, raw) {
|
|
|
473
505
|
throw new HavenPaymentStateError(state.message, statusCode, state, raw);
|
|
474
506
|
}
|
|
475
507
|
if (raw.status === "pending_approval") {
|
|
476
|
-
throw
|
|
508
|
+
throw HavenApiError.deterministic(
|
|
477
509
|
`${label} exceeds the agent's on-chain budget and was declined; no approval is pending and none will arrive (payment_id: ${raw.payment_id}).`,
|
|
478
510
|
statusCode,
|
|
479
511
|
raw
|
|
@@ -488,6 +520,7 @@ function throwPaymentStateError(label, raw) {
|
|
|
488
520
|
}
|
|
489
521
|
const paymentId = raw.payment_id ? ` (payment_id: ${raw.payment_id})` : "";
|
|
490
522
|
const message = raw.error ?? `${label} ${raw.status}${paymentId}`;
|
|
523
|
+
if (statusCode >= 500) throw HavenApiError.deterministic(message, statusCode, raw);
|
|
491
524
|
throw new HavenApiError(message, statusCode, raw);
|
|
492
525
|
}
|
|
493
526
|
|
|
@@ -1346,6 +1379,17 @@ function deriveReadiness(status, allowances) {
|
|
|
1346
1379
|
if (status !== "active") return "revoked";
|
|
1347
1380
|
return allowances.some((allowance) => safeBigInt(allowance.remainingAtomic) > 0n) ? "ready" : "needs_approval";
|
|
1348
1381
|
}
|
|
1382
|
+
function deriveBudgetRefillsAt(readiness, allowances) {
|
|
1383
|
+
if (readiness !== "needs_approval" || allowances.length === 0) return null;
|
|
1384
|
+
const times = allowances.map((allowance) => allowance.nextRefillAt).filter((at) => at !== null && !Number.isNaN(Date.parse(at)));
|
|
1385
|
+
if (times.length === 0) return null;
|
|
1386
|
+
return times.reduce((earliest, at) => Date.parse(at) < Date.parse(earliest) ? at : earliest);
|
|
1387
|
+
}
|
|
1388
|
+
function deriveBudgetEndedAt(readiness, allowances, budgetEndedAt) {
|
|
1389
|
+
if (readiness !== "needs_approval" || allowances.length > 0) return null;
|
|
1390
|
+
if (!budgetEndedAt || Number.isNaN(Date.parse(budgetEndedAt))) return null;
|
|
1391
|
+
return budgetEndedAt;
|
|
1392
|
+
}
|
|
1349
1393
|
function formatRemainingDisplay(tokenAddress, tokenSymbol, remainingAtomic) {
|
|
1350
1394
|
const token = resolveTokenFromAddress(tokenAddress);
|
|
1351
1395
|
return token ? `${formatAtomicAmount(safeBigInt(remainingAtomic), token.decimals)} ${tokenSymbol}` : `${remainingAtomic} ${tokenSymbol} (atomic; unknown decimals)`;
|
|
@@ -1514,11 +1558,23 @@ var AccountReads = class {
|
|
|
1514
1558
|
isResetPending: allowance.onchain.isResetPending,
|
|
1515
1559
|
// #3731: same provenance as the detailed read's field — the compact
|
|
1516
1560
|
// row is a projection of the same allowance, never a second source.
|
|
1517
|
-
fundsCoverRemaining: allowance.fundsCoverRemaining
|
|
1561
|
+
fundsCoverRemaining: allowance.fundsCoverRemaining,
|
|
1562
|
+
nextRefillAt: allowance.nextRefillAt
|
|
1518
1563
|
};
|
|
1519
1564
|
});
|
|
1520
1565
|
const readiness = deriveReadiness(agent.status, allowances);
|
|
1521
|
-
|
|
1566
|
+
const budgetRefillsAt = deriveBudgetRefillsAt(readiness, allowances);
|
|
1567
|
+
const budgetEndedAt = deriveBudgetEndedAt(readiness, allowances, allowanceSummary.budgetEndedAt);
|
|
1568
|
+
return {
|
|
1569
|
+
...agent,
|
|
1570
|
+
readiness,
|
|
1571
|
+
spend_authority_readiness: readiness,
|
|
1572
|
+
budgetRefillsAt,
|
|
1573
|
+
budgetEndedAt,
|
|
1574
|
+
allowances,
|
|
1575
|
+
taskBudgets,
|
|
1576
|
+
pendingSubBudgetSignatures
|
|
1577
|
+
};
|
|
1522
1578
|
}
|
|
1523
1579
|
/**
|
|
1524
1580
|
* #3329: `GET /task-budgets?status=open`, mapped to the condensed summary
|
|
@@ -1614,6 +1670,9 @@ var AccountReads = class {
|
|
|
1614
1670
|
accountAddress: raw.account_address,
|
|
1615
1671
|
delegateAddress: raw.delegate_address,
|
|
1616
1672
|
chainId: raw.chain_id,
|
|
1673
|
+
// #4352: a non-empty parseable date → itself; anything else (absent on
|
|
1674
|
+
// an older backend, null when no budget has ended) → null.
|
|
1675
|
+
budgetEndedAt: typeof raw.budget_ended_at === "string" && !Number.isNaN(Date.parse(raw.budget_ended_at)) ? raw.budget_ended_at : null,
|
|
1617
1676
|
supersededBy: mapSupersession(raw.superseded_by),
|
|
1618
1677
|
allowances: raw.allowances.map((allowance) => ({
|
|
1619
1678
|
id: allowance.id,
|
|
@@ -1632,6 +1691,9 @@ var AccountReads = class {
|
|
|
1632
1691
|
// "unverifiable", not a missing key. The key is absent on the wire
|
|
1633
1692
|
// when the remaining is 0; that maps to null here too.
|
|
1634
1693
|
fundsCoverRemaining: allowance.funds_cover_remaining === true || allowance.funds_cover_remaining === false ? allowance.funds_cover_remaining : null,
|
|
1694
|
+
// #4355: a non-empty string → itself; anything else (absent on an
|
|
1695
|
+
// older backend, null when the budget never refills) → null.
|
|
1696
|
+
nextRefillAt: typeof allowance.next_refill_at === "string" && allowance.next_refill_at !== "" ? allowance.next_refill_at : null,
|
|
1635
1697
|
remainingDisplay: formatRemainingDisplay(allowance.token_address, allowance.token_symbol, allowance.onchain.remaining),
|
|
1636
1698
|
onchain: {
|
|
1637
1699
|
amount: allowance.onchain.amount,
|
|
@@ -2257,7 +2319,7 @@ var X402FundingLeg = class {
|
|
|
2257
2319
|
}
|
|
2258
2320
|
this.assertSignableAuthorizationState("x402 payment", raw);
|
|
2259
2321
|
if (!raw.sign_data?.hash) {
|
|
2260
|
-
throw
|
|
2322
|
+
throw HavenApiError.deterministic("No sign_hash returned from x402/authorize", 500, raw);
|
|
2261
2323
|
}
|
|
2262
2324
|
const sig = await this.signForData(raw.sign_data, {
|
|
2263
2325
|
asset: option.asset,
|
|
@@ -2379,20 +2441,27 @@ var X402FundingLeg = class {
|
|
|
2379
2441
|
* Skipped when `chainRpcs` does not include the chain; in that case Haven's
|
|
2380
2442
|
* backend has already confirmed on-chain submission and callers accept the
|
|
2381
2443
|
* small propagation window as a trade-off for not configuring an RPC URL.
|
|
2444
|
+
*
|
|
2445
|
+
* #4470: the two outcomes are typed apart. ethers 6 `waitForTransaction`
|
|
2446
|
+
* REJECTS on its timeout (and on an RPC failure) rather than resolving
|
|
2447
|
+
* null, so that path is `FundingConfirmationUnknownError` — funding may
|
|
2448
|
+
* have moved. A mined receipt with `status !== 1` is `FundingRevertedError`.
|
|
2382
2449
|
*/
|
|
2383
2450
|
async waitForFundingTx(txHash, chainId, timeoutMs = 3e4) {
|
|
2384
2451
|
if (!txHash || !chainId) return;
|
|
2385
2452
|
const rpcUrl = this.chainRpcs[chainId];
|
|
2386
2453
|
if (!rpcUrl) return;
|
|
2387
2454
|
const provider = createJsonRpcProvider(rpcUrl);
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2455
|
+
let onChainReceipt;
|
|
2456
|
+
try {
|
|
2457
|
+
onChainReceipt = await provider.waitForTransaction(txHash, 1, timeoutMs);
|
|
2458
|
+
} catch (err) {
|
|
2459
|
+
throw new FundingConfirmationUnknownError(txHash, chainId, err instanceof Error ? err.message : String(err));
|
|
2460
|
+
}
|
|
2461
|
+
if (!onChainReceipt) {
|
|
2462
|
+
throw new FundingConfirmationUnknownError(txHash, chainId, "no receipt within the timeout window");
|
|
2395
2463
|
}
|
|
2464
|
+
if (onChainReceipt.status !== 1) throw new FundingRevertedError(txHash, chainId);
|
|
2396
2465
|
}
|
|
2397
2466
|
/**
|
|
2398
2467
|
* Can the delegate EOA still fund an authorization for `amountAtomic`?
|
|
@@ -2574,7 +2643,7 @@ var X402Erc7710 = class {
|
|
|
2574
2643
|
...options.subBudgetId ? { subBudgetId: options.subBudgetId } : {}
|
|
2575
2644
|
});
|
|
2576
2645
|
if (!raw.payment_id) {
|
|
2577
|
-
throw
|
|
2646
|
+
throw HavenApiError.deterministic("No payment_id returned from x402/authorize", 500, raw);
|
|
2578
2647
|
}
|
|
2579
2648
|
if (raw.status === "confirmed" && raw.tx_hash) {
|
|
2580
2649
|
const requestedUrl = options.resourceUrl ?? paymentRequired.resource?.url;
|
|
@@ -2591,7 +2660,7 @@ var X402Erc7710 = class {
|
|
|
2591
2660
|
}
|
|
2592
2661
|
const signData = raw.sign_data;
|
|
2593
2662
|
if (signData?.signature_scheme !== "eip712_delegation" || !signData.typed_data) {
|
|
2594
|
-
throw
|
|
2663
|
+
throw HavenApiError.deterministic(
|
|
2595
2664
|
`x402/authorize did not return an erc7710 settlement child (signature_scheme was ${JSON.stringify(signData?.signature_scheme)}). Refusing to sign a payload this path did not ask for.`,
|
|
2596
2665
|
500,
|
|
2597
2666
|
raw
|
|
@@ -2634,7 +2703,7 @@ var X402Erc7710 = class {
|
|
|
2634
2703
|
throw err;
|
|
2635
2704
|
}
|
|
2636
2705
|
if (!settled.payment_header) {
|
|
2637
|
-
throw
|
|
2706
|
+
throw HavenApiError.deterministic(
|
|
2638
2707
|
"x402 settle returned no payment_header \u2014 the merchant cannot be retried.",
|
|
2639
2708
|
500,
|
|
2640
2709
|
settled
|
|
@@ -2805,10 +2874,10 @@ var MerchantCompletion = class {
|
|
|
2805
2874
|
}
|
|
2806
2875
|
async retryRequest(url, initialInit, paymentRequired, receipt) {
|
|
2807
2876
|
if (!receipt.accepted) {
|
|
2808
|
-
throw
|
|
2877
|
+
throw HavenApiError.deterministic("No accepted x402 option was recorded for payment retry", 500);
|
|
2809
2878
|
}
|
|
2810
2879
|
if (!receipt.paymentHeader) {
|
|
2811
|
-
throw
|
|
2880
|
+
throw HavenApiError.deterministic("No x402 payment header was returned for payment retry", 500);
|
|
2812
2881
|
}
|
|
2813
2882
|
let retryInit = initialInit;
|
|
2814
2883
|
if (this.getTaxDeclarationHeader && this.delegateKey) {
|
|
@@ -3172,7 +3241,11 @@ var MerchantCompletion = class {
|
|
|
3172
3241
|
merchantStatus: input.merchantStatus,
|
|
3173
3242
|
// #3778: accepted outcomes only — a rejection delivered nothing, so
|
|
3174
3243
|
// there is no deliverable to point at.
|
|
3175
|
-
...input.deliveryReference ? { deliveryReference: input.deliveryReference } : {}
|
|
3244
|
+
...input.deliveryReference ? { deliveryReference: input.deliveryReference } : {},
|
|
3245
|
+
// #4534: the purchase correlation key — accepted outcomes only, for the
|
|
3246
|
+
// same reason: a rejected retry bought nothing to group.
|
|
3247
|
+
...input.purchaseGroupId ? { purchaseGroupId: input.purchaseGroupId } : {},
|
|
3248
|
+
...input.merchantResponse ? { merchantResponse: input.merchantResponse } : {}
|
|
3176
3249
|
});
|
|
3177
3250
|
return { paymentId: status.paymentId, outcome: "accepted", txHash, resourceUrl, recorded: "evidence" };
|
|
3178
3251
|
}
|
|
@@ -3194,15 +3267,16 @@ var MerchantCompletion = class {
|
|
|
3194
3267
|
};
|
|
3195
3268
|
for (let attempt = 0; ; attempt += 1) {
|
|
3196
3269
|
const result = await this.postEvidenceOnce(body);
|
|
3197
|
-
if (result.outcome !== "retryable" || attempt >= EVIDENCE_RETRY_DELAYS_MS.length) {
|
|
3270
|
+
if (result.outcome !== "retryable" || result.statusCode !== EVIDENCE_RETRYABLE_STATUS || attempt >= EVIDENCE_RETRY_DELAYS_MS.length) {
|
|
3198
3271
|
return result;
|
|
3199
3272
|
}
|
|
3200
3273
|
await this.sleep(EVIDENCE_RETRY_DELAYS_MS[attempt]);
|
|
3201
3274
|
}
|
|
3202
3275
|
}
|
|
3203
3276
|
/**
|
|
3204
|
-
* ONE evidence attempt — the body of `reportEvidence`'s loop above. A 503
|
|
3205
|
-
*
|
|
3277
|
+
* ONE evidence attempt — the body of `reportEvidence`'s loop above. A 503,
|
|
3278
|
+
* or no verdict at all (a non-answer or a 408, #4470), answers `retryable`
|
|
3279
|
+
* immediately; the caller decides whether to sleep and
|
|
3206
3280
|
* re-attempt (`reportEvidence`, and — #4215 — the local settlement report
|
|
3207
3281
|
* in `retryRequest`) or hand the outcome to its consumer (#3764 D2, the
|
|
3208
3282
|
* hosted settlement report).
|
|
@@ -3213,7 +3287,8 @@ var MerchantCompletion = class {
|
|
|
3213
3287
|
return { outcome: "confirmed" };
|
|
3214
3288
|
} catch (err) {
|
|
3215
3289
|
const statusCode = err instanceof HavenApiError ? err.statusCode : void 0;
|
|
3216
|
-
|
|
3290
|
+
const noVerdict = statusCode === 408 || err instanceof HavenApiError && err.failureCause?.kind === "no_answer";
|
|
3291
|
+
if (statusCode !== EVIDENCE_RETRYABLE_STATUS && !noVerdict) {
|
|
3217
3292
|
const reason = refusalReasonFromBody(err);
|
|
3218
3293
|
const errorCode = refusalErrorCodeFromBody(err);
|
|
3219
3294
|
return {
|
|
@@ -3634,7 +3709,7 @@ var HavenClient = class {
|
|
|
3634
3709
|
}
|
|
3635
3710
|
const fundingTo = options.delegateAddress ?? (await this.getAgent()).delegateAddress;
|
|
3636
3711
|
if (!fundingTo) {
|
|
3637
|
-
throw
|
|
3712
|
+
throw HavenApiError.deterministic("Authenticated agent has no delegate address registered.", 502);
|
|
3638
3713
|
}
|
|
3639
3714
|
const idempotencyKey = options.idempotencyKey ?? buildX402IdempotencyKey(paymentRequired, option);
|
|
3640
3715
|
const raw = await this.post("/x402", {
|
|
@@ -3672,7 +3747,7 @@ var HavenClient = class {
|
|
|
3672
3747
|
throwPaymentStateError("x402 payment", raw);
|
|
3673
3748
|
}
|
|
3674
3749
|
if (!raw.sign_data?.hash) {
|
|
3675
|
-
throw
|
|
3750
|
+
throw HavenApiError.deterministic("No sign_hash returned from x402/authorize", 500, raw);
|
|
3676
3751
|
}
|
|
3677
3752
|
if (raw.sign_data.signature_scheme !== void 0 && !raw.sign_data.typed_data) {
|
|
3678
3753
|
throw new HavenSigningError(
|
|
@@ -3680,7 +3755,7 @@ var HavenClient = class {
|
|
|
3680
3755
|
);
|
|
3681
3756
|
}
|
|
3682
3757
|
if (!raw.x402_expected_auth) {
|
|
3683
|
-
throw
|
|
3758
|
+
throw HavenApiError.deterministic("No x402 expected-context binding returned from x402/authorize", 500, raw);
|
|
3684
3759
|
}
|
|
3685
3760
|
return {
|
|
3686
3761
|
paymentId: raw.payment_id,
|
|
@@ -3910,6 +3985,12 @@ var HavenClient = class {
|
|
|
3910
3985
|
* degrade-to-warning path and the ledger row would still land while the
|
|
3911
3986
|
* purchase proceeded.
|
|
3912
3987
|
*
|
|
3988
|
+
* #4458: an agent whose only budget for the token has ENDED is refused the
|
|
3989
|
+
* same way with `error_code: 'delegation_expired'` and `budget_ended_at`
|
|
3990
|
+
* (ISO 8601) — also thrown, also recorded (`source: 'hosted_prepare'`).
|
|
3991
|
+
* The remedy differs: the owner sets a NEW budget, waiting for a period to
|
|
3992
|
+
* reset cannot help.
|
|
3993
|
+
*
|
|
3913
3994
|
* #3518: without `merchantTo`, an agent whose budgets for the token are all
|
|
3914
3995
|
* merchant-locked gets 409 `budget_requires_recipient` (thrown, nothing
|
|
3915
3996
|
* recorded) naming the pins in `budget_recipient_addresses` — repeat the
|
|
@@ -3934,12 +4015,13 @@ var HavenClient = class {
|
|
|
3934
4015
|
/**
|
|
3935
4016
|
* Post-purchase allowance/budget summary for a settled payment (#1310).
|
|
3936
4017
|
*
|
|
3937
|
-
* Reuses the EXACT rail-aware read path {@link getAllowances}
|
|
3938
|
-
*
|
|
3939
|
-
*
|
|
3940
|
-
* the #1090 `deriveDelegationBudgets`-backed enforcer read, never
|
|
4018
|
+
* Reuses the EXACT rail-aware read path {@link getAllowances} uses —
|
|
4019
|
+
* `GET /machine-payments/allowances`, with delegation-rail values coming
|
|
4020
|
+
* from the #1090 `deriveDelegationBudgets`-backed enforcer read, never
|
|
3941
4021
|
* `agent_allowances` — so this can never disagree with
|
|
3942
|
-
* {@link getAllowances} for the same fixture.
|
|
4022
|
+
* {@link getAllowances} for the same fixture. (#1306's catalog-purchase
|
|
4023
|
+
* preflight read the same path until #3054; it now calls
|
|
4024
|
+
* `POST /machine-payments/budget-precheck` instead.) The settled token is
|
|
3943
4025
|
* resolved from {@link getPaymentStatus} so callers pass only
|
|
3944
4026
|
* `paymentId`, never a second haven_get_agent-style round trip.
|
|
3945
4027
|
*
|
|
@@ -4964,7 +5046,7 @@ var toolDescriptions = {
|
|
|
4964
5046
|
getAgent: {
|
|
4965
5047
|
summary: "Return the authenticated agent identity AND its live spend authority in one call: Haven wallet, delegate, chain, raw status, spend_authority_readiness, per-token remaining allowance (atomic + human-readable). The recommended first call in a new session.",
|
|
4966
5048
|
selectionGuidance: "Use this as the session bootstrap, or to confirm identity together with whether the agent has spend authority right now. For per-token detail (configured vs spent vs reset window) use haven_get_allowances.",
|
|
4967
|
-
behavior: `Reads identity plus the live spend-authority snapshot \u2014 the active on-chain budget delegation. spend_authority_readiness (readiness is a deprecated alias, same value) is "ready" when at least one token has remaining spend authority, "needs_approval" when the agent is active but has none, and "revoked" when the credential is not active. Readiness is AUTHORITY
|
|
5049
|
+
behavior: `Reads identity plus the live spend-authority snapshot \u2014 the active on-chain budget delegation. spend_authority_readiness (readiness is a deprecated alias, same value) is "ready" when at least one token has remaining spend authority, "needs_approval" when the agent is active but has none, and "revoked" when the credential is not active. needs_approval with no allowances[] rows and budgetEndedAt set: the budget ENDED on that date \u2014 ask the owner to set a new one, waiting will not help; with no rows and no budgetEndedAt: nothing approved yet; with rows: spent for this period, refilling at budgetRefillsAt (null: it ends first). Readiness is AUTHORITY \u2014 a budget is live, not that funds are held. Each allowances[] row carries fundsCoverRemaining: false when the account's balance cannot back that row's whole remaining budget \u2014 a heads-up to mention, not a refusal (budgets above the balance are normal; an owner may top up weekly) \u2014 and null when the coverage read failed or the remaining was not read live; the key is absent at remaining 0, and rows for one token are compared alone. 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 it with a signer tool call or connect --doctor. An over-budget payment is declined before any money moves; there is no approval queue \u2014 ask the owner to grant or raise the budget. allowances[] carries id, tokenAddress, remainingAtomic, remainingDisplay, nextRefillAt per token. taskBudgets[] rows carry maxAtomic/maxDisplay plus spentAtomic/remainingAtomic/remainingDisplay \u2014 what the chain still allows that budget \u2014 and remainingIsFromChain: false marks a failed read (figures null, never the cap); only live rows are listed, each with its status (pending | open | closing) and isExpired. pendingSubBudgetSignatures[] lists sub-budget rows awaiting YOUR signature, with the haven_sign step. Identity fields: id, name, status, accountAddress, delegateAddress, chainId.`,
|
|
4968
5050
|
nextActionGuidance: ""
|
|
4969
5051
|
},
|
|
4970
5052
|
getAllowances: {
|
|
@@ -5286,7 +5368,7 @@ You are an AI agent whose user has no Haven account yet, or has one but has not
|
|
|
5286
5368
|
|
|
5287
5369
|
## What Haven is, in three sentences
|
|
5288
5370
|
|
|
5289
|
-
Your user gives you a **budget on their own account** \u2014 not their wallet, and not a key to their funds. The budget is a delegation they sign, enforced on-chain: a payment over it, to the wrong recipient, or past its expiry is refused at execution time, not by a dashboard promise. Haven constructs and relays the payments; you get an agent credential and a signing key made on your machine, and your user can revoke the budget without you and without Haven.
|
|
5371
|
+
Your user gives you a **budget on their own account** \u2014 not their wallet, and not a key to their funds. The budget is a delegation they sign, enforced on-chain: a payment over it, to the wrong recipient, or past its expiry is refused at execution time, not by a dashboard promise. The budget also ENDS on a date your user chooses \u2014 30, 90 (default), 180 or 365 days out \u2014 and that day is enforced on-chain too. When the budget ends, your authority to pay ends with it; ask your user to renew it (a new approval on the agent's page, or a new \`budget grant\`). Haven constructs and relays the payments; you get an agent credential and a signing key made on your machine, and your user can revoke the budget without you and without Haven.
|
|
5290
5372
|
|
|
5291
5373
|
## The sequence
|
|
5292
5374
|
|
|
@@ -5359,7 +5441,7 @@ If the approval wait times out, nothing is lost \u2014 your agent is registered
|
|
|
5359
5441
|
Call \`haven_get_agent\`, one of the Haven MCP tools the connector wires into your runtime in step 4. It returns identity plus \`spend_authority_readiness\`:
|
|
5360
5442
|
|
|
5361
5443
|
- \`ready\` \u2014 a budget is live: you have the authority to pay. It does not say the account holds funds \u2014 the \`allowances[]\` rows carry \`funds_cover_remaining\`, and \`false\` there is a heads-up to mention to your user, not a refusal.
|
|
5362
|
-
- \`needs_approval\` \u2014
|
|
5444
|
+
- \`needs_approval\` \u2014 no budget can pay. \`allowances[]\` empty with \`budgetEndedAt\` set: your budget ENDED on that date; waiting will not help \u2014 ask your user to set a new one in their Haven tab. \`allowances[]\` empty with \`budgetEndedAt\` null: nobody approved one yet; ask your user again \u2014 there is no queue. Rows, all spent: it refills at \`budgetRefillsAt\`; tell your user, then wait or ask them to raise it (\`null\`: it ends first; ask them to renew it).
|
|
5363
5445
|
- \`revoked\` \u2014 the credential is not active; ask your user to create a new agent.
|
|
5364
5446
|
|
|
5365
5447
|
\`ready\` covers hosted identity and the budget only, not your local signer. Check that with \`npx -y @haven_ai/connect@<channel> --doctor\`, the same tag your prompt named \u2014 a separate command, so the command-modification rule does not bind it.
|
|
@@ -5395,7 +5477,7 @@ Then:
|
|
|
5395
5477
|
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.
|
|
5396
5478
|
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.
|
|
5397
5479
|
3. ${AGENT_APPROVAL_RELAY_JSON_SENTENCE}
|
|
5398
|
-
4. Once I have approved the budget, verify with the \`haven_get_agent\` tool: \`ready\` means I have given you the authority to pay \u2014 it does not confirm the account holds funds, so if its allowances rows carry \`funds_cover_remaining: false\`, tell me and still try the payment; \`needs_approval\` means my approval has not landed yet.
|
|
5480
|
+
4. Once I have approved the budget, verify with the \`haven_get_agent\` tool: \`ready\` means I have given you the authority to pay \u2014 it does not confirm the account holds funds, so if its allowances rows carry \`funds_cover_remaining: false\`, tell me and still try the payment; \`needs_approval\` with no allowances rows and \`budgetEndedAt\` set means my budget ended on that date \u2014 tell me, and I will set a new one; with no rows and no \`budgetEndedAt\`, my approval has not landed yet; with rows, the budget is spent \u2014 it refills at \`budgetRefillsAt\` (\`null\`: it ends first), so tell me, and I can raise or renew it.
|
|
5399
5481
|
|
|
5400
5482
|
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.
|
|
5401
5483
|
|
|
@@ -5515,8 +5597,10 @@ Do not guess the wallet address, network, or budget.
|
|
|
5515
5597
|
|
|
5516
5598
|
For instant orientation at the start of a session, read the non-secret
|
|
5517
5599
|
\`agent.json\` the connector wrote to your Haven credential directory (typically
|
|
5518
|
-
\`~/.haven/agents/<
|
|
5519
|
-
|
|
5600
|
+
\`~/.haven/agents/<slug>/agent.json\`, where \`<slug>\` is the wiring name of the
|
|
5601
|
+
\`haven-<slug>\` pair \u2014 only a \`--bare\` run, or a \`--replace\` without \`--name\`,
|
|
5602
|
+
names the directory by the agent id instead; if you don't know the folder, list
|
|
5603
|
+
\`~/.haven/agents/\` to find it). It
|
|
5520
5604
|
holds your agent id, Haven wallet address, network, and *configured* per-token
|
|
5521
5605
|
budget, and contains no keys \u2014 the fastest way to answer "who am I and what may
|
|
5522
5606
|
I spend" with no round trip. If that file is absent (some setups don't write
|
|
@@ -5537,7 +5621,13 @@ spending:
|
|
|
5537
5621
|
the user, not a refusal \u2014 a budget above the balance is a normal setup, so
|
|
5538
5622
|
still try the payment; \`null\` means the coverage read failed or the remaining
|
|
5539
5623
|
figure was not read live; the key is absent when the remaining is 0. Rows for
|
|
5540
|
-
one token are compared alone.
|
|
5624
|
+
one token are compared alone. \`needs_approval\` with no \`allowances[]\`
|
|
5625
|
+
rows and \`budgetEndedAt\` set means the budget ENDED on that date \u2014 waiting
|
|
5626
|
+
will not help, ask the owner to set a new one; with no rows and no
|
|
5627
|
+
\`budgetEndedAt\`, nobody approved a budget yet; with rows (all at remaining 0) the
|
|
5628
|
+
budget is spent for this period, and \`budgetRefillsAt\` says when it refills
|
|
5629
|
+
(\`null\`: it ends before it refills) \u2014 tell the user, then wait or ask them
|
|
5630
|
+
to raise it.
|
|
5541
5631
|
- \`mcp__haven__haven_get_allowances\` \u2014 detailed per-token breakdown
|
|
5542
5632
|
(configured, spent, reset window) when you need more than the summary.
|
|
5543
5633
|
- \`mcp__haven__haven_check_funds\` \u2014 whether the account actually HOLDS at
|
|
@@ -5751,8 +5841,10 @@ for example, an invoice carrying an \`x402_payment_url\` to POST with its
|
|
|
5751
5841
|
report it, the merchant's settlement evidence. (A deposit transfer is bound
|
|
5752
5842
|
to its payee and amount on-chain too; the difference is the record, not
|
|
5753
5843
|
the binding.)
|
|
5754
|
-
- **If the quote says stop, stop.** The quote checks the agent's
|
|
5755
|
-
budget at quote time, and it can check a price you state
|
|
5844
|
+
- **If the quote says stop, stop.** The hosted quote checks the agent's
|
|
5845
|
+
remaining budget at quote time, and it can check a price you state
|
|
5846
|
+
(\`expected_amount\`). On the local runtime (\`@haven_ai/mcp\`) the quote
|
|
5847
|
+
checks only a price you state; the budget is checked when you pay.
|
|
5756
5848
|
When it returns \`safe_to_continue: false\` \u2014 the amount exceeds the budget, or
|
|
5757
5849
|
the price does not match what you expected \u2014 it prefills no cap and names no
|
|
5758
5850
|
next tool: stop and tell the user. If the x402 challenge itself is wrong (a
|
|
@@ -5897,8 +5989,8 @@ only part of it was usable), and an optional \`note\` (max 2000 characters)
|
|
|
5897
5989
|
saying what was wrong. It is evidence only: it moves no money, never changes
|
|
5898
5990
|
\`settled\` or any money field, and works only on your own settled
|
|
5899
5991
|
payments \u2014 another agent's payment is refused. A re-report replaces your
|
|
5900
|
-
earlier verdict; the receipt then carries it beside the payment, so
|
|
5901
|
-
|
|
5992
|
+
earlier verdict; the receipt then carries it beside the payment, so a junk
|
|
5993
|
+
delivery does not read as a success.
|
|
5902
5994
|
### Relay a delivered code or credential to the owner \u2014 immediately, verbatim
|
|
5903
5995
|
|
|
5904
5996
|
If a merchant response carries what the user PAID FOR \u2014 a redemption code, gift
|