@mixrpay/agent-sdk 0.5.0 → 0.6.1

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 CHANGED
@@ -41,9 +41,13 @@ await wallet.fetch(url, options);
41
41
  // Get wallet balance
42
42
  const balance = await wallet.getBalance();
43
43
 
44
- // Track payments
45
- const payments = wallet.getPaymentHistory();
46
- const total = wallet.getTotalSpent();
44
+ // Pre-flight balance check
45
+ const check = await wallet.canAfford(0.50);
46
+ if (check.canAfford) { /* proceed */ }
47
+
48
+ // Run diagnostics
49
+ const diag = await wallet.runDiagnostics();
50
+ console.log(diag.recommendations);
47
51
  ```
48
52
 
49
53
  ## Configuration
@@ -63,6 +67,7 @@ import {
63
67
  AgentWallet,
64
68
  InsufficientBalanceError,
65
69
  SessionLimitExceededError,
70
+ MixrPayError,
66
71
  } from '@mixrpay/agent-sdk';
67
72
 
68
73
  try {
@@ -71,12 +76,27 @@ try {
71
76
  if (error instanceof InsufficientBalanceError) {
72
77
  console.log(`Need $${error.required}, have $${error.available}`);
73
78
  }
79
+
80
+ // Check if error is retryable
81
+ if (error instanceof MixrPayError && error.isRetryable()) {
82
+ const delay = error.retryAfterMs || 1000;
83
+ await sleep(delay);
84
+ return retry();
85
+ }
74
86
  }
75
87
  ```
76
88
 
89
+ ## What's New in v0.6.0
90
+
91
+ - `canAfford(amountUsd)` - Pre-flight balance check
92
+ - `isRetryable()` on all errors - Determine if operation should be retried
93
+ - `retryAfterMs` on errors - Suggested retry delay
94
+ - Enhanced `runDiagnostics()` - Session limits, latency, recommendations
95
+ - `requestId` and `correlationId` in payment events
96
+
77
97
  ## Documentation
78
98
 
79
- [mixrpay.com/build](https://www.mixrpay.com/build)
99
+ [mixrpay.com/docs](https://www.mixrpay.com/docs)
80
100
 
81
101
  ## License
82
102
 
package/dist/index.cjs CHANGED
@@ -32,6 +32,8 @@ __export(index_exports, {
32
32
  SessionNotFoundError: () => SessionNotFoundError,
33
33
  SessionRevokedError: () => SessionRevokedError,
34
34
  SpendingLimitExceededError: () => SpendingLimitExceededError,
35
+ X402ProtocolError: () => X402ProtocolError,
36
+ getErrorMessage: () => getErrorMessage,
35
37
  isMixrPayError: () => isMixrPayError
36
38
  });
37
39
  module.exports = __toCommonJS(index_exports);
@@ -43,14 +45,43 @@ var import_accounts = require("viem/accounts");
43
45
  var MixrPayError = class extends Error {
44
46
  /** Error code for programmatic handling */
45
47
  code;
46
- constructor(message, code = "MIXRPAY_ERROR") {
48
+ /** Optional hint for how long to wait before retrying (in milliseconds) */
49
+ retryAfterMs;
50
+ constructor(message, code = "MIXRPAY_ERROR", retryAfterMs) {
47
51
  super(message);
48
52
  this.name = "MixrPayError";
49
53
  this.code = code;
54
+ this.retryAfterMs = retryAfterMs;
50
55
  if (Error.captureStackTrace) {
51
56
  Error.captureStackTrace(this, this.constructor);
52
57
  }
53
58
  }
59
+ /**
60
+ * Check if this error is retryable.
61
+ *
62
+ * Returns true if the operation might succeed on retry (e.g., transient network issues).
63
+ * Returns false if the error requires user action to resolve (e.g., insufficient balance).
64
+ *
65
+ * @returns true if the operation should be retried, false otherwise
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * try {
70
+ * await wallet.fetch(...);
71
+ * } catch (error) {
72
+ * if (error instanceof MixrPayError && error.isRetryable()) {
73
+ * // Retry the operation
74
+ * await retry();
75
+ * } else {
76
+ * // Handle permanent failure
77
+ * throw error;
78
+ * }
79
+ * }
80
+ * ```
81
+ */
82
+ isRetryable() {
83
+ return false;
84
+ }
54
85
  };
55
86
  var InsufficientBalanceError = class extends MixrPayError {
56
87
  /** Amount required for the payment in USD */
@@ -108,6 +139,14 @@ var SpendingLimitExceededError = class extends MixrPayError {
108
139
  this.limit = limit;
109
140
  this.attempted = attempted;
110
141
  }
142
+ /**
143
+ * Daily limits reset at midnight, so those are retryable (after waiting).
144
+ * Other limit types require user action (new session key, config change).
145
+ * @returns true only for daily limits
146
+ */
147
+ isRetryable() {
148
+ return this.limitType === "daily";
149
+ }
111
150
  };
112
151
  var PaymentFailedError = class extends MixrPayError {
113
152
  /** Detailed reason for the failure */
@@ -124,6 +163,13 @@ var PaymentFailedError = class extends MixrPayError {
124
163
  this.reason = reason;
125
164
  this.txHash = txHash;
126
165
  }
166
+ /**
167
+ * Payment failures are often transient (network issues, temporary server errors).
168
+ * @returns true - payment failures should generally be retried
169
+ */
170
+ isRetryable() {
171
+ return true;
172
+ }
127
173
  };
128
174
  var InvalidSessionKeyError = class extends MixrPayError {
129
175
  /** Detailed reason why the key is invalid */
@@ -148,6 +194,13 @@ var X402ProtocolError = class extends MixrPayError {
148
194
  this.name = "X402ProtocolError";
149
195
  this.reason = reason;
150
196
  }
197
+ /**
198
+ * Protocol errors may be caused by temporary server issues.
199
+ * @returns true - worth retrying in case server recovers
200
+ */
201
+ isRetryable() {
202
+ return true;
203
+ }
151
204
  };
152
205
  var SessionExpiredError = class extends MixrPayError {
153
206
  /** ID of the expired session */
@@ -215,6 +268,15 @@ var SessionRevokedError = class extends MixrPayError {
215
268
  function isMixrPayError(error) {
216
269
  return error instanceof MixrPayError;
217
270
  }
271
+ function getErrorMessage(error) {
272
+ if (error instanceof MixrPayError) {
273
+ return error.message;
274
+ }
275
+ if (error instanceof Error) {
276
+ return error.message;
277
+ }
278
+ return String(error);
279
+ }
218
280
 
219
281
  // src/session-key.ts
220
282
  var USDC_ADDRESSES = {
@@ -445,7 +507,7 @@ function getAmountUsd(requirements) {
445
507
  }
446
508
 
447
509
  // src/agent-wallet.ts
448
- var SDK_VERSION = "0.5.0";
510
+ var SDK_VERSION = "0.6.1";
449
511
  var DEFAULT_BASE_URL = process.env.MIXRPAY_BASE_URL || "https://www.mixrpay.com";
450
512
  var DEFAULT_TIMEOUT = 3e4;
451
513
  var NETWORKS = {
@@ -637,7 +699,19 @@ var AgentWallet = class {
637
699
  });
638
700
  if (!registerResponse.ok) {
639
701
  const error = await registerResponse.json().catch(() => ({}));
640
- throw new MixrPayError(error.error || `Registration failed: ${registerResponse.status}`);
702
+ const errorMessage = error.error || `Registration failed with status ${registerResponse.status}`;
703
+ const requestId = error.request_id;
704
+ const errorCode = error.code;
705
+ let helpText = "";
706
+ if (registerResponse.status === 503) {
707
+ helpText = " The service may be temporarily unavailable. Please try again later.";
708
+ } else if (registerResponse.status === 500) {
709
+ helpText = " This is a server error. Please contact support with the request ID.";
710
+ } else if (errorCode === "MISSING_CHALLENGE" || errorCode === "MISSING_SIGNATURE") {
711
+ helpText = " This may indicate an SDK bug. Please update to the latest version.";
712
+ }
713
+ const fullMessage = requestId ? `${errorMessage} (request_id: ${requestId})${helpText}` : `${errorMessage}${helpText}`;
714
+ throw new MixrPayError(fullMessage);
641
715
  }
642
716
  const data = await registerResponse.json();
643
717
  return {
@@ -645,6 +719,52 @@ var AgentWallet = class {
645
719
  depositAddress: data.deposit_address
646
720
  };
647
721
  }
722
+ /**
723
+ * Check if the MixrPay server is properly configured for agent registration.
724
+ *
725
+ * Use this to diagnose registration issues before attempting to register.
726
+ *
727
+ * @param baseUrl - MixrPay API base URL (default: https://www.mixrpay.com)
728
+ * @returns Server health status including agent registration availability
729
+ *
730
+ * @example
731
+ * ```typescript
732
+ * const status = await AgentWallet.checkServerHealth();
733
+ * if (!status.agentRegistrationAvailable) {
734
+ * console.error('Agent registration is not available:', status);
735
+ * }
736
+ * ```
737
+ */
738
+ static async checkServerHealth(baseUrl) {
739
+ const url = (baseUrl || DEFAULT_BASE_URL).replace(/\/$/, "");
740
+ try {
741
+ const response = await fetch(`${url}/api/health/ready?details=true`);
742
+ if (!response.ok) {
743
+ return {
744
+ healthy: false,
745
+ database: "unknown",
746
+ agentRegistrationAvailable: false,
747
+ privyConfigured: false,
748
+ error: `Health check failed with status ${response.status}`
749
+ };
750
+ }
751
+ const data = await response.json();
752
+ return {
753
+ healthy: data.status === "ready",
754
+ database: data.database || "unknown",
755
+ agentRegistrationAvailable: data.services?.agentRegistration?.available ?? false,
756
+ privyConfigured: data.services?.privy?.configured ?? false
757
+ };
758
+ } catch (error) {
759
+ return {
760
+ healthy: false,
761
+ database: "unreachable",
762
+ agentRegistrationAvailable: false,
763
+ privyConfigured: false,
764
+ error: error instanceof Error ? error.message : "Failed to reach server"
765
+ };
766
+ }
767
+ }
648
768
  /**
649
769
  * Get a session key for an already-registered agent.
650
770
  *
@@ -819,6 +939,67 @@ var AgentWallet = class {
819
939
  }
820
940
  return true;
821
941
  }
942
+ /**
943
+ * Withdraw USDC from agent's MixrPay wallet to their external wallet.
944
+ *
945
+ * SECURITY: Withdrawals can ONLY go to the agent's own registration wallet
946
+ * (the wallet used during `register()`). This prevents prompt injection
947
+ * attacks where a compromised agent might be tricked into withdrawing
948
+ * to an attacker's address.
949
+ *
950
+ * @param options - Withdrawal options
951
+ * @returns Withdrawal result with transaction hash
952
+ * @throws {MixrPayError} If withdrawal fails
953
+ *
954
+ * @example
955
+ * ```typescript
956
+ * const result = await AgentWallet.withdraw({
957
+ * privateKey: process.env.AGENT_WALLET_KEY as `0x${string}`,
958
+ * amountUsd: 50.00,
959
+ * });
960
+ *
961
+ * console.log(`Withdrew $${result.amountUsd}`);
962
+ * console.log(`Transaction: ${result.txHash}`);
963
+ * console.log(`Remaining balance: $${result.remainingBalanceUsd}`);
964
+ * ```
965
+ */
966
+ static async withdraw(options) {
967
+ const { privateKey, amountUsd } = options;
968
+ const baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/$/, "");
969
+ const account = (0, import_accounts2.privateKeyToAccount)(privateKey);
970
+ const walletAddress = account.address;
971
+ const challengeResponse = await fetch(
972
+ `${baseUrl}/api/v1/agent/challenge?wallet=${walletAddress}&action=withdraw`
973
+ );
974
+ if (!challengeResponse.ok) {
975
+ const error = await challengeResponse.json().catch(() => ({}));
976
+ throw new MixrPayError(error.error || `Failed to get challenge: ${challengeResponse.status}`);
977
+ }
978
+ const { challenge, message } = await challengeResponse.json();
979
+ const signature = await (0, import_accounts2.signMessage)({ message, privateKey });
980
+ const withdrawResponse = await fetch(`${baseUrl}/api/v1/agent/withdraw`, {
981
+ method: "POST",
982
+ headers: { "Content-Type": "application/json" },
983
+ body: JSON.stringify({
984
+ challenge,
985
+ external_wallet: walletAddress,
986
+ signature,
987
+ to_address: walletAddress,
988
+ // Always withdraw to self
989
+ amount_usd: amountUsd
990
+ })
991
+ });
992
+ if (!withdrawResponse.ok) {
993
+ const error = await withdrawResponse.json().catch(() => ({}));
994
+ throw new MixrPayError(error.error || `Withdrawal failed: ${withdrawResponse.status}`);
995
+ }
996
+ const data = await withdrawResponse.json();
997
+ return {
998
+ txHash: data.tx_hash,
999
+ amountUsd: data.amount_usd,
1000
+ remainingBalanceUsd: data.remaining_balance_usd
1001
+ };
1002
+ }
822
1003
  // ===========================================================================
823
1004
  // Core Methods
824
1005
  // ===========================================================================
@@ -857,6 +1038,8 @@ var AgentWallet = class {
857
1038
  */
858
1039
  async fetch(url, init) {
859
1040
  this.logger.debug(`Fetching ${init?.method || "GET"} ${url}`);
1041
+ const requestId = crypto.randomUUID();
1042
+ const correlationId = this.extractCorrelationId(init?.headers);
860
1043
  const controller = new AbortController();
861
1044
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
862
1045
  try {
@@ -867,7 +1050,7 @@ var AgentWallet = class {
867
1050
  this.logger.debug(`Initial response: ${response.status}`);
868
1051
  if (response.status === 402) {
869
1052
  this.logger.info(`Payment required for ${url}`);
870
- response = await this.handlePaymentRequired(url, init, response);
1053
+ response = await this.handlePaymentRequired(url, init, response, requestId, correlationId);
871
1054
  }
872
1055
  return response;
873
1056
  } catch (error) {
@@ -879,10 +1062,27 @@ var AgentWallet = class {
879
1062
  clearTimeout(timeoutId);
880
1063
  }
881
1064
  }
1065
+ /**
1066
+ * Extract correlation ID from request headers.
1067
+ */
1068
+ extractCorrelationId(headers) {
1069
+ if (!headers) return void 0;
1070
+ if (headers instanceof Headers) {
1071
+ return headers.get("X-Correlation-Id") || headers.get("x-correlation-id") || void 0;
1072
+ }
1073
+ if (Array.isArray(headers)) {
1074
+ const entry = headers.find(
1075
+ ([key]) => key.toLowerCase() === "x-correlation-id"
1076
+ );
1077
+ return entry ? entry[1] : void 0;
1078
+ }
1079
+ const record = headers;
1080
+ return record["X-Correlation-Id"] || record["x-correlation-id"] || void 0;
1081
+ }
882
1082
  /**
883
1083
  * Handle a 402 Payment Required response.
884
1084
  */
885
- async handlePaymentRequired(url, init, response) {
1085
+ async handlePaymentRequired(url, init, response, requestId, correlationId) {
886
1086
  let requirements;
887
1087
  try {
888
1088
  requirements = await parse402Response(response);
@@ -938,7 +1138,9 @@ var AgentWallet = class {
938
1138
  txHash: retryResponse.headers.get("X-Payment-TxHash"),
939
1139
  timestamp: /* @__PURE__ */ new Date(),
940
1140
  description: requirements.description,
941
- url
1141
+ url,
1142
+ requestId,
1143
+ correlationId
942
1144
  };
943
1145
  this.payments.push(payment);
944
1146
  this.totalSpentUsd += amountUsd;
@@ -1037,6 +1239,36 @@ var AgentWallet = class {
1037
1239
  this.logger.debug("Using estimated balance based on tracking");
1038
1240
  return Math.max(0, 100 - this.totalSpentUsd);
1039
1241
  }
1242
+ /**
1243
+ * Check if the wallet can afford a specific amount.
1244
+ *
1245
+ * This is a convenience method to check balance before making a request
1246
+ * when you know the expected cost.
1247
+ *
1248
+ * @param amountUsd - Amount to check in USD
1249
+ * @returns Object with affordability information
1250
+ *
1251
+ * @example
1252
+ * ```typescript
1253
+ * const check = await wallet.canAfford(5.00);
1254
+ * if (check.canAfford) {
1255
+ * console.log(`Can afford! Will have $${check.remainingAfter.toFixed(2)} left`);
1256
+ * await wallet.fetch(url);
1257
+ * } else {
1258
+ * console.log(`Need $${check.shortfall.toFixed(2)} more`);
1259
+ * }
1260
+ * ```
1261
+ */
1262
+ async canAfford(amountUsd) {
1263
+ const balance = await this.getBalance();
1264
+ const canAfford = balance >= amountUsd;
1265
+ return {
1266
+ canAfford,
1267
+ balance,
1268
+ shortfall: canAfford ? 0 : amountUsd - balance,
1269
+ remainingAfter: canAfford ? balance - amountUsd : 0
1270
+ };
1271
+ }
1040
1272
  /**
1041
1273
  * Get information about the session key.
1042
1274
  *
@@ -1177,56 +1409,100 @@ var AgentWallet = class {
1177
1409
  async runDiagnostics() {
1178
1410
  this.logger.info("Running diagnostics...");
1179
1411
  const issues = [];
1412
+ const recommendations = [];
1180
1413
  const checks = {};
1414
+ let latencyMs;
1415
+ let sessionLimits;
1181
1416
  checks.sessionKeyFormat = true;
1182
1417
  try {
1418
+ const startTime = Date.now();
1183
1419
  const response = await fetch(`${this.baseUrl}/health`, {
1184
1420
  method: "GET",
1185
1421
  signal: AbortSignal.timeout(5e3)
1186
1422
  });
1423
+ latencyMs = Date.now() - startTime;
1187
1424
  checks.apiConnectivity = response.ok;
1188
1425
  if (!response.ok) {
1189
1426
  issues.push(`API server returned ${response.status}. Check baseUrl configuration.`);
1427
+ recommendations.push("Verify the baseUrl configuration points to a valid MixrPay server.");
1428
+ }
1429
+ if (latencyMs > 2e3) {
1430
+ issues.push(`High API latency: ${latencyMs}ms. This may cause timeouts.`);
1431
+ recommendations.push("Consider using a server closer to your region or check network connectivity.");
1190
1432
  }
1191
1433
  } catch {
1192
1434
  checks.apiConnectivity = false;
1193
1435
  issues.push("Cannot connect to MixrPay API. Check your network connection and baseUrl.");
1436
+ recommendations.push("Verify network connectivity and that the MixrPay server is running.");
1194
1437
  }
1195
1438
  try {
1196
1439
  const info = await this.getSessionKeyInfo(true);
1197
1440
  checks.sessionKeyValid = info.isValid;
1198
1441
  if (!info.isValid) {
1199
1442
  issues.push("Session key is invalid or has been revoked.");
1443
+ recommendations.push("Request a new session key from the wallet owner or create one at /wallet/sessions.");
1444
+ }
1445
+ const now = /* @__PURE__ */ new Date();
1446
+ let expiresInHours = null;
1447
+ if (info.expiresAt) {
1448
+ expiresInHours = (info.expiresAt.getTime() - now.getTime()) / (1e3 * 60 * 60);
1449
+ if (info.expiresAt < now) {
1450
+ checks.sessionKeyValid = false;
1451
+ issues.push(`Session key expired on ${info.expiresAt.toISOString()}`);
1452
+ recommendations.push("Create a new session key to continue making payments.");
1453
+ } else if (expiresInHours < 24) {
1454
+ issues.push(`Session key expires in ${expiresInHours.toFixed(1)} hours.`);
1455
+ recommendations.push("Consider creating a new session key before the current one expires.");
1456
+ }
1200
1457
  }
1201
- if (info.expiresAt && info.expiresAt < /* @__PURE__ */ new Date()) {
1202
- checks.sessionKeyValid = false;
1203
- issues.push(`Session key expired on ${info.expiresAt.toISOString()}`);
1458
+ const stats = await this.getSpendingStats();
1459
+ sessionLimits = {
1460
+ remainingDailyUsd: stats.remainingDailyUsd,
1461
+ remainingTotalUsd: stats.remainingTotalUsd,
1462
+ expiresAt: info.expiresAt,
1463
+ expiresInHours
1464
+ };
1465
+ if (stats.remainingDailyUsd !== null && stats.remainingDailyUsd < 1) {
1466
+ issues.push(`Daily limit nearly exhausted: $${stats.remainingDailyUsd.toFixed(2)} remaining.`);
1467
+ recommendations.push("Wait until tomorrow for daily limit to reset, or request a higher daily limit.");
1468
+ }
1469
+ if (stats.remainingTotalUsd !== null && stats.remainingTotalUsd < 1) {
1470
+ issues.push(`Total limit nearly exhausted: $${stats.remainingTotalUsd.toFixed(2)} remaining.`);
1471
+ recommendations.push("Request a new session key with a higher total limit.");
1204
1472
  }
1205
1473
  } catch {
1206
1474
  checks.sessionKeyValid = false;
1207
1475
  issues.push("Could not verify session key validity.");
1476
+ recommendations.push("Check network connectivity and try again.");
1208
1477
  }
1478
+ let balance = 0;
1209
1479
  try {
1210
- const balance = await this.getBalance();
1480
+ balance = await this.getBalance();
1211
1481
  checks.hasBalance = balance > 0;
1212
1482
  if (balance <= 0) {
1213
1483
  issues.push("Wallet has no USDC balance. Top up at your MixrPay server /wallet");
1484
+ recommendations.push("Deposit USDC to your wallet address to enable payments.");
1214
1485
  } else if (balance < 1) {
1215
1486
  issues.push(`Low balance: $${balance.toFixed(2)}. Consider topping up.`);
1487
+ recommendations.push("Top up your wallet balance to avoid payment failures.");
1216
1488
  }
1217
1489
  } catch {
1218
1490
  checks.hasBalance = false;
1219
1491
  issues.push("Could not fetch wallet balance.");
1492
+ recommendations.push("Check network connectivity and try again.");
1220
1493
  }
1221
1494
  const healthy = issues.length === 0;
1222
- this.logger.info("Diagnostics complete:", { healthy, issues });
1495
+ this.logger.info("Diagnostics complete:", { healthy, issues, latencyMs });
1223
1496
  return {
1224
1497
  healthy,
1225
1498
  issues,
1226
1499
  checks,
1227
1500
  sdkVersion: SDK_VERSION,
1228
1501
  network: this.getNetwork().name,
1229
- walletAddress: this.walletAddress
1502
+ walletAddress: this.walletAddress,
1503
+ sessionLimits,
1504
+ latencyMs,
1505
+ recommendations
1230
1506
  };
1231
1507
  }
1232
1508
  /**
@@ -1635,7 +1911,9 @@ var AgentWallet = class {
1635
1911
  txHash: response.headers.get("X-Payment-TxHash"),
1636
1912
  timestamp: /* @__PURE__ */ new Date(),
1637
1913
  description: feature || "API call",
1638
- url
1914
+ url,
1915
+ requestId: crypto.randomUUID(),
1916
+ correlationId: this.extractCorrelationId(customHeaders)
1639
1917
  };
1640
1918
  this.payments.push(payment);
1641
1919
  this.totalSpentUsd += amountUsd;
@@ -1834,7 +2112,8 @@ Timestamp: ${timestamp}`;
1834
2112
  txHash: mixrpay.txHash,
1835
2113
  timestamp: /* @__PURE__ */ new Date(),
1836
2114
  description: `MCP: ${toolName}`,
1837
- url: `${this.baseUrl}/api/mcp`
2115
+ url: `${this.baseUrl}/api/mcp`,
2116
+ requestId: crypto.randomUUID()
1838
2117
  };
1839
2118
  this.payments.push(payment);
1840
2119
  this.totalSpentUsd += mixrpay.chargedUsd;
@@ -1951,7 +2230,8 @@ Timestamp: ${timestamp}`;
1951
2230
  txHash: data.tx_hash,
1952
2231
  timestamp: /* @__PURE__ */ new Date(),
1953
2232
  description: `Agent run: ${data.run_id}`,
1954
- url: `${this.baseUrl}/api/v2/agent/run`
2233
+ url: `${this.baseUrl}/api/v2/agent/run`,
2234
+ requestId: idempotencyKey || crypto.randomUUID()
1955
2235
  };
1956
2236
  this.payments.push(payment);
1957
2237
  this.totalSpentUsd += data.cost.total_usd;
@@ -2043,7 +2323,8 @@ Timestamp: ${timestamp}`;
2043
2323
  txHash: data.tx_hash,
2044
2324
  timestamp: /* @__PURE__ */ new Date(),
2045
2325
  description: `Agent run: ${data.run_id}`,
2046
- url: `${this.baseUrl}/api/v2/agent/run`
2326
+ url: `${this.baseUrl}/api/v2/agent/run`,
2327
+ requestId: body.idempotency_key || crypto.randomUUID()
2047
2328
  };
2048
2329
  this.payments.push(payment);
2049
2330
  this.totalSpentUsd += data.total_cost_usd;
@@ -2219,7 +2500,8 @@ Timestamp: ${timestamp}`;
2219
2500
  txHash: mixrpay.txHash,
2220
2501
  timestamp: /* @__PURE__ */ new Date(),
2221
2502
  description: `MCP: ${toolName}`,
2222
- url: `${this.baseUrl}/api/mcp`
2503
+ url: `${this.baseUrl}/api/mcp`,
2504
+ requestId: crypto.randomUUID()
2223
2505
  };
2224
2506
  this.payments.push(payment);
2225
2507
  this.totalSpentUsd += mixrpay.chargedUsd;
@@ -2250,5 +2532,7 @@ Timestamp: ${timestamp}`;
2250
2532
  SessionNotFoundError,
2251
2533
  SessionRevokedError,
2252
2534
  SpendingLimitExceededError,
2535
+ X402ProtocolError,
2536
+ getErrorMessage,
2253
2537
  isMixrPayError
2254
2538
  });