@mixrpay/agent-sdk 0.5.0 → 0.6.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/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.0";
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 = {
@@ -857,6 +919,8 @@ var AgentWallet = class {
857
919
  */
858
920
  async fetch(url, init) {
859
921
  this.logger.debug(`Fetching ${init?.method || "GET"} ${url}`);
922
+ const requestId = crypto.randomUUID();
923
+ const correlationId = this.extractCorrelationId(init?.headers);
860
924
  const controller = new AbortController();
861
925
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
862
926
  try {
@@ -867,7 +931,7 @@ var AgentWallet = class {
867
931
  this.logger.debug(`Initial response: ${response.status}`);
868
932
  if (response.status === 402) {
869
933
  this.logger.info(`Payment required for ${url}`);
870
- response = await this.handlePaymentRequired(url, init, response);
934
+ response = await this.handlePaymentRequired(url, init, response, requestId, correlationId);
871
935
  }
872
936
  return response;
873
937
  } catch (error) {
@@ -879,10 +943,27 @@ var AgentWallet = class {
879
943
  clearTimeout(timeoutId);
880
944
  }
881
945
  }
946
+ /**
947
+ * Extract correlation ID from request headers.
948
+ */
949
+ extractCorrelationId(headers) {
950
+ if (!headers) return void 0;
951
+ if (headers instanceof Headers) {
952
+ return headers.get("X-Correlation-Id") || headers.get("x-correlation-id") || void 0;
953
+ }
954
+ if (Array.isArray(headers)) {
955
+ const entry = headers.find(
956
+ ([key]) => key.toLowerCase() === "x-correlation-id"
957
+ );
958
+ return entry ? entry[1] : void 0;
959
+ }
960
+ const record = headers;
961
+ return record["X-Correlation-Id"] || record["x-correlation-id"] || void 0;
962
+ }
882
963
  /**
883
964
  * Handle a 402 Payment Required response.
884
965
  */
885
- async handlePaymentRequired(url, init, response) {
966
+ async handlePaymentRequired(url, init, response, requestId, correlationId) {
886
967
  let requirements;
887
968
  try {
888
969
  requirements = await parse402Response(response);
@@ -938,7 +1019,9 @@ var AgentWallet = class {
938
1019
  txHash: retryResponse.headers.get("X-Payment-TxHash"),
939
1020
  timestamp: /* @__PURE__ */ new Date(),
940
1021
  description: requirements.description,
941
- url
1022
+ url,
1023
+ requestId,
1024
+ correlationId
942
1025
  };
943
1026
  this.payments.push(payment);
944
1027
  this.totalSpentUsd += amountUsd;
@@ -1037,6 +1120,36 @@ var AgentWallet = class {
1037
1120
  this.logger.debug("Using estimated balance based on tracking");
1038
1121
  return Math.max(0, 100 - this.totalSpentUsd);
1039
1122
  }
1123
+ /**
1124
+ * Check if the wallet can afford a specific amount.
1125
+ *
1126
+ * This is a convenience method to check balance before making a request
1127
+ * when you know the expected cost.
1128
+ *
1129
+ * @param amountUsd - Amount to check in USD
1130
+ * @returns Object with affordability information
1131
+ *
1132
+ * @example
1133
+ * ```typescript
1134
+ * const check = await wallet.canAfford(5.00);
1135
+ * if (check.canAfford) {
1136
+ * console.log(`Can afford! Will have $${check.remainingAfter.toFixed(2)} left`);
1137
+ * await wallet.fetch(url);
1138
+ * } else {
1139
+ * console.log(`Need $${check.shortfall.toFixed(2)} more`);
1140
+ * }
1141
+ * ```
1142
+ */
1143
+ async canAfford(amountUsd) {
1144
+ const balance = await this.getBalance();
1145
+ const canAfford = balance >= amountUsd;
1146
+ return {
1147
+ canAfford,
1148
+ balance,
1149
+ shortfall: canAfford ? 0 : amountUsd - balance,
1150
+ remainingAfter: canAfford ? balance - amountUsd : 0
1151
+ };
1152
+ }
1040
1153
  /**
1041
1154
  * Get information about the session key.
1042
1155
  *
@@ -1177,56 +1290,100 @@ var AgentWallet = class {
1177
1290
  async runDiagnostics() {
1178
1291
  this.logger.info("Running diagnostics...");
1179
1292
  const issues = [];
1293
+ const recommendations = [];
1180
1294
  const checks = {};
1295
+ let latencyMs;
1296
+ let sessionLimits;
1181
1297
  checks.sessionKeyFormat = true;
1182
1298
  try {
1299
+ const startTime = Date.now();
1183
1300
  const response = await fetch(`${this.baseUrl}/health`, {
1184
1301
  method: "GET",
1185
1302
  signal: AbortSignal.timeout(5e3)
1186
1303
  });
1304
+ latencyMs = Date.now() - startTime;
1187
1305
  checks.apiConnectivity = response.ok;
1188
1306
  if (!response.ok) {
1189
1307
  issues.push(`API server returned ${response.status}. Check baseUrl configuration.`);
1308
+ recommendations.push("Verify the baseUrl configuration points to a valid MixrPay server.");
1309
+ }
1310
+ if (latencyMs > 2e3) {
1311
+ issues.push(`High API latency: ${latencyMs}ms. This may cause timeouts.`);
1312
+ recommendations.push("Consider using a server closer to your region or check network connectivity.");
1190
1313
  }
1191
1314
  } catch {
1192
1315
  checks.apiConnectivity = false;
1193
1316
  issues.push("Cannot connect to MixrPay API. Check your network connection and baseUrl.");
1317
+ recommendations.push("Verify network connectivity and that the MixrPay server is running.");
1194
1318
  }
1195
1319
  try {
1196
1320
  const info = await this.getSessionKeyInfo(true);
1197
1321
  checks.sessionKeyValid = info.isValid;
1198
1322
  if (!info.isValid) {
1199
1323
  issues.push("Session key is invalid or has been revoked.");
1324
+ recommendations.push("Request a new session key from the wallet owner or create one at /wallet/sessions.");
1325
+ }
1326
+ const now = /* @__PURE__ */ new Date();
1327
+ let expiresInHours = null;
1328
+ if (info.expiresAt) {
1329
+ expiresInHours = (info.expiresAt.getTime() - now.getTime()) / (1e3 * 60 * 60);
1330
+ if (info.expiresAt < now) {
1331
+ checks.sessionKeyValid = false;
1332
+ issues.push(`Session key expired on ${info.expiresAt.toISOString()}`);
1333
+ recommendations.push("Create a new session key to continue making payments.");
1334
+ } else if (expiresInHours < 24) {
1335
+ issues.push(`Session key expires in ${expiresInHours.toFixed(1)} hours.`);
1336
+ recommendations.push("Consider creating a new session key before the current one expires.");
1337
+ }
1200
1338
  }
1201
- if (info.expiresAt && info.expiresAt < /* @__PURE__ */ new Date()) {
1202
- checks.sessionKeyValid = false;
1203
- issues.push(`Session key expired on ${info.expiresAt.toISOString()}`);
1339
+ const stats = await this.getSpendingStats();
1340
+ sessionLimits = {
1341
+ remainingDailyUsd: stats.remainingDailyUsd,
1342
+ remainingTotalUsd: stats.remainingTotalUsd,
1343
+ expiresAt: info.expiresAt,
1344
+ expiresInHours
1345
+ };
1346
+ if (stats.remainingDailyUsd !== null && stats.remainingDailyUsd < 1) {
1347
+ issues.push(`Daily limit nearly exhausted: $${stats.remainingDailyUsd.toFixed(2)} remaining.`);
1348
+ recommendations.push("Wait until tomorrow for daily limit to reset, or request a higher daily limit.");
1349
+ }
1350
+ if (stats.remainingTotalUsd !== null && stats.remainingTotalUsd < 1) {
1351
+ issues.push(`Total limit nearly exhausted: $${stats.remainingTotalUsd.toFixed(2)} remaining.`);
1352
+ recommendations.push("Request a new session key with a higher total limit.");
1204
1353
  }
1205
1354
  } catch {
1206
1355
  checks.sessionKeyValid = false;
1207
1356
  issues.push("Could not verify session key validity.");
1357
+ recommendations.push("Check network connectivity and try again.");
1208
1358
  }
1359
+ let balance = 0;
1209
1360
  try {
1210
- const balance = await this.getBalance();
1361
+ balance = await this.getBalance();
1211
1362
  checks.hasBalance = balance > 0;
1212
1363
  if (balance <= 0) {
1213
1364
  issues.push("Wallet has no USDC balance. Top up at your MixrPay server /wallet");
1365
+ recommendations.push("Deposit USDC to your wallet address to enable payments.");
1214
1366
  } else if (balance < 1) {
1215
1367
  issues.push(`Low balance: $${balance.toFixed(2)}. Consider topping up.`);
1368
+ recommendations.push("Top up your wallet balance to avoid payment failures.");
1216
1369
  }
1217
1370
  } catch {
1218
1371
  checks.hasBalance = false;
1219
1372
  issues.push("Could not fetch wallet balance.");
1373
+ recommendations.push("Check network connectivity and try again.");
1220
1374
  }
1221
1375
  const healthy = issues.length === 0;
1222
- this.logger.info("Diagnostics complete:", { healthy, issues });
1376
+ this.logger.info("Diagnostics complete:", { healthy, issues, latencyMs });
1223
1377
  return {
1224
1378
  healthy,
1225
1379
  issues,
1226
1380
  checks,
1227
1381
  sdkVersion: SDK_VERSION,
1228
1382
  network: this.getNetwork().name,
1229
- walletAddress: this.walletAddress
1383
+ walletAddress: this.walletAddress,
1384
+ sessionLimits,
1385
+ latencyMs,
1386
+ recommendations
1230
1387
  };
1231
1388
  }
1232
1389
  /**
@@ -1635,7 +1792,9 @@ var AgentWallet = class {
1635
1792
  txHash: response.headers.get("X-Payment-TxHash"),
1636
1793
  timestamp: /* @__PURE__ */ new Date(),
1637
1794
  description: feature || "API call",
1638
- url
1795
+ url,
1796
+ requestId: crypto.randomUUID(),
1797
+ correlationId: this.extractCorrelationId(customHeaders)
1639
1798
  };
1640
1799
  this.payments.push(payment);
1641
1800
  this.totalSpentUsd += amountUsd;
@@ -1834,7 +1993,8 @@ Timestamp: ${timestamp}`;
1834
1993
  txHash: mixrpay.txHash,
1835
1994
  timestamp: /* @__PURE__ */ new Date(),
1836
1995
  description: `MCP: ${toolName}`,
1837
- url: `${this.baseUrl}/api/mcp`
1996
+ url: `${this.baseUrl}/api/mcp`,
1997
+ requestId: crypto.randomUUID()
1838
1998
  };
1839
1999
  this.payments.push(payment);
1840
2000
  this.totalSpentUsd += mixrpay.chargedUsd;
@@ -1951,7 +2111,8 @@ Timestamp: ${timestamp}`;
1951
2111
  txHash: data.tx_hash,
1952
2112
  timestamp: /* @__PURE__ */ new Date(),
1953
2113
  description: `Agent run: ${data.run_id}`,
1954
- url: `${this.baseUrl}/api/v2/agent/run`
2114
+ url: `${this.baseUrl}/api/v2/agent/run`,
2115
+ requestId: idempotencyKey || crypto.randomUUID()
1955
2116
  };
1956
2117
  this.payments.push(payment);
1957
2118
  this.totalSpentUsd += data.cost.total_usd;
@@ -2043,7 +2204,8 @@ Timestamp: ${timestamp}`;
2043
2204
  txHash: data.tx_hash,
2044
2205
  timestamp: /* @__PURE__ */ new Date(),
2045
2206
  description: `Agent run: ${data.run_id}`,
2046
- url: `${this.baseUrl}/api/v2/agent/run`
2207
+ url: `${this.baseUrl}/api/v2/agent/run`,
2208
+ requestId: body.idempotency_key || crypto.randomUUID()
2047
2209
  };
2048
2210
  this.payments.push(payment);
2049
2211
  this.totalSpentUsd += data.total_cost_usd;
@@ -2219,7 +2381,8 @@ Timestamp: ${timestamp}`;
2219
2381
  txHash: mixrpay.txHash,
2220
2382
  timestamp: /* @__PURE__ */ new Date(),
2221
2383
  description: `MCP: ${toolName}`,
2222
- url: `${this.baseUrl}/api/mcp`
2384
+ url: `${this.baseUrl}/api/mcp`,
2385
+ requestId: crypto.randomUUID()
2223
2386
  };
2224
2387
  this.payments.push(payment);
2225
2388
  this.totalSpentUsd += mixrpay.chargedUsd;
@@ -2250,5 +2413,7 @@ Timestamp: ${timestamp}`;
2250
2413
  SessionNotFoundError,
2251
2414
  SessionRevokedError,
2252
2415
  SpendingLimitExceededError,
2416
+ X402ProtocolError,
2417
+ getErrorMessage,
2253
2418
  isMixrPayError
2254
2419
  });
package/dist/index.d.cts CHANGED
@@ -121,6 +121,10 @@ interface PaymentEvent {
121
121
  description?: string;
122
122
  /** URL that triggered the payment */
123
123
  url?: string;
124
+ /** Auto-generated unique ID for this request */
125
+ requestId: string;
126
+ /** User-provided correlation ID (from X-Correlation-Id header if provided) */
127
+ correlationId?: string;
124
128
  }
125
129
  /**
126
130
  * Information about a session key.
@@ -195,6 +199,21 @@ interface DiagnosticsResult {
195
199
  network: string;
196
200
  /** Wallet address */
197
201
  walletAddress: string;
202
+ /** Session key limit info (if available) */
203
+ sessionLimits?: {
204
+ /** Remaining daily limit in USD (null if no limit) */
205
+ remainingDailyUsd: number | null;
206
+ /** Remaining total limit in USD (null if no limit) */
207
+ remainingTotalUsd: number | null;
208
+ /** When the session key expires (null if never) */
209
+ expiresAt: Date | null;
210
+ /** Hours until expiration (null if never) */
211
+ expiresInHours: number | null;
212
+ };
213
+ /** Network latency to API in milliseconds */
214
+ latencyMs?: number;
215
+ /** Actionable recommendations based on issues found */
216
+ recommendations: string[];
198
217
  }
199
218
  /**
200
219
  * Options for creating a session authorization with a MixrPay merchant.
@@ -359,7 +378,7 @@ interface SessionStats {
359
378
  */
360
379
 
361
380
  /** Current SDK version */
362
- declare const SDK_VERSION = "0.5.0";
381
+ declare const SDK_VERSION = "0.6.0";
363
382
  /** Supported networks */
364
383
  declare const NETWORKS: {
365
384
  readonly BASE_MAINNET: {
@@ -672,6 +691,10 @@ declare class AgentWallet {
672
691
  * ```
673
692
  */
674
693
  fetch(url: string, init?: RequestInit): Promise<Response>;
694
+ /**
695
+ * Extract correlation ID from request headers.
696
+ */
697
+ private extractCorrelationId;
675
698
  /**
676
699
  * Handle a 402 Payment Required response.
677
700
  */
@@ -710,6 +733,36 @@ declare class AgentWallet {
710
733
  * ```
711
734
  */
712
735
  getBalance(): Promise<number>;
736
+ /**
737
+ * Check if the wallet can afford a specific amount.
738
+ *
739
+ * This is a convenience method to check balance before making a request
740
+ * when you know the expected cost.
741
+ *
742
+ * @param amountUsd - Amount to check in USD
743
+ * @returns Object with affordability information
744
+ *
745
+ * @example
746
+ * ```typescript
747
+ * const check = await wallet.canAfford(5.00);
748
+ * if (check.canAfford) {
749
+ * console.log(`Can afford! Will have $${check.remainingAfter.toFixed(2)} left`);
750
+ * await wallet.fetch(url);
751
+ * } else {
752
+ * console.log(`Need $${check.shortfall.toFixed(2)} more`);
753
+ * }
754
+ * ```
755
+ */
756
+ canAfford(amountUsd: number): Promise<{
757
+ /** Whether the wallet can afford this amount */
758
+ canAfford: boolean;
759
+ /** Current wallet balance in USD */
760
+ balance: number;
761
+ /** Amount short (0 if can afford) */
762
+ shortfall: number;
763
+ /** Remaining after payment (0 if can't afford) */
764
+ remainingAfter: number;
765
+ }>;
713
766
  /**
714
767
  * Get information about the session key.
715
768
  *
@@ -1326,7 +1379,33 @@ interface AgentRunStatusResult {
1326
1379
  declare class MixrPayError extends Error {
1327
1380
  /** Error code for programmatic handling */
1328
1381
  readonly code: string;
1329
- constructor(message: string, code?: string);
1382
+ /** Optional hint for how long to wait before retrying (in milliseconds) */
1383
+ readonly retryAfterMs?: number;
1384
+ constructor(message: string, code?: string, retryAfterMs?: number);
1385
+ /**
1386
+ * Check if this error is retryable.
1387
+ *
1388
+ * Returns true if the operation might succeed on retry (e.g., transient network issues).
1389
+ * Returns false if the error requires user action to resolve (e.g., insufficient balance).
1390
+ *
1391
+ * @returns true if the operation should be retried, false otherwise
1392
+ *
1393
+ * @example
1394
+ * ```typescript
1395
+ * try {
1396
+ * await wallet.fetch(...);
1397
+ * } catch (error) {
1398
+ * if (error instanceof MixrPayError && error.isRetryable()) {
1399
+ * // Retry the operation
1400
+ * await retry();
1401
+ * } else {
1402
+ * // Handle permanent failure
1403
+ * throw error;
1404
+ * }
1405
+ * }
1406
+ * ```
1407
+ */
1408
+ isRetryable(): boolean;
1330
1409
  }
1331
1410
  /**
1332
1411
  * Thrown when the wallet doesn't have enough USDC for the payment.
@@ -1418,6 +1497,12 @@ declare class SpendingLimitExceededError extends MixrPayError {
1418
1497
  /** The amount that was attempted in USD */
1419
1498
  readonly attempted: number;
1420
1499
  constructor(limitType: string, limit: number, attempted: number);
1500
+ /**
1501
+ * Daily limits reset at midnight, so those are retryable (after waiting).
1502
+ * Other limit types require user action (new session key, config change).
1503
+ * @returns true only for daily limits
1504
+ */
1505
+ isRetryable(): boolean;
1421
1506
  }
1422
1507
  /**
1423
1508
  * Thrown when a payment transaction fails.
@@ -1449,6 +1534,11 @@ declare class PaymentFailedError extends MixrPayError {
1449
1534
  /** Transaction hash if the tx was submitted (for debugging) */
1450
1535
  readonly txHash?: string;
1451
1536
  constructor(reason: string, txHash?: string);
1537
+ /**
1538
+ * Payment failures are often transient (network issues, temporary server errors).
1539
+ * @returns true - payment failures should generally be retried
1540
+ */
1541
+ isRetryable(): boolean;
1452
1542
  }
1453
1543
  /**
1454
1544
  * Thrown when the session key format is invalid.
@@ -1479,6 +1569,38 @@ declare class InvalidSessionKeyError extends MixrPayError {
1479
1569
  readonly reason: string;
1480
1570
  constructor(reason?: string);
1481
1571
  }
1572
+ /**
1573
+ * Thrown when there's an error in x402 protocol handling.
1574
+ *
1575
+ * This indicates a problem with the payment protocol, usually due to:
1576
+ * - Malformed 402 response from server
1577
+ * - Missing required payment fields
1578
+ * - Invalid payment parameters
1579
+ * - Protocol version mismatch
1580
+ *
1581
+ * ## Resolution
1582
+ * This is usually a server-side issue. Contact the API provider if the error persists.
1583
+ *
1584
+ * @example
1585
+ * ```typescript
1586
+ * catch (error) {
1587
+ * if (error instanceof X402ProtocolError) {
1588
+ * console.log(`Protocol error: ${error.reason}`);
1589
+ * // Contact API provider or check server configuration
1590
+ * }
1591
+ * }
1592
+ * ```
1593
+ */
1594
+ declare class X402ProtocolError extends MixrPayError {
1595
+ /** Detailed reason for the protocol error */
1596
+ readonly reason: string;
1597
+ constructor(reason: string);
1598
+ /**
1599
+ * Protocol errors may be caused by temporary server issues.
1600
+ * @returns true - worth retrying in case server recovers
1601
+ */
1602
+ isRetryable(): boolean;
1603
+ }
1482
1604
  /**
1483
1605
  * Thrown when a session authorization has expired.
1484
1606
  *
@@ -1611,5 +1733,20 @@ declare class SessionRevokedError extends MixrPayError {
1611
1733
  * ```
1612
1734
  */
1613
1735
  declare function isMixrPayError(error: unknown): error is MixrPayError;
1736
+ /**
1737
+ * Get a user-friendly error message from any error.
1738
+ *
1739
+ * @param error - The error to get a message from
1740
+ * @returns A user-friendly error message
1741
+ *
1742
+ * @example
1743
+ * ```typescript
1744
+ * catch (error) {
1745
+ * const message = getErrorMessage(error);
1746
+ * showToast(message);
1747
+ * }
1748
+ * ```
1749
+ */
1750
+ declare function getErrorMessage(error: unknown): string;
1614
1751
 
1615
- export { type AgentMessage, type AgentRunConfig, type AgentRunEvent, type AgentRunOptions, type AgentRunResult, type AgentRunStatusResult, AgentWallet, type AgentWalletConfig, type CallMerchantApiOptions, type ChargeResult, InsufficientBalanceError, InvalidSessionKeyError, MixrPayError, type PaymentEvent, PaymentFailedError, SDK_VERSION, type SessionAuthorization, SessionExpiredError, SessionKeyExpiredError, SessionLimitExceededError, SessionNotFoundError, SessionRevokedError, type SessionStats, SpendingLimitExceededError, type SpendingStats, isMixrPayError };
1752
+ export { type AgentMessage, type AgentRunConfig, type AgentRunEvent, type AgentRunOptions, type AgentRunResult, type AgentRunStatusResult, AgentWallet, type AgentWalletConfig, type CallMerchantApiOptions, type ChargeResult, type DiagnosticsResult, InsufficientBalanceError, InvalidSessionKeyError, MixrPayError, type PaymentEvent, PaymentFailedError, SDK_VERSION, type SessionAuthorization, SessionExpiredError, SessionKeyExpiredError, type SessionKeyInfo, SessionLimitExceededError, SessionNotFoundError, SessionRevokedError, type SessionStats, SpendingLimitExceededError, type SpendingStats, X402ProtocolError, getErrorMessage, isMixrPayError };
package/dist/index.d.ts CHANGED
@@ -121,6 +121,10 @@ interface PaymentEvent {
121
121
  description?: string;
122
122
  /** URL that triggered the payment */
123
123
  url?: string;
124
+ /** Auto-generated unique ID for this request */
125
+ requestId: string;
126
+ /** User-provided correlation ID (from X-Correlation-Id header if provided) */
127
+ correlationId?: string;
124
128
  }
125
129
  /**
126
130
  * Information about a session key.
@@ -195,6 +199,21 @@ interface DiagnosticsResult {
195
199
  network: string;
196
200
  /** Wallet address */
197
201
  walletAddress: string;
202
+ /** Session key limit info (if available) */
203
+ sessionLimits?: {
204
+ /** Remaining daily limit in USD (null if no limit) */
205
+ remainingDailyUsd: number | null;
206
+ /** Remaining total limit in USD (null if no limit) */
207
+ remainingTotalUsd: number | null;
208
+ /** When the session key expires (null if never) */
209
+ expiresAt: Date | null;
210
+ /** Hours until expiration (null if never) */
211
+ expiresInHours: number | null;
212
+ };
213
+ /** Network latency to API in milliseconds */
214
+ latencyMs?: number;
215
+ /** Actionable recommendations based on issues found */
216
+ recommendations: string[];
198
217
  }
199
218
  /**
200
219
  * Options for creating a session authorization with a MixrPay merchant.
@@ -359,7 +378,7 @@ interface SessionStats {
359
378
  */
360
379
 
361
380
  /** Current SDK version */
362
- declare const SDK_VERSION = "0.5.0";
381
+ declare const SDK_VERSION = "0.6.0";
363
382
  /** Supported networks */
364
383
  declare const NETWORKS: {
365
384
  readonly BASE_MAINNET: {
@@ -672,6 +691,10 @@ declare class AgentWallet {
672
691
  * ```
673
692
  */
674
693
  fetch(url: string, init?: RequestInit): Promise<Response>;
694
+ /**
695
+ * Extract correlation ID from request headers.
696
+ */
697
+ private extractCorrelationId;
675
698
  /**
676
699
  * Handle a 402 Payment Required response.
677
700
  */
@@ -710,6 +733,36 @@ declare class AgentWallet {
710
733
  * ```
711
734
  */
712
735
  getBalance(): Promise<number>;
736
+ /**
737
+ * Check if the wallet can afford a specific amount.
738
+ *
739
+ * This is a convenience method to check balance before making a request
740
+ * when you know the expected cost.
741
+ *
742
+ * @param amountUsd - Amount to check in USD
743
+ * @returns Object with affordability information
744
+ *
745
+ * @example
746
+ * ```typescript
747
+ * const check = await wallet.canAfford(5.00);
748
+ * if (check.canAfford) {
749
+ * console.log(`Can afford! Will have $${check.remainingAfter.toFixed(2)} left`);
750
+ * await wallet.fetch(url);
751
+ * } else {
752
+ * console.log(`Need $${check.shortfall.toFixed(2)} more`);
753
+ * }
754
+ * ```
755
+ */
756
+ canAfford(amountUsd: number): Promise<{
757
+ /** Whether the wallet can afford this amount */
758
+ canAfford: boolean;
759
+ /** Current wallet balance in USD */
760
+ balance: number;
761
+ /** Amount short (0 if can afford) */
762
+ shortfall: number;
763
+ /** Remaining after payment (0 if can't afford) */
764
+ remainingAfter: number;
765
+ }>;
713
766
  /**
714
767
  * Get information about the session key.
715
768
  *
@@ -1326,7 +1379,33 @@ interface AgentRunStatusResult {
1326
1379
  declare class MixrPayError extends Error {
1327
1380
  /** Error code for programmatic handling */
1328
1381
  readonly code: string;
1329
- constructor(message: string, code?: string);
1382
+ /** Optional hint for how long to wait before retrying (in milliseconds) */
1383
+ readonly retryAfterMs?: number;
1384
+ constructor(message: string, code?: string, retryAfterMs?: number);
1385
+ /**
1386
+ * Check if this error is retryable.
1387
+ *
1388
+ * Returns true if the operation might succeed on retry (e.g., transient network issues).
1389
+ * Returns false if the error requires user action to resolve (e.g., insufficient balance).
1390
+ *
1391
+ * @returns true if the operation should be retried, false otherwise
1392
+ *
1393
+ * @example
1394
+ * ```typescript
1395
+ * try {
1396
+ * await wallet.fetch(...);
1397
+ * } catch (error) {
1398
+ * if (error instanceof MixrPayError && error.isRetryable()) {
1399
+ * // Retry the operation
1400
+ * await retry();
1401
+ * } else {
1402
+ * // Handle permanent failure
1403
+ * throw error;
1404
+ * }
1405
+ * }
1406
+ * ```
1407
+ */
1408
+ isRetryable(): boolean;
1330
1409
  }
1331
1410
  /**
1332
1411
  * Thrown when the wallet doesn't have enough USDC for the payment.
@@ -1418,6 +1497,12 @@ declare class SpendingLimitExceededError extends MixrPayError {
1418
1497
  /** The amount that was attempted in USD */
1419
1498
  readonly attempted: number;
1420
1499
  constructor(limitType: string, limit: number, attempted: number);
1500
+ /**
1501
+ * Daily limits reset at midnight, so those are retryable (after waiting).
1502
+ * Other limit types require user action (new session key, config change).
1503
+ * @returns true only for daily limits
1504
+ */
1505
+ isRetryable(): boolean;
1421
1506
  }
1422
1507
  /**
1423
1508
  * Thrown when a payment transaction fails.
@@ -1449,6 +1534,11 @@ declare class PaymentFailedError extends MixrPayError {
1449
1534
  /** Transaction hash if the tx was submitted (for debugging) */
1450
1535
  readonly txHash?: string;
1451
1536
  constructor(reason: string, txHash?: string);
1537
+ /**
1538
+ * Payment failures are often transient (network issues, temporary server errors).
1539
+ * @returns true - payment failures should generally be retried
1540
+ */
1541
+ isRetryable(): boolean;
1452
1542
  }
1453
1543
  /**
1454
1544
  * Thrown when the session key format is invalid.
@@ -1479,6 +1569,38 @@ declare class InvalidSessionKeyError extends MixrPayError {
1479
1569
  readonly reason: string;
1480
1570
  constructor(reason?: string);
1481
1571
  }
1572
+ /**
1573
+ * Thrown when there's an error in x402 protocol handling.
1574
+ *
1575
+ * This indicates a problem with the payment protocol, usually due to:
1576
+ * - Malformed 402 response from server
1577
+ * - Missing required payment fields
1578
+ * - Invalid payment parameters
1579
+ * - Protocol version mismatch
1580
+ *
1581
+ * ## Resolution
1582
+ * This is usually a server-side issue. Contact the API provider if the error persists.
1583
+ *
1584
+ * @example
1585
+ * ```typescript
1586
+ * catch (error) {
1587
+ * if (error instanceof X402ProtocolError) {
1588
+ * console.log(`Protocol error: ${error.reason}`);
1589
+ * // Contact API provider or check server configuration
1590
+ * }
1591
+ * }
1592
+ * ```
1593
+ */
1594
+ declare class X402ProtocolError extends MixrPayError {
1595
+ /** Detailed reason for the protocol error */
1596
+ readonly reason: string;
1597
+ constructor(reason: string);
1598
+ /**
1599
+ * Protocol errors may be caused by temporary server issues.
1600
+ * @returns true - worth retrying in case server recovers
1601
+ */
1602
+ isRetryable(): boolean;
1603
+ }
1482
1604
  /**
1483
1605
  * Thrown when a session authorization has expired.
1484
1606
  *
@@ -1611,5 +1733,20 @@ declare class SessionRevokedError extends MixrPayError {
1611
1733
  * ```
1612
1734
  */
1613
1735
  declare function isMixrPayError(error: unknown): error is MixrPayError;
1736
+ /**
1737
+ * Get a user-friendly error message from any error.
1738
+ *
1739
+ * @param error - The error to get a message from
1740
+ * @returns A user-friendly error message
1741
+ *
1742
+ * @example
1743
+ * ```typescript
1744
+ * catch (error) {
1745
+ * const message = getErrorMessage(error);
1746
+ * showToast(message);
1747
+ * }
1748
+ * ```
1749
+ */
1750
+ declare function getErrorMessage(error: unknown): string;
1614
1751
 
1615
- export { type AgentMessage, type AgentRunConfig, type AgentRunEvent, type AgentRunOptions, type AgentRunResult, type AgentRunStatusResult, AgentWallet, type AgentWalletConfig, type CallMerchantApiOptions, type ChargeResult, InsufficientBalanceError, InvalidSessionKeyError, MixrPayError, type PaymentEvent, PaymentFailedError, SDK_VERSION, type SessionAuthorization, SessionExpiredError, SessionKeyExpiredError, SessionLimitExceededError, SessionNotFoundError, SessionRevokedError, type SessionStats, SpendingLimitExceededError, type SpendingStats, isMixrPayError };
1752
+ export { type AgentMessage, type AgentRunConfig, type AgentRunEvent, type AgentRunOptions, type AgentRunResult, type AgentRunStatusResult, AgentWallet, type AgentWalletConfig, type CallMerchantApiOptions, type ChargeResult, type DiagnosticsResult, InsufficientBalanceError, InvalidSessionKeyError, MixrPayError, type PaymentEvent, PaymentFailedError, SDK_VERSION, type SessionAuthorization, SessionExpiredError, SessionKeyExpiredError, type SessionKeyInfo, SessionLimitExceededError, SessionNotFoundError, SessionRevokedError, type SessionStats, SpendingLimitExceededError, type SpendingStats, X402ProtocolError, getErrorMessage, isMixrPayError };
package/dist/index.js CHANGED
@@ -9,14 +9,43 @@ import {
9
9
  var MixrPayError = class extends Error {
10
10
  /** Error code for programmatic handling */
11
11
  code;
12
- constructor(message, code = "MIXRPAY_ERROR") {
12
+ /** Optional hint for how long to wait before retrying (in milliseconds) */
13
+ retryAfterMs;
14
+ constructor(message, code = "MIXRPAY_ERROR", retryAfterMs) {
13
15
  super(message);
14
16
  this.name = "MixrPayError";
15
17
  this.code = code;
18
+ this.retryAfterMs = retryAfterMs;
16
19
  if (Error.captureStackTrace) {
17
20
  Error.captureStackTrace(this, this.constructor);
18
21
  }
19
22
  }
23
+ /**
24
+ * Check if this error is retryable.
25
+ *
26
+ * Returns true if the operation might succeed on retry (e.g., transient network issues).
27
+ * Returns false if the error requires user action to resolve (e.g., insufficient balance).
28
+ *
29
+ * @returns true if the operation should be retried, false otherwise
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * try {
34
+ * await wallet.fetch(...);
35
+ * } catch (error) {
36
+ * if (error instanceof MixrPayError && error.isRetryable()) {
37
+ * // Retry the operation
38
+ * await retry();
39
+ * } else {
40
+ * // Handle permanent failure
41
+ * throw error;
42
+ * }
43
+ * }
44
+ * ```
45
+ */
46
+ isRetryable() {
47
+ return false;
48
+ }
20
49
  };
21
50
  var InsufficientBalanceError = class extends MixrPayError {
22
51
  /** Amount required for the payment in USD */
@@ -74,6 +103,14 @@ var SpendingLimitExceededError = class extends MixrPayError {
74
103
  this.limit = limit;
75
104
  this.attempted = attempted;
76
105
  }
106
+ /**
107
+ * Daily limits reset at midnight, so those are retryable (after waiting).
108
+ * Other limit types require user action (new session key, config change).
109
+ * @returns true only for daily limits
110
+ */
111
+ isRetryable() {
112
+ return this.limitType === "daily";
113
+ }
77
114
  };
78
115
  var PaymentFailedError = class extends MixrPayError {
79
116
  /** Detailed reason for the failure */
@@ -90,6 +127,13 @@ var PaymentFailedError = class extends MixrPayError {
90
127
  this.reason = reason;
91
128
  this.txHash = txHash;
92
129
  }
130
+ /**
131
+ * Payment failures are often transient (network issues, temporary server errors).
132
+ * @returns true - payment failures should generally be retried
133
+ */
134
+ isRetryable() {
135
+ return true;
136
+ }
93
137
  };
94
138
  var InvalidSessionKeyError = class extends MixrPayError {
95
139
  /** Detailed reason why the key is invalid */
@@ -114,6 +158,13 @@ var X402ProtocolError = class extends MixrPayError {
114
158
  this.name = "X402ProtocolError";
115
159
  this.reason = reason;
116
160
  }
161
+ /**
162
+ * Protocol errors may be caused by temporary server issues.
163
+ * @returns true - worth retrying in case server recovers
164
+ */
165
+ isRetryable() {
166
+ return true;
167
+ }
117
168
  };
118
169
  var SessionExpiredError = class extends MixrPayError {
119
170
  /** ID of the expired session */
@@ -181,6 +232,15 @@ var SessionRevokedError = class extends MixrPayError {
181
232
  function isMixrPayError(error) {
182
233
  return error instanceof MixrPayError;
183
234
  }
235
+ function getErrorMessage(error) {
236
+ if (error instanceof MixrPayError) {
237
+ return error.message;
238
+ }
239
+ if (error instanceof Error) {
240
+ return error.message;
241
+ }
242
+ return String(error);
243
+ }
184
244
 
185
245
  // src/session-key.ts
186
246
  var USDC_ADDRESSES = {
@@ -411,7 +471,7 @@ function getAmountUsd(requirements) {
411
471
  }
412
472
 
413
473
  // src/agent-wallet.ts
414
- var SDK_VERSION = "0.5.0";
474
+ var SDK_VERSION = "0.6.0";
415
475
  var DEFAULT_BASE_URL = process.env.MIXRPAY_BASE_URL || "https://www.mixrpay.com";
416
476
  var DEFAULT_TIMEOUT = 3e4;
417
477
  var NETWORKS = {
@@ -823,6 +883,8 @@ var AgentWallet = class {
823
883
  */
824
884
  async fetch(url, init) {
825
885
  this.logger.debug(`Fetching ${init?.method || "GET"} ${url}`);
886
+ const requestId = crypto.randomUUID();
887
+ const correlationId = this.extractCorrelationId(init?.headers);
826
888
  const controller = new AbortController();
827
889
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
828
890
  try {
@@ -833,7 +895,7 @@ var AgentWallet = class {
833
895
  this.logger.debug(`Initial response: ${response.status}`);
834
896
  if (response.status === 402) {
835
897
  this.logger.info(`Payment required for ${url}`);
836
- response = await this.handlePaymentRequired(url, init, response);
898
+ response = await this.handlePaymentRequired(url, init, response, requestId, correlationId);
837
899
  }
838
900
  return response;
839
901
  } catch (error) {
@@ -845,10 +907,27 @@ var AgentWallet = class {
845
907
  clearTimeout(timeoutId);
846
908
  }
847
909
  }
910
+ /**
911
+ * Extract correlation ID from request headers.
912
+ */
913
+ extractCorrelationId(headers) {
914
+ if (!headers) return void 0;
915
+ if (headers instanceof Headers) {
916
+ return headers.get("X-Correlation-Id") || headers.get("x-correlation-id") || void 0;
917
+ }
918
+ if (Array.isArray(headers)) {
919
+ const entry = headers.find(
920
+ ([key]) => key.toLowerCase() === "x-correlation-id"
921
+ );
922
+ return entry ? entry[1] : void 0;
923
+ }
924
+ const record = headers;
925
+ return record["X-Correlation-Id"] || record["x-correlation-id"] || void 0;
926
+ }
848
927
  /**
849
928
  * Handle a 402 Payment Required response.
850
929
  */
851
- async handlePaymentRequired(url, init, response) {
930
+ async handlePaymentRequired(url, init, response, requestId, correlationId) {
852
931
  let requirements;
853
932
  try {
854
933
  requirements = await parse402Response(response);
@@ -904,7 +983,9 @@ var AgentWallet = class {
904
983
  txHash: retryResponse.headers.get("X-Payment-TxHash"),
905
984
  timestamp: /* @__PURE__ */ new Date(),
906
985
  description: requirements.description,
907
- url
986
+ url,
987
+ requestId,
988
+ correlationId
908
989
  };
909
990
  this.payments.push(payment);
910
991
  this.totalSpentUsd += amountUsd;
@@ -1003,6 +1084,36 @@ var AgentWallet = class {
1003
1084
  this.logger.debug("Using estimated balance based on tracking");
1004
1085
  return Math.max(0, 100 - this.totalSpentUsd);
1005
1086
  }
1087
+ /**
1088
+ * Check if the wallet can afford a specific amount.
1089
+ *
1090
+ * This is a convenience method to check balance before making a request
1091
+ * when you know the expected cost.
1092
+ *
1093
+ * @param amountUsd - Amount to check in USD
1094
+ * @returns Object with affordability information
1095
+ *
1096
+ * @example
1097
+ * ```typescript
1098
+ * const check = await wallet.canAfford(5.00);
1099
+ * if (check.canAfford) {
1100
+ * console.log(`Can afford! Will have $${check.remainingAfter.toFixed(2)} left`);
1101
+ * await wallet.fetch(url);
1102
+ * } else {
1103
+ * console.log(`Need $${check.shortfall.toFixed(2)} more`);
1104
+ * }
1105
+ * ```
1106
+ */
1107
+ async canAfford(amountUsd) {
1108
+ const balance = await this.getBalance();
1109
+ const canAfford = balance >= amountUsd;
1110
+ return {
1111
+ canAfford,
1112
+ balance,
1113
+ shortfall: canAfford ? 0 : amountUsd - balance,
1114
+ remainingAfter: canAfford ? balance - amountUsd : 0
1115
+ };
1116
+ }
1006
1117
  /**
1007
1118
  * Get information about the session key.
1008
1119
  *
@@ -1143,56 +1254,100 @@ var AgentWallet = class {
1143
1254
  async runDiagnostics() {
1144
1255
  this.logger.info("Running diagnostics...");
1145
1256
  const issues = [];
1257
+ const recommendations = [];
1146
1258
  const checks = {};
1259
+ let latencyMs;
1260
+ let sessionLimits;
1147
1261
  checks.sessionKeyFormat = true;
1148
1262
  try {
1263
+ const startTime = Date.now();
1149
1264
  const response = await fetch(`${this.baseUrl}/health`, {
1150
1265
  method: "GET",
1151
1266
  signal: AbortSignal.timeout(5e3)
1152
1267
  });
1268
+ latencyMs = Date.now() - startTime;
1153
1269
  checks.apiConnectivity = response.ok;
1154
1270
  if (!response.ok) {
1155
1271
  issues.push(`API server returned ${response.status}. Check baseUrl configuration.`);
1272
+ recommendations.push("Verify the baseUrl configuration points to a valid MixrPay server.");
1273
+ }
1274
+ if (latencyMs > 2e3) {
1275
+ issues.push(`High API latency: ${latencyMs}ms. This may cause timeouts.`);
1276
+ recommendations.push("Consider using a server closer to your region or check network connectivity.");
1156
1277
  }
1157
1278
  } catch {
1158
1279
  checks.apiConnectivity = false;
1159
1280
  issues.push("Cannot connect to MixrPay API. Check your network connection and baseUrl.");
1281
+ recommendations.push("Verify network connectivity and that the MixrPay server is running.");
1160
1282
  }
1161
1283
  try {
1162
1284
  const info = await this.getSessionKeyInfo(true);
1163
1285
  checks.sessionKeyValid = info.isValid;
1164
1286
  if (!info.isValid) {
1165
1287
  issues.push("Session key is invalid or has been revoked.");
1288
+ recommendations.push("Request a new session key from the wallet owner or create one at /wallet/sessions.");
1289
+ }
1290
+ const now = /* @__PURE__ */ new Date();
1291
+ let expiresInHours = null;
1292
+ if (info.expiresAt) {
1293
+ expiresInHours = (info.expiresAt.getTime() - now.getTime()) / (1e3 * 60 * 60);
1294
+ if (info.expiresAt < now) {
1295
+ checks.sessionKeyValid = false;
1296
+ issues.push(`Session key expired on ${info.expiresAt.toISOString()}`);
1297
+ recommendations.push("Create a new session key to continue making payments.");
1298
+ } else if (expiresInHours < 24) {
1299
+ issues.push(`Session key expires in ${expiresInHours.toFixed(1)} hours.`);
1300
+ recommendations.push("Consider creating a new session key before the current one expires.");
1301
+ }
1166
1302
  }
1167
- if (info.expiresAt && info.expiresAt < /* @__PURE__ */ new Date()) {
1168
- checks.sessionKeyValid = false;
1169
- issues.push(`Session key expired on ${info.expiresAt.toISOString()}`);
1303
+ const stats = await this.getSpendingStats();
1304
+ sessionLimits = {
1305
+ remainingDailyUsd: stats.remainingDailyUsd,
1306
+ remainingTotalUsd: stats.remainingTotalUsd,
1307
+ expiresAt: info.expiresAt,
1308
+ expiresInHours
1309
+ };
1310
+ if (stats.remainingDailyUsd !== null && stats.remainingDailyUsd < 1) {
1311
+ issues.push(`Daily limit nearly exhausted: $${stats.remainingDailyUsd.toFixed(2)} remaining.`);
1312
+ recommendations.push("Wait until tomorrow for daily limit to reset, or request a higher daily limit.");
1313
+ }
1314
+ if (stats.remainingTotalUsd !== null && stats.remainingTotalUsd < 1) {
1315
+ issues.push(`Total limit nearly exhausted: $${stats.remainingTotalUsd.toFixed(2)} remaining.`);
1316
+ recommendations.push("Request a new session key with a higher total limit.");
1170
1317
  }
1171
1318
  } catch {
1172
1319
  checks.sessionKeyValid = false;
1173
1320
  issues.push("Could not verify session key validity.");
1321
+ recommendations.push("Check network connectivity and try again.");
1174
1322
  }
1323
+ let balance = 0;
1175
1324
  try {
1176
- const balance = await this.getBalance();
1325
+ balance = await this.getBalance();
1177
1326
  checks.hasBalance = balance > 0;
1178
1327
  if (balance <= 0) {
1179
1328
  issues.push("Wallet has no USDC balance. Top up at your MixrPay server /wallet");
1329
+ recommendations.push("Deposit USDC to your wallet address to enable payments.");
1180
1330
  } else if (balance < 1) {
1181
1331
  issues.push(`Low balance: $${balance.toFixed(2)}. Consider topping up.`);
1332
+ recommendations.push("Top up your wallet balance to avoid payment failures.");
1182
1333
  }
1183
1334
  } catch {
1184
1335
  checks.hasBalance = false;
1185
1336
  issues.push("Could not fetch wallet balance.");
1337
+ recommendations.push("Check network connectivity and try again.");
1186
1338
  }
1187
1339
  const healthy = issues.length === 0;
1188
- this.logger.info("Diagnostics complete:", { healthy, issues });
1340
+ this.logger.info("Diagnostics complete:", { healthy, issues, latencyMs });
1189
1341
  return {
1190
1342
  healthy,
1191
1343
  issues,
1192
1344
  checks,
1193
1345
  sdkVersion: SDK_VERSION,
1194
1346
  network: this.getNetwork().name,
1195
- walletAddress: this.walletAddress
1347
+ walletAddress: this.walletAddress,
1348
+ sessionLimits,
1349
+ latencyMs,
1350
+ recommendations
1196
1351
  };
1197
1352
  }
1198
1353
  /**
@@ -1601,7 +1756,9 @@ var AgentWallet = class {
1601
1756
  txHash: response.headers.get("X-Payment-TxHash"),
1602
1757
  timestamp: /* @__PURE__ */ new Date(),
1603
1758
  description: feature || "API call",
1604
- url
1759
+ url,
1760
+ requestId: crypto.randomUUID(),
1761
+ correlationId: this.extractCorrelationId(customHeaders)
1605
1762
  };
1606
1763
  this.payments.push(payment);
1607
1764
  this.totalSpentUsd += amountUsd;
@@ -1800,7 +1957,8 @@ Timestamp: ${timestamp}`;
1800
1957
  txHash: mixrpay.txHash,
1801
1958
  timestamp: /* @__PURE__ */ new Date(),
1802
1959
  description: `MCP: ${toolName}`,
1803
- url: `${this.baseUrl}/api/mcp`
1960
+ url: `${this.baseUrl}/api/mcp`,
1961
+ requestId: crypto.randomUUID()
1804
1962
  };
1805
1963
  this.payments.push(payment);
1806
1964
  this.totalSpentUsd += mixrpay.chargedUsd;
@@ -1917,7 +2075,8 @@ Timestamp: ${timestamp}`;
1917
2075
  txHash: data.tx_hash,
1918
2076
  timestamp: /* @__PURE__ */ new Date(),
1919
2077
  description: `Agent run: ${data.run_id}`,
1920
- url: `${this.baseUrl}/api/v2/agent/run`
2078
+ url: `${this.baseUrl}/api/v2/agent/run`,
2079
+ requestId: idempotencyKey || crypto.randomUUID()
1921
2080
  };
1922
2081
  this.payments.push(payment);
1923
2082
  this.totalSpentUsd += data.cost.total_usd;
@@ -2009,7 +2168,8 @@ Timestamp: ${timestamp}`;
2009
2168
  txHash: data.tx_hash,
2010
2169
  timestamp: /* @__PURE__ */ new Date(),
2011
2170
  description: `Agent run: ${data.run_id}`,
2012
- url: `${this.baseUrl}/api/v2/agent/run`
2171
+ url: `${this.baseUrl}/api/v2/agent/run`,
2172
+ requestId: body.idempotency_key || crypto.randomUUID()
2013
2173
  };
2014
2174
  this.payments.push(payment);
2015
2175
  this.totalSpentUsd += data.total_cost_usd;
@@ -2185,7 +2345,8 @@ Timestamp: ${timestamp}`;
2185
2345
  txHash: mixrpay.txHash,
2186
2346
  timestamp: /* @__PURE__ */ new Date(),
2187
2347
  description: `MCP: ${toolName}`,
2188
- url: `${this.baseUrl}/api/mcp`
2348
+ url: `${this.baseUrl}/api/mcp`,
2349
+ requestId: crypto.randomUUID()
2189
2350
  };
2190
2351
  this.payments.push(payment);
2191
2352
  this.totalSpentUsd += mixrpay.chargedUsd;
@@ -2215,5 +2376,7 @@ export {
2215
2376
  SessionNotFoundError,
2216
2377
  SessionRevokedError,
2217
2378
  SpendingLimitExceededError,
2379
+ X402ProtocolError,
2380
+ getErrorMessage,
2218
2381
  isMixrPayError
2219
2382
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mixrpay/agent-sdk",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "MixrPay Agent SDK - Enable AI agents to make x402 payments with session keys",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",