@haven_ai/sdk 0.1.4 → 0.1.6
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 +75 -10
- package/dist/index.cjs +229 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +216 -5
- package/dist/index.d.ts +216 -5
- package/dist/index.js +224 -27
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -68,6 +68,15 @@ The agent will pay a tiny amount (~0.01 EURe on Gnosis Chain), receive the demo
|
|
|
68
68
|
| Gnosis Chain | `eip155:100` | EURe, USDC.e, xDAI |
|
|
69
69
|
| Base | `eip155:8453` | USDC, ETH |
|
|
70
70
|
|
|
71
|
+
## Credential Lifecycle
|
|
72
|
+
|
|
73
|
+
- The Haven API key identifies the agent. It is not payment authority.
|
|
74
|
+
- The delegate key signs payment payloads locally. Haven's backend never receives it.
|
|
75
|
+
- On-chain Safe AllowanceModule state enforces the agent budget.
|
|
76
|
+
- `getAllowances()` / `get_allowances` is the right path for budget, remaining amount, reset period, or "what can I spend?" questions.
|
|
77
|
+
- If an API key is exposed or lost, rotate it from the Haven agent detail page. The new key is shown once and the old key stops working.
|
|
78
|
+
- If a delegate key is exposed or lost, pause or revoke the agent and create a new signing path.
|
|
79
|
+
|
|
71
80
|
## Step-by-Step API
|
|
72
81
|
|
|
73
82
|
For agents that need control over each step (e.g., external signing):
|
|
@@ -94,7 +103,7 @@ const result = await haven.waitForConfirmation(intent.paymentId)
|
|
|
94
103
|
|
|
95
104
|
Production merchant acceptance, facilitator, settlement, fiat, or acquiring functionality needs separate product and legal review under the repo's [CASP / MiCA guardrails](../../docs/regulatory/casp-risk-guardrails.md). The hosted x402 endpoint is an internal technical demo, not a merchant settlement product.
|
|
96
105
|
|
|
97
|
-
|
|
106
|
+
The SDK supports [x402](https://x402.org) client flows. When an API returns HTTP 402, the SDK evaluates the challenge against the agent's approved limits, uses the configured delegate key for the required signature, and retries automatically:
|
|
98
107
|
|
|
99
108
|
```typescript
|
|
100
109
|
// Automatic — fetch() intercepts 402, pays, and retries.
|
|
@@ -178,8 +187,12 @@ for (const block of response.content) {
|
|
|
178
187
|
|------|-------------|
|
|
179
188
|
| `make_payment` | Request and sign a payment from the user-controlled Safe within approved limits |
|
|
180
189
|
| `get_payment_status` | Check the status of a payment intent or approval request |
|
|
190
|
+
| `get_allowances` | Read configured and on-chain allowance state, including spent and remaining allowance |
|
|
181
191
|
| `authorize_x402_payment` | Authorize a policy-limited x402 payment and return a payment header for an HTTP 402 resource |
|
|
182
192
|
| `resume_x402_payment` | Resume an approved x402 payment and return a merchant payment header without creating a duplicate approval |
|
|
193
|
+
| `authorize_machine_payment` | Authorize an internal Haven MPP demo challenge and return proof details |
|
|
194
|
+
|
|
195
|
+
Use `get_allowances` for allowance, budget, spend-limit, remaining amount, reset-period, or "what can I spend?" questions. Payment tools still require the agent-held delegate key and on-chain Safe allowance state; the Haven API key identifies the agent but does not authorize spending by itself.
|
|
183
196
|
|
|
184
197
|
## Configuration
|
|
185
198
|
|
|
@@ -214,7 +227,7 @@ Every payment or approval state returned by Haven includes:
|
|
|
214
227
|
|
|
215
228
|
- `phase`: where the Haven-side payment currently is.
|
|
216
229
|
- `nextAction`: the stable action an agent should take next.
|
|
217
|
-
- `rail`: which payment rail produced the state
|
|
230
|
+
- `rail`: which payment rail produced the state. Categorical values (`direct`, `x402`, `mpp`) appear on resume-state discriminators; granular values (`mpp_demo`, `mpp_crypto`, `stripe_deposit`, `spt`) appear on response bodies.
|
|
218
231
|
- `message`: human-readable guidance for the same state.
|
|
219
232
|
|
|
220
233
|
The enum values and JSON Schema fragments are exported from `@haven_ai/sdk`:
|
|
@@ -230,22 +243,74 @@ import {
|
|
|
230
243
|
} from '@haven_ai/sdk'
|
|
231
244
|
```
|
|
232
245
|
|
|
246
|
+
### Flow diagram
|
|
247
|
+
|
|
233
248
|
```text
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
249
|
+
┌─────────────────────────┐
|
|
250
|
+
│ agent_signature_required│
|
|
251
|
+
└──────────┬──────────────┘
|
|
252
|
+
│ sign_and_submit_payment
|
|
253
|
+
▼
|
|
254
|
+
┌─────────────────────────┐
|
|
255
|
+
│ payment_submitted │
|
|
256
|
+
└──────────┬──────────────┘
|
|
257
|
+
│ check_status_later
|
|
258
|
+
┌──────────┴──────────────┐
|
|
259
|
+
▼ ▼
|
|
260
|
+
┌────────────────────────┐ ┌──────────────────────┐
|
|
261
|
+
│ payment_confirmed (✔) │ │ user_approval_required│
|
|
262
|
+
└────────────────────────┘ └──────────┬───────────┘
|
|
263
|
+
│ wait_for_user_approval
|
|
264
|
+
┌─────────────────┴───────────────────┐
|
|
265
|
+
│ single-owner Safe │ multisig Safe
|
|
266
|
+
▼ ▼
|
|
267
|
+
┌──────────────────────────┐ ┌──────────────────────────────────┐
|
|
268
|
+
│ user_execution_required │ │ waiting_for_additional_approvals │
|
|
269
|
+
└──────────┬───────────────┘ └────────────────┬─────────────────┘
|
|
270
|
+
│ wait_for_user_to_complete_payment │ wait_for_user_approval
|
|
271
|
+
└─────────────────┬───────────────────┘
|
|
272
|
+
▼
|
|
273
|
+
┌───────────────────────┐
|
|
274
|
+
│ funding_sent │
|
|
275
|
+
└──────────┬────────────┘
|
|
276
|
+
│ retry_original_x402_request (x402)
|
|
277
|
+
│ none (direct)
|
|
278
|
+
▼
|
|
279
|
+
┌───────────────────────┐
|
|
280
|
+
│ executed (✔) │
|
|
281
|
+
└───────────────────────┘
|
|
282
|
+
|
|
283
|
+
Terminal from any non-confirmed phase:
|
|
284
|
+
rejected → stop_and_tell_user
|
|
285
|
+
failed → stop_and_tell_user
|
|
286
|
+
expired → request_again_if_user_still_wants_it
|
|
241
287
|
```
|
|
242
288
|
|
|
289
|
+
### `phase` reference
|
|
290
|
+
|
|
291
|
+
| `phase` | Meaning | Terminal? |
|
|
292
|
+
|---------|---------|-----------|
|
|
293
|
+
| `agent_signature_required` | Haven prepared a payment intent; the agent must sign and submit. | no |
|
|
294
|
+
| `payment_submitted` | Haven received the signed payment; the agent should poll for confirmation. | no |
|
|
295
|
+
| `payment_confirmed` | Direct payment is confirmed on chain. | yes |
|
|
296
|
+
| `user_approval_required` | Payment exceeds remaining on-chain allowance; wallet owner must approve in Haven. | no |
|
|
297
|
+
| `user_execution_required` | Owner approved; the funding payment has not been sent yet (single-owner Safe). | no |
|
|
298
|
+
| `waiting_for_additional_approvals` | Funding payment was proposed and is waiting for the remaining multisig approvals. | no |
|
|
299
|
+
| `funding_sent` | Haven funding leg landed; the agent can continue the merchant/protocol leg. | no |
|
|
300
|
+
| `rejected` | Owner rejected the request. | yes |
|
|
301
|
+
| `expired` | Payment or approval request expired before completion. | yes |
|
|
302
|
+
| `failed` | Haven could not complete the payment. | yes |
|
|
303
|
+
|
|
304
|
+
The merchant settlement leg of x402 (and the MPP retry) is the agent's own request to the merchant — it does not have a Haven `phase`. The payment is `funding_sent` until the agent retries with `X-PAYMENT` (x402) or the MPP proof header; from Haven's perspective the payment becomes `executed` only after the agent successfully resumes.
|
|
305
|
+
|
|
306
|
+
### `nextAction` reference
|
|
307
|
+
|
|
243
308
|
| `nextAction` | What the agent should do |
|
|
244
309
|
|--------------|--------------------------|
|
|
245
310
|
| `sign_and_submit_payment` | Sign with the delegate key and submit the payment to Haven. |
|
|
246
311
|
| `check_status_later` | Poll `getPaymentStatus(payment_id)` later. |
|
|
247
312
|
| `none` | Stop polling; no more action is needed for this payment id. |
|
|
248
|
-
| `wait_for_user_approval` | Tell the user the payment is waiting in Haven, then poll later. Do not create a duplicate payment. |
|
|
313
|
+
| `wait_for_user_approval` | Tell the user the payment is waiting in Haven, then poll later. Do not create a duplicate payment. Same `nextAction` covers both the single-owner case (waiting for one owner to approve) and the multisig case (waiting for additional approvals after the first one). |
|
|
249
314
|
| `wait_for_user_to_complete_payment` | The user approved the request; wait for them to finish the funding payment. |
|
|
250
315
|
| `retry_original_x402_request` | Resume this payment id and retry the original x402 request with the merchant payment header. Do not start a new merchant session. |
|
|
251
316
|
| `stop_and_tell_user` | Stop retrying and tell the user the payment failed or was rejected. |
|
package/dist/index.cjs
CHANGED
|
@@ -54,8 +54,16 @@ var AgentPaymentRail = {
|
|
|
54
54
|
Direct: "direct",
|
|
55
55
|
/** x402 HTTP 402 payment flow with a Haven funding leg and merchant retry leg. */
|
|
56
56
|
X402: "x402",
|
|
57
|
-
/** Machine Payment Protocol
|
|
58
|
-
Mpp: "mpp"
|
|
57
|
+
/** Machine Payment Protocol family — categorical value used as a resume-state discriminator. */
|
|
58
|
+
Mpp: "mpp",
|
|
59
|
+
/** Haven internal MPP demo rail. Not for production traffic. */
|
|
60
|
+
MppDemo: "mpp_demo",
|
|
61
|
+
/** Crypto-settled MPP rail. */
|
|
62
|
+
MppCrypto: "mpp_crypto",
|
|
63
|
+
/** Stripe-deposit-backed MPP rail. */
|
|
64
|
+
StripeDeposit: "stripe_deposit",
|
|
65
|
+
/** Stripe Payment Token MPP rail. */
|
|
66
|
+
Spt: "spt"
|
|
59
67
|
};
|
|
60
68
|
var AGENT_PAYMENT_PHASE_VALUES = Object.values(AgentPaymentPhase);
|
|
61
69
|
var AGENT_PAYMENT_NEXT_ACTION_VALUES = Object.values(AgentPaymentNextAction);
|
|
@@ -85,7 +93,11 @@ var AgentPaymentNextActionDescriptions = {
|
|
|
85
93
|
var AgentPaymentRailDescriptions = {
|
|
86
94
|
[AgentPaymentRail.Direct]: "Standard Haven payment from the user-controlled Safe through an approved delegate allowance.",
|
|
87
95
|
[AgentPaymentRail.X402]: "x402 HTTP 402 payment flow with a Haven funding leg and merchant retry leg.",
|
|
88
|
-
[AgentPaymentRail.Mpp]: "
|
|
96
|
+
[AgentPaymentRail.Mpp]: "Categorical MPP rail value used as a resume-state discriminator. Response bodies carry a granular mpp_* value instead.",
|
|
97
|
+
[AgentPaymentRail.MppDemo]: "Haven internal MPP demo rail. Not for production traffic.",
|
|
98
|
+
[AgentPaymentRail.MppCrypto]: "Crypto-settled MPP rail.",
|
|
99
|
+
[AgentPaymentRail.StripeDeposit]: "Stripe-deposit-backed MPP rail.",
|
|
100
|
+
[AgentPaymentRail.Spt]: "Stripe Payment Token MPP rail."
|
|
89
101
|
};
|
|
90
102
|
var AgentPaymentPhaseSchema = {
|
|
91
103
|
type: "string",
|
|
@@ -325,6 +337,23 @@ function selectStandardPaymentOption(accepts) {
|
|
|
325
337
|
}
|
|
326
338
|
return null;
|
|
327
339
|
}
|
|
340
|
+
function x402AuthorizationAmount(option) {
|
|
341
|
+
return option.maxAmountRequired ?? option.amount;
|
|
342
|
+
}
|
|
343
|
+
function buildX402ExpectedMessage(context) {
|
|
344
|
+
return `Haven x402 expected context v1
|
|
345
|
+
${stableStringify({
|
|
346
|
+
version: 1,
|
|
347
|
+
kind: "haven.x402.expected",
|
|
348
|
+
paymentId: context.paymentId,
|
|
349
|
+
payloadHash: context.payloadHash.toLowerCase(),
|
|
350
|
+
resourceUrl: context.resourceUrl,
|
|
351
|
+
merchantTo: context.merchantTo.toLowerCase(),
|
|
352
|
+
amount: context.amount,
|
|
353
|
+
asset: context.asset.toLowerCase(),
|
|
354
|
+
network: context.network
|
|
355
|
+
})}`;
|
|
356
|
+
}
|
|
328
357
|
function toStandardPaymentRequirements(paymentRequired, option) {
|
|
329
358
|
const network = STANDARD_X402_NETWORKS[option.network];
|
|
330
359
|
if (!network) {
|
|
@@ -336,7 +365,7 @@ function toStandardPaymentRequirements(paymentRequired, option) {
|
|
|
336
365
|
return {
|
|
337
366
|
scheme: "exact",
|
|
338
367
|
network,
|
|
339
|
-
maxAmountRequired: option
|
|
368
|
+
maxAmountRequired: x402AuthorizationAmount(option),
|
|
340
369
|
resource: option.resource ?? paymentRequired.resource.url,
|
|
341
370
|
description: option.description ?? paymentRequired.resource.description ?? "Haven x402 payment",
|
|
342
371
|
mimeType: option.mimeType ?? paymentRequired.resource.mimeType ?? "application/octet-stream",
|
|
@@ -353,7 +382,7 @@ function buildX402IdempotencyKey(paymentRequired, option, now = Date.now()) {
|
|
|
353
382
|
paymentRequired.resource.description ?? "",
|
|
354
383
|
option.payTo.toLowerCase(),
|
|
355
384
|
option.asset.toLowerCase(),
|
|
356
|
-
option
|
|
385
|
+
x402AuthorizationAmount(option),
|
|
357
386
|
option.network,
|
|
358
387
|
bucket
|
|
359
388
|
].join("|");
|
|
@@ -382,6 +411,15 @@ function resolveTokenFromAddress(address, network) {
|
|
|
382
411
|
}
|
|
383
412
|
return ALL_TOKENS[lower] ?? null;
|
|
384
413
|
}
|
|
414
|
+
function stableStringify(value) {
|
|
415
|
+
if (value === null || typeof value !== "object") {
|
|
416
|
+
const primitive = JSON.stringify(value);
|
|
417
|
+
return primitive === void 0 ? "undefined" : primitive;
|
|
418
|
+
}
|
|
419
|
+
if (Array.isArray(value)) return `[${value.map((item) => stableStringify(item)).join(",")}]`;
|
|
420
|
+
const object = value;
|
|
421
|
+
return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(object[key])}`).join(",")}}`;
|
|
422
|
+
}
|
|
385
423
|
function decodeBase64Json2(value, label) {
|
|
386
424
|
try {
|
|
387
425
|
return JSON.parse(atob(value));
|
|
@@ -659,6 +697,70 @@ var HavenClient = class {
|
|
|
659
697
|
signData: raw.sign_data
|
|
660
698
|
};
|
|
661
699
|
}
|
|
700
|
+
/**
|
|
701
|
+
* Keyless x402 construct.
|
|
702
|
+
*
|
|
703
|
+
* The non-custodial half of an x402 payment: posts the funding request to
|
|
704
|
+
* `/x402` and returns the unsigned funding hash plus the data the caller
|
|
705
|
+
* needs to build and sign the EIP-3009 merchant header itself. Crucially it
|
|
706
|
+
* does **not** sign — neither the funding hash nor the merchant header — so
|
|
707
|
+
* it works without a `delegateKey`. Both delegate signatures happen on the
|
|
708
|
+
* machine that holds the key (the edge); the hosted MCP server relays only.
|
|
709
|
+
*
|
|
710
|
+
* Use this from the hosted, keyless server. The all-in-one `authorizeX402`
|
|
711
|
+
* remains for local clients that hold the key.
|
|
712
|
+
*
|
|
713
|
+
* Throws (via the shared payment-state path) when the amount exceeds the
|
|
714
|
+
* on-chain allowance — there is nothing to sign until the user approves.
|
|
715
|
+
*/
|
|
716
|
+
async createX402Intent(paymentRequired, options = {}) {
|
|
717
|
+
const option = selectStandardPaymentOption(paymentRequired.accepts);
|
|
718
|
+
if (!option) {
|
|
719
|
+
throw new HavenApiError(
|
|
720
|
+
"No compatible payment option found in x402 requirements. Haven supports standard x402 exact payments on Base USDC.",
|
|
721
|
+
400
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
const agent = await this.getAgent();
|
|
725
|
+
const fundingTo = agent.delegateAddress;
|
|
726
|
+
if (!fundingTo) {
|
|
727
|
+
throw new HavenApiError("Authenticated agent has no delegate address registered.", 502);
|
|
728
|
+
}
|
|
729
|
+
const idempotencyKey = options.idempotencyKey ?? buildX402IdempotencyKey(paymentRequired, option);
|
|
730
|
+
const raw = await this.post("/x402", {
|
|
731
|
+
url: paymentRequired.resource.url,
|
|
732
|
+
payTo: fundingTo,
|
|
733
|
+
merchantPayTo: option.payTo,
|
|
734
|
+
amount: x402AuthorizationAmount(option),
|
|
735
|
+
asset: option.asset,
|
|
736
|
+
network: option.network,
|
|
737
|
+
description: paymentRequired.resource.description,
|
|
738
|
+
idempotencyKey
|
|
739
|
+
});
|
|
740
|
+
if (raw.status !== "pending_signature") {
|
|
741
|
+
this.throwPaymentStateError("x402 payment", raw);
|
|
742
|
+
}
|
|
743
|
+
if (!raw.sign_data?.hash) {
|
|
744
|
+
throw new HavenApiError("No sign_hash returned from x402/authorize", 500, raw);
|
|
745
|
+
}
|
|
746
|
+
if (!raw.x402_expected_auth) {
|
|
747
|
+
throw new HavenApiError("No x402 expected-context binding returned from x402/authorize", 500, raw);
|
|
748
|
+
}
|
|
749
|
+
return {
|
|
750
|
+
paymentId: raw.payment_id,
|
|
751
|
+
status: "pending_signature",
|
|
752
|
+
expiresAt: raw.expires_at,
|
|
753
|
+
signData: raw.sign_data,
|
|
754
|
+
accepted: option,
|
|
755
|
+
resourceUrl: paymentRequired.resource.url,
|
|
756
|
+
merchantTo: raw.merchant_to ?? option.payTo,
|
|
757
|
+
amountAtomic: x402AuthorizationAmount(option),
|
|
758
|
+
asset: option.asset,
|
|
759
|
+
network: option.network,
|
|
760
|
+
expectedAuth: raw.x402_expected_auth,
|
|
761
|
+
fundingTo
|
|
762
|
+
};
|
|
763
|
+
}
|
|
662
764
|
/**
|
|
663
765
|
* Step 2: Sign a hash with the delegate key.
|
|
664
766
|
*
|
|
@@ -882,7 +984,7 @@ var HavenClient = class {
|
|
|
882
984
|
url: paymentRequired.resource.url,
|
|
883
985
|
payTo: this.delegateAddress,
|
|
884
986
|
merchantPayTo: option.payTo,
|
|
885
|
-
amount: option
|
|
987
|
+
amount: x402AuthorizationAmount(option),
|
|
886
988
|
asset: option.asset,
|
|
887
989
|
network: option.network,
|
|
888
990
|
description: paymentRequired.resource.description,
|
|
@@ -1076,7 +1178,7 @@ var HavenClient = class {
|
|
|
1076
1178
|
...initialInit,
|
|
1077
1179
|
headers: retryHeaders
|
|
1078
1180
|
});
|
|
1079
|
-
if (retryResponse.
|
|
1181
|
+
if (!retryResponse.ok) {
|
|
1080
1182
|
await this.recordMerchantRetryRejected({
|
|
1081
1183
|
rail: "x402",
|
|
1082
1184
|
paymentId: receipt.paymentId,
|
|
@@ -1089,8 +1191,8 @@ var HavenClient = class {
|
|
|
1089
1191
|
}
|
|
1090
1192
|
});
|
|
1091
1193
|
throw new HavenApiError(
|
|
1092
|
-
"x402 retry
|
|
1093
|
-
|
|
1194
|
+
"x402 retry failed after Haven funded the delegate wallet; reconciliation may be required.",
|
|
1195
|
+
retryResponse.status,
|
|
1094
1196
|
{
|
|
1095
1197
|
marker: "x402_retry_rejected_after_funding",
|
|
1096
1198
|
payment_id: receipt.paymentId,
|
|
@@ -1229,7 +1331,7 @@ var HavenClient = class {
|
|
|
1229
1331
|
...initialInit,
|
|
1230
1332
|
headers: retryHeaders
|
|
1231
1333
|
});
|
|
1232
|
-
if (retryResponse.
|
|
1334
|
+
if (!retryResponse.ok) {
|
|
1233
1335
|
await this.recordMerchantRetryRejected({
|
|
1234
1336
|
rail: receipt.rail,
|
|
1235
1337
|
paymentId: receipt.paymentId,
|
|
@@ -1241,8 +1343,8 @@ var HavenClient = class {
|
|
|
1241
1343
|
}
|
|
1242
1344
|
});
|
|
1243
1345
|
throw new HavenApiError(
|
|
1244
|
-
"Machine payment retry
|
|
1245
|
-
|
|
1346
|
+
"Machine payment retry failed after Haven sent the payment.",
|
|
1347
|
+
retryResponse.status,
|
|
1246
1348
|
{
|
|
1247
1349
|
marker: "machine_payment_retry_rejected_after_payment",
|
|
1248
1350
|
payment_id: receipt.paymentId,
|
|
@@ -1319,7 +1421,7 @@ var HavenClient = class {
|
|
|
1319
1421
|
);
|
|
1320
1422
|
}
|
|
1321
1423
|
const approvedAmount = status.amount ? normalizeDecimal(status.amount) : "";
|
|
1322
|
-
const requestedAmount = normalizeDecimal(decimalFromUsdcAtomic(option
|
|
1424
|
+
const requestedAmount = normalizeDecimal(decimalFromUsdcAtomic(x402AuthorizationAmount(option)));
|
|
1323
1425
|
if (approvedAmount && approvedAmount !== requestedAmount) {
|
|
1324
1426
|
throw new HavenApiError(
|
|
1325
1427
|
"x402 resume request does not match the approved amount.",
|
|
@@ -1395,7 +1497,7 @@ var HavenClient = class {
|
|
|
1395
1497
|
const txHash = execResult?.tx_hash ?? raw.tx_hash ?? "";
|
|
1396
1498
|
const chainId = execResult?.chain_id ?? raw.chain_id ?? chainIdFromNetwork(option.network);
|
|
1397
1499
|
const token = execResult?.token ?? raw.token ?? "USDC";
|
|
1398
|
-
const amount = execResult?.amount ?? raw.amount ?? decimalFromUsdcAtomic(option
|
|
1500
|
+
const amount = execResult?.amount ?? raw.amount ?? decimalFromUsdcAtomic(x402AuthorizationAmount(option));
|
|
1399
1501
|
const to = execResult?.to ?? raw.to ?? this.delegateAddress ?? "";
|
|
1400
1502
|
const explorerUrl = execResult?.explorer_url ?? raw.explorer_url ?? explorerUrlOrEmpty(chainId, txHash);
|
|
1401
1503
|
const merchantTo = execResult?.merchant_to ?? raw.merchant_to ?? option.payTo;
|
|
@@ -1428,7 +1530,7 @@ var HavenClient = class {
|
|
|
1428
1530
|
paymentId: status.paymentId,
|
|
1429
1531
|
txHash: status.txHash,
|
|
1430
1532
|
token: status.token || "USDC",
|
|
1431
|
-
amount: status.amount || decimalFromUsdcAtomic(option
|
|
1533
|
+
amount: status.amount || decimalFromUsdcAtomic(x402AuthorizationAmount(option)),
|
|
1432
1534
|
to: this.delegateAddress ?? "",
|
|
1433
1535
|
resourceUrl: paymentRequired.resource.url,
|
|
1434
1536
|
explorerUrl: explorerUrlOrEmpty(status.chainId, status.txHash),
|
|
@@ -1464,7 +1566,7 @@ var HavenClient = class {
|
|
|
1464
1566
|
payTo: input.merchantTo ?? input.accepted.payTo
|
|
1465
1567
|
},
|
|
1466
1568
|
x402: {
|
|
1467
|
-
amount: input.accepted
|
|
1569
|
+
amount: x402AuthorizationAmount(input.accepted),
|
|
1468
1570
|
token: input.token,
|
|
1469
1571
|
network: input.accepted.network,
|
|
1470
1572
|
asset: input.accepted.asset,
|
|
@@ -1714,8 +1816,8 @@ var HavenClient = class {
|
|
|
1714
1816
|
resourceUrl: paymentRequired.resource.url,
|
|
1715
1817
|
description: paymentRequired.resource.description ?? option.description ?? null,
|
|
1716
1818
|
mimeType: paymentRequired.resource.mimeType ?? option.mimeType ?? null,
|
|
1717
|
-
amountAtomic: option
|
|
1718
|
-
amount: decimalFromUsdcAtomic(option
|
|
1819
|
+
amountAtomic: x402AuthorizationAmount(option),
|
|
1820
|
+
amount: decimalFromUsdcAtomic(x402AuthorizationAmount(option)),
|
|
1719
1821
|
token: token?.symbol ?? "USDC",
|
|
1720
1822
|
asset: option.asset,
|
|
1721
1823
|
network: option.network,
|
|
@@ -1736,8 +1838,8 @@ var HavenClient = class {
|
|
|
1736
1838
|
request: input.request,
|
|
1737
1839
|
resourceUrl: input.paymentRequired.resource.url,
|
|
1738
1840
|
description: input.paymentRequired.resource.description ?? input.accepted.description ?? null,
|
|
1739
|
-
amountAtomic: input.accepted
|
|
1740
|
-
amount: decimalFromUsdcAtomic(input.accepted
|
|
1841
|
+
amountAtomic: x402AuthorizationAmount(input.accepted),
|
|
1842
|
+
amount: decimalFromUsdcAtomic(x402AuthorizationAmount(input.accepted)),
|
|
1741
1843
|
token: token?.symbol ?? "USDC",
|
|
1742
1844
|
asset: input.accepted.asset,
|
|
1743
1845
|
network: input.accepted.network,
|
|
@@ -1932,6 +2034,9 @@ var HavenClient = class {
|
|
|
1932
2034
|
message: result.message
|
|
1933
2035
|
};
|
|
1934
2036
|
}
|
|
2037
|
+
if (toolName === "get_allowances") {
|
|
2038
|
+
return { ...await this.getAllowances() };
|
|
2039
|
+
}
|
|
1935
2040
|
throw new Error(`Unknown tool: ${toolName}`);
|
|
1936
2041
|
}
|
|
1937
2042
|
toolX402PaymentRequired(input) {
|
|
@@ -2122,7 +2227,7 @@ var HavenClient = class {
|
|
|
2122
2227
|
};
|
|
2123
2228
|
}
|
|
2124
2229
|
mapPaymentReceipt(raw) {
|
|
2125
|
-
|
|
2230
|
+
const receipt = {
|
|
2126
2231
|
id: raw.id,
|
|
2127
2232
|
paymentId: raw.payment_id,
|
|
2128
2233
|
rail: raw.rail,
|
|
@@ -2149,6 +2254,13 @@ var HavenClient = class {
|
|
|
2149
2254
|
createdAt: raw.created_at,
|
|
2150
2255
|
updatedAt: raw.updated_at
|
|
2151
2256
|
};
|
|
2257
|
+
if ("payment_intent_id" in raw) {
|
|
2258
|
+
receipt.paymentIntentId = raw.payment_intent_id ?? null;
|
|
2259
|
+
}
|
|
2260
|
+
if ("approval_request_id" in raw) {
|
|
2261
|
+
receipt.approvalRequestId = raw.approval_request_id ?? null;
|
|
2262
|
+
}
|
|
2263
|
+
return receipt;
|
|
2152
2264
|
}
|
|
2153
2265
|
};
|
|
2154
2266
|
function sleep(ms) {
|
|
@@ -2189,6 +2301,72 @@ async function responseSnippet(response) {
|
|
|
2189
2301
|
}
|
|
2190
2302
|
}
|
|
2191
2303
|
|
|
2304
|
+
// src/tool-descriptions.ts
|
|
2305
|
+
function composeDescription(d) {
|
|
2306
|
+
return [d.summary, d.selectionGuidance, d.behavior, d.nextActionGuidance].filter(Boolean).join(" ");
|
|
2307
|
+
}
|
|
2308
|
+
var toolDescriptions = {
|
|
2309
|
+
quoteX402: {
|
|
2310
|
+
summary: "Inspect an HTTP 402 x402 paid resource without creating a Haven payment, signature, approval, or on-chain transaction.",
|
|
2311
|
+
behavior: "Probes the merchant directly and parses the 402 response. Pure read-only client behavior \u2014 Haven is not contacted.",
|
|
2312
|
+
nextActionGuidance: ""
|
|
2313
|
+
},
|
|
2314
|
+
payX402: {
|
|
2315
|
+
summary: "Pay an inspected x402 quote. The delegate key signs locally; Haven only validates and relays signed, on-chain-constrained payment transactions.",
|
|
2316
|
+
selectionGuidance: "Do not use this for read-only allowance, budget, spend-limit, remaining-amount, reset-period, or what-can-I-spend questions; use the allowance lookup tool instead.",
|
|
2317
|
+
behavior: "Signs the EIP-3009 payment from the delegate wallet, asks Haven for a Safe AllowanceModule top-up if needed, and returns the merchant response or a pending-approval state.",
|
|
2318
|
+
nextActionGuidance: "If approval is needed, preserve the returned resume_state and wait for nextAction=retry_original_x402_request before resuming."
|
|
2319
|
+
},
|
|
2320
|
+
resumeX402: {
|
|
2321
|
+
summary: "Resume an x402 payment after the Haven wallet owner approved the funding step.",
|
|
2322
|
+
behavior: "Accepts either resume_state or payment_id, validates the original x402 details against the approved Haven funding, and retries the merchant request with the X-PAYMENT header. No new Haven approval is created.",
|
|
2323
|
+
nextActionGuidance: "Only use when get_payment_status returns nextAction=retry_original_x402_request; do not start a new merchant session."
|
|
2324
|
+
},
|
|
2325
|
+
quoteMpp: {
|
|
2326
|
+
summary: "Inspect a Haven MPP challenge or paid MPP URL without creating a Haven payment, signature, approval, or on-chain transaction.",
|
|
2327
|
+
behavior: "Parses an MPP challenge envelope and returns a typed quote with rail tag, amount, asset, and merchant context. Pure read-only \u2014 Haven is not contacted.",
|
|
2328
|
+
nextActionGuidance: ""
|
|
2329
|
+
},
|
|
2330
|
+
payMpp: {
|
|
2331
|
+
summary: "Pay an inspected MPP challenge. The delegate key signs locally; Haven only validates and relays signed, on-chain-constrained payment transactions.",
|
|
2332
|
+
selectionGuidance: "Do not use this for read-only allowance, budget, spend-limit, remaining-amount, reset-period, or what-can-I-spend questions; use the allowance lookup tool instead.",
|
|
2333
|
+
behavior: "Authorizes the payment through Haven within the on-chain allowance, signs the challenge proof, and returns the proof header for retrying the original paid resource.",
|
|
2334
|
+
nextActionGuidance: "If approval is needed, preserve resume_state or payment_id and wait for nextAction=retry_original_x402_request before resuming."
|
|
2335
|
+
},
|
|
2336
|
+
resumeMpp: {
|
|
2337
|
+
summary: "Resume an MPP payment after the Haven wallet owner approved the funding step.",
|
|
2338
|
+
behavior: "Accepts either resume_state or payment_id and retries the original paid resource with the MPP proof header. No new Haven approval is created.",
|
|
2339
|
+
nextActionGuidance: ""
|
|
2340
|
+
},
|
|
2341
|
+
getPaymentStatus: {
|
|
2342
|
+
summary: "Fetch structured Haven payment status, including phase and nextAction taxonomy for agent recovery.",
|
|
2343
|
+
behavior: "Accepts a payment intent or approval request id and returns the full state taxonomy (phase, nextAction, rail, amount, merchant, resource url, idempotency key, message).",
|
|
2344
|
+
nextActionGuidance: ""
|
|
2345
|
+
},
|
|
2346
|
+
getResumeState: {
|
|
2347
|
+
summary: "Rehydrate stored x402 or MPP resume_state by payment_id.",
|
|
2348
|
+
behavior: "Returns the context that the agent originally received in a pending-approval response, reconstructed from Haven's database. This is context only; signing still happens locally when a resume tool is called.",
|
|
2349
|
+
nextActionGuidance: ""
|
|
2350
|
+
},
|
|
2351
|
+
getAgent: {
|
|
2352
|
+
summary: "Return the authenticated agent identity, Haven wallet, delegate address, chain, and status.",
|
|
2353
|
+
behavior: "Read-only identity lookup. Useful for verifying which on-chain Safe and delegate the credential is bound to.",
|
|
2354
|
+
nextActionGuidance: ""
|
|
2355
|
+
},
|
|
2356
|
+
getAllowances: {
|
|
2357
|
+
summary: "Return configured and on-chain allowance state for the authenticated agent. On-chain allowance is the real spend gate.",
|
|
2358
|
+
selectionGuidance: "Use this when the user asks about allowance, budget, spend limit, remaining amount, remaining allowance, remaining budget, daily limit, reset period, what can I spend, or what the agent can still spend.",
|
|
2359
|
+
behavior: "Reads the Safe AllowanceModule snapshot per token (allowance, spent, remaining, reset window). Configured amounts from Haven are returned alongside the on-chain truth.",
|
|
2360
|
+
nextActionGuidance: ""
|
|
2361
|
+
},
|
|
2362
|
+
listReceipts: {
|
|
2363
|
+
summary: "List recent machine-payment receipts and evidence for bookkeeping.",
|
|
2364
|
+
selectionGuidance: "Use this for transaction history, receipts, payment evidence, or bookkeeping; use the allowance tool instead for remaining allowance, budget, spend-limit, or what-can-I-spend questions.",
|
|
2365
|
+
behavior: "Returns the agent's recent machine-payment receipts ordered by recency. Proof header values are not returned.",
|
|
2366
|
+
nextActionGuidance: ""
|
|
2367
|
+
}
|
|
2368
|
+
};
|
|
2369
|
+
|
|
2192
2370
|
// src/tools.ts
|
|
2193
2371
|
var makePaymentSchema = {
|
|
2194
2372
|
type: "object",
|
|
@@ -2222,6 +2400,11 @@ var getPaymentStatusSchema = {
|
|
|
2222
2400
|
},
|
|
2223
2401
|
required: ["payment_id"]
|
|
2224
2402
|
};
|
|
2403
|
+
var getAllowancesSchema = {
|
|
2404
|
+
type: "object",
|
|
2405
|
+
properties: {},
|
|
2406
|
+
required: []
|
|
2407
|
+
};
|
|
2225
2408
|
var authorizeX402Schema = {
|
|
2226
2409
|
type: "object",
|
|
2227
2410
|
properties: {
|
|
@@ -2304,11 +2487,12 @@ var authorizeMachinePaymentSchema = {
|
|
|
2304
2487
|
},
|
|
2305
2488
|
required: ["challenge"]
|
|
2306
2489
|
};
|
|
2307
|
-
var MAKE_PAYMENT_DESCRIPTION = "Request and sign a payment from the user-controlled Safe within approved on-chain limits. Haven authenticates the agent, validates the signed intent, and relays the Safe AllowanceModule transaction; it does not hold keys or control funds. Gnosis Chain tokens: EURe, USDC.e, xDAI. Base tokens: USDC, ETH.";
|
|
2308
|
-
var GET_STATUS_DESCRIPTION =
|
|
2309
|
-
var
|
|
2310
|
-
var
|
|
2311
|
-
var
|
|
2490
|
+
var MAKE_PAYMENT_DESCRIPTION = "Request and sign a payment from the user-controlled Safe within approved on-chain limits. For read-only allowance, budget, spend-limit, remaining-amount, or reset-period questions, use get_allowances instead of making a payment. Haven authenticates the agent, validates the signed intent, and relays the Safe AllowanceModule transaction; it does not hold keys or control funds. Gnosis Chain tokens: EURe, USDC.e, xDAI. Base tokens: USDC, ETH.";
|
|
2491
|
+
var GET_STATUS_DESCRIPTION = toolDescriptions.getPaymentStatus.summary + " Accepts payment intent IDs and approval request IDs. Returns the current status, phase, next_action, transaction hash if available, and payment details.";
|
|
2492
|
+
var GET_ALLOWANCES_DESCRIPTION = composeDescription(toolDescriptions.getAllowances);
|
|
2493
|
+
var AUTHORIZE_X402_DESCRIPTION = composeDescription(toolDescriptions.payX402) + " In this SDK tool set, the allowance lookup tool is get_allowances. When a paid API returns x402 payment requirements, use this tool to sign with the agent-owned delegate key and request a policy-limited Safe AllowanceModule top-up when needed. Haven relays signed transactions only; the agent key authorizes payment and on-chain limits enforce spend. If this returns pending_approval, tell the user it is waiting in Haven, preserve the original merchant/MCP session and x402 details, call get_payment_status later, and use resume_x402_payment only when next_action is retry_original_x402_request. Do not start a new merchant session or loop retries while approval is pending. Use the returned payment_header as the X-PAYMENT header on the retry request when doing a manual HTTP retry.";
|
|
2494
|
+
var RESUME_X402_DESCRIPTION = toolDescriptions.resumeX402.summary + " Use this only after get_payment_status returns next_action=retry_original_x402_request. It checks the approved payment, validates the original x402 details, and returns a merchant X-PAYMENT header without creating a new approval request or merchant session.";
|
|
2495
|
+
var AUTHORIZE_MACHINE_PAYMENT_DESCRIPTION = composeDescription(toolDescriptions.payMpp) + " In this SDK tool set, the allowance lookup tool is get_allowances. Currently scoped to the internal MPP demo rail. The agent signs the payment, Haven relays it within the on-chain allowance, and the tool returns a proof header for the retry request.";
|
|
2312
2496
|
function claudeTools() {
|
|
2313
2497
|
return [
|
|
2314
2498
|
{
|
|
@@ -2321,6 +2505,11 @@ function claudeTools() {
|
|
|
2321
2505
|
description: GET_STATUS_DESCRIPTION,
|
|
2322
2506
|
input_schema: getPaymentStatusSchema
|
|
2323
2507
|
},
|
|
2508
|
+
{
|
|
2509
|
+
name: "get_allowances",
|
|
2510
|
+
description: GET_ALLOWANCES_DESCRIPTION,
|
|
2511
|
+
input_schema: getAllowancesSchema
|
|
2512
|
+
},
|
|
2324
2513
|
{
|
|
2325
2514
|
name: "authorize_x402_payment",
|
|
2326
2515
|
description: AUTHORIZE_X402_DESCRIPTION,
|
|
@@ -2356,6 +2545,14 @@ function openaiTools() {
|
|
|
2356
2545
|
parameters: getPaymentStatusSchema
|
|
2357
2546
|
}
|
|
2358
2547
|
},
|
|
2548
|
+
{
|
|
2549
|
+
type: "function",
|
|
2550
|
+
function: {
|
|
2551
|
+
name: "get_allowances",
|
|
2552
|
+
description: GET_ALLOWANCES_DESCRIPTION,
|
|
2553
|
+
parameters: getAllowancesSchema
|
|
2554
|
+
}
|
|
2555
|
+
},
|
|
2359
2556
|
{
|
|
2360
2557
|
type: "function",
|
|
2361
2558
|
function: {
|
|
@@ -2409,6 +2606,8 @@ exports.HavenSigningError = HavenSigningError;
|
|
|
2409
2606
|
exports.HavenTimeoutError = HavenTimeoutError;
|
|
2410
2607
|
exports.addressFromKey = addressFromKey;
|
|
2411
2608
|
exports.buildMachinePaymentIdempotencyKey = buildMachinePaymentIdempotencyKey;
|
|
2609
|
+
exports.buildX402ExpectedMessage = buildX402ExpectedMessage;
|
|
2610
|
+
exports.composeDescription = composeDescription;
|
|
2412
2611
|
exports.encodeMachinePaymentProof = encodeMachinePaymentProof;
|
|
2413
2612
|
exports.encodePaymentProof = encodePaymentProof;
|
|
2414
2613
|
exports.havenTools = havenTools;
|
|
@@ -2417,7 +2616,11 @@ exports.parseMachinePaymentChallengeResponse = parseMachinePaymentChallengeRespo
|
|
|
2417
2616
|
exports.parsePaymentRequired = parsePaymentRequired;
|
|
2418
2617
|
exports.parsePaymentRequiredResponse = parsePaymentRequiredResponse;
|
|
2419
2618
|
exports.selectPaymentOption = selectPaymentOption;
|
|
2619
|
+
exports.selectStandardPaymentOption = selectStandardPaymentOption;
|
|
2420
2620
|
exports.signHash = signHash;
|
|
2621
|
+
exports.toStandardPaymentRequirements = toStandardPaymentRequirements;
|
|
2622
|
+
exports.toolDescriptions = toolDescriptions;
|
|
2421
2623
|
exports.verifySignature = verifySignature;
|
|
2624
|
+
exports.x402AuthorizationAmount = x402AuthorizationAmount;
|
|
2422
2625
|
//# sourceMappingURL=index.cjs.map
|
|
2423
2626
|
//# sourceMappingURL=index.cjs.map
|