@ar-agents/mercadopago 0.10.0 → 0.12.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
@@ -228,6 +228,23 @@ var DEFAULT_BASE_URL = "https://api.mercadopago.com";
228
228
  function sleep(ms) {
229
229
  return new Promise((resolve) => setTimeout(resolve, ms));
230
230
  }
231
+ function randomUuid() {
232
+ const c = globalThis.crypto;
233
+ if (c?.randomUUID) {
234
+ return c.randomUUID();
235
+ }
236
+ if (c?.getRandomValues) {
237
+ const bytes = new Uint8Array(16);
238
+ c.getRandomValues(bytes);
239
+ bytes[6] = bytes[6] & 15 | 64;
240
+ bytes[8] = bytes[8] & 63 | 128;
241
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
242
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
243
+ }
244
+ throw new Error(
245
+ "MercadoPagoClient: no Web Crypto available for idempotency key generation. This shouldn't happen on Node 19+, Edge Runtime, or modern browsers."
246
+ );
247
+ }
231
248
  var MercadoPagoClient = class {
232
249
  accessToken;
233
250
  baseUrl;
@@ -271,8 +288,10 @@ var MercadoPagoClient = class {
271
288
  Authorization: `Bearer ${this.accessToken}`,
272
289
  "Content-Type": "application/json"
273
290
  };
274
- if (options?.idempotencyKey) {
275
- headers["X-Idempotency-Key"] = options.idempotencyKey;
291
+ const isMutatingPost = method === "POST";
292
+ const idempotencyKey = options?.idempotencyKey ?? (isMutatingPost ? randomUuid() : void 0);
293
+ if (idempotencyKey) {
294
+ headers["X-Idempotency-Key"] = idempotencyKey;
276
295
  }
277
296
  const trace = this.traceContext?.();
278
297
  if (trace?.traceId && trace?.spanId) {
@@ -2429,6 +2448,377 @@ function paginateSubscriptionPayments(client, preapprovalId, opts = {}) {
2429
2448
  );
2430
2449
  }
2431
2450
 
2451
+ // src/tax-id.ts
2452
+ function validateTaxId(input, type) {
2453
+ switch (type) {
2454
+ case "DNI":
2455
+ return validateAR_DNI(input);
2456
+ case "CUIT":
2457
+ case "CUIL":
2458
+ return validateAR_CUIT(input, type);
2459
+ case "CPF":
2460
+ return validateBR_CPF(input);
2461
+ case "CNPJ":
2462
+ return validateBR_CNPJ(input);
2463
+ case "RFC":
2464
+ return validateMX_RFC(input);
2465
+ case "RUT_CL":
2466
+ return validateCL_RUT(input);
2467
+ case "NIT":
2468
+ return validateCO_NIT(input);
2469
+ case "RUT_UY":
2470
+ return validateUY_RUT(input);
2471
+ case "RUC":
2472
+ return validatePE_RUC(input);
2473
+ }
2474
+ }
2475
+ function validateAR_DNI(input) {
2476
+ const normalized = (input ?? "").replace(/\D/g, "");
2477
+ if (normalized.length === 0) {
2478
+ return failure(normalized, "DNI", "AR", "DNI vac\xEDo.");
2479
+ }
2480
+ if (normalized.length < 7 || normalized.length > 8) {
2481
+ return failure(
2482
+ normalized,
2483
+ "DNI",
2484
+ "AR",
2485
+ `Debe tener 7 u 8 d\xEDgitos; recib\xED ${normalized.length}.`
2486
+ );
2487
+ }
2488
+ return {
2489
+ valid: true,
2490
+ normalized,
2491
+ formatted: normalized.replace(/^(\d{1,2})(\d{3})(\d{3})$/, "$1.$2.$3"),
2492
+ type: "DNI",
2493
+ country: "AR",
2494
+ error: null
2495
+ };
2496
+ }
2497
+ var AR_CHECK_WEIGHTS = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2];
2498
+ function validateAR_CUIT(input, type) {
2499
+ const normalized = (input ?? "").replace(/\D/g, "");
2500
+ if (normalized.length !== 11) {
2501
+ return failure(
2502
+ normalized,
2503
+ type,
2504
+ "AR",
2505
+ `Debe tener 11 d\xEDgitos; recib\xED ${normalized.length}.`
2506
+ );
2507
+ }
2508
+ const sum = AR_CHECK_WEIGHTS.reduce(
2509
+ (acc, w, i) => acc + w * Number(normalized[i]),
2510
+ 0
2511
+ );
2512
+ const remainder = sum % 11;
2513
+ const expected = remainder === 0 ? 0 : remainder === 1 ? 9 : 11 - remainder;
2514
+ const actual = Number(normalized[10]);
2515
+ if (actual !== expected) {
2516
+ return failure(
2517
+ normalized,
2518
+ type,
2519
+ "AR",
2520
+ `D\xEDgito verificador inv\xE1lido. Esperado: ${expected}, recibido: ${actual}.`
2521
+ );
2522
+ }
2523
+ return {
2524
+ valid: true,
2525
+ normalized,
2526
+ formatted: `${normalized.slice(0, 2)}-${normalized.slice(2, 10)}-${normalized.slice(10)}`,
2527
+ type,
2528
+ country: "AR",
2529
+ error: null
2530
+ };
2531
+ }
2532
+ function validateBR_CPF(input) {
2533
+ const normalized = (input ?? "").replace(/\D/g, "");
2534
+ if (normalized.length !== 11) {
2535
+ return failure(
2536
+ normalized,
2537
+ "CPF",
2538
+ "BR",
2539
+ `Debe tener 11 d\xEDgitos; recib\xED ${normalized.length}.`
2540
+ );
2541
+ }
2542
+ if (/^(\d)\1{10}$/.test(normalized)) {
2543
+ return failure(normalized, "CPF", "BR", "CPF inv\xE1lido (todos los d\xEDgitos iguales).");
2544
+ }
2545
+ const computeDigit = (slice, weights) => {
2546
+ const sum = weights.reduce((acc, w, i) => acc + w * Number(slice[i]), 0);
2547
+ const r = sum * 10 % 11;
2548
+ return r === 10 ? 0 : r;
2549
+ };
2550
+ const d1 = computeDigit(normalized.slice(0, 9), [10, 9, 8, 7, 6, 5, 4, 3, 2]);
2551
+ const d2 = computeDigit(
2552
+ normalized.slice(0, 10),
2553
+ [11, 10, 9, 8, 7, 6, 5, 4, 3, 2]
2554
+ );
2555
+ if (d1 !== Number(normalized[9]) || d2 !== Number(normalized[10])) {
2556
+ return failure(normalized, "CPF", "BR", "D\xEDgitos verificadores inv\xE1lidos.");
2557
+ }
2558
+ return {
2559
+ valid: true,
2560
+ normalized,
2561
+ formatted: normalized.replace(
2562
+ /^(\d{3})(\d{3})(\d{3})(\d{2})$/,
2563
+ "$1.$2.$3-$4"
2564
+ ),
2565
+ type: "CPF",
2566
+ country: "BR",
2567
+ error: null
2568
+ };
2569
+ }
2570
+ function validateBR_CNPJ(input) {
2571
+ const normalized = (input ?? "").replace(/\D/g, "");
2572
+ if (normalized.length !== 14) {
2573
+ return failure(
2574
+ normalized,
2575
+ "CNPJ",
2576
+ "BR",
2577
+ `Debe tener 14 d\xEDgitos; recib\xED ${normalized.length}.`
2578
+ );
2579
+ }
2580
+ if (/^(\d)\1{13}$/.test(normalized)) {
2581
+ return failure(normalized, "CNPJ", "BR", "CNPJ inv\xE1lido (todos los d\xEDgitos iguales).");
2582
+ }
2583
+ const computeDigit = (slice, weights) => {
2584
+ const sum = weights.reduce((acc, w, i) => acc + w * Number(slice[i]), 0);
2585
+ const r = sum % 11;
2586
+ return r < 2 ? 0 : 11 - r;
2587
+ };
2588
+ const w1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
2589
+ const w2 = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
2590
+ const d1 = computeDigit(normalized.slice(0, 12), w1);
2591
+ const d2 = computeDigit(normalized.slice(0, 13), w2);
2592
+ if (d1 !== Number(normalized[12]) || d2 !== Number(normalized[13])) {
2593
+ return failure(normalized, "CNPJ", "BR", "D\xEDgitos verificadores inv\xE1lidos.");
2594
+ }
2595
+ return {
2596
+ valid: true,
2597
+ normalized,
2598
+ formatted: normalized.replace(
2599
+ /^(\d{2})(\d{3})(\d{3})(\d{4})(\d{2})$/,
2600
+ "$1.$2.$3/$4-$5"
2601
+ ),
2602
+ type: "CNPJ",
2603
+ country: "BR",
2604
+ error: null
2605
+ };
2606
+ }
2607
+ function validateMX_RFC(input) {
2608
+ const normalized = (input ?? "").trim().toUpperCase().replace(/[\s-]/g, "");
2609
+ const pfPattern = /^[A-ZÑ&]{4}\d{6}[A-Z\d]{3}$/;
2610
+ const pmPattern = /^[A-ZÑ&]{3}\d{6}[A-Z\d]{3}$/;
2611
+ if (!pfPattern.test(normalized) && !pmPattern.test(normalized)) {
2612
+ return failure(
2613
+ normalized,
2614
+ "RFC",
2615
+ "MX",
2616
+ "RFC mal formado. Persona f\xEDsica: 4 letras + YYMMDD + 3 alfanum\xE9ricos. Persona moral: 3 letras + YYMMDD + 3 alfanum\xE9ricos."
2617
+ );
2618
+ }
2619
+ return {
2620
+ valid: true,
2621
+ normalized,
2622
+ formatted: normalized,
2623
+ type: "RFC",
2624
+ country: "MX",
2625
+ error: null
2626
+ };
2627
+ }
2628
+ function validateCL_RUT(input) {
2629
+ const cleaned = (input ?? "").replace(/[^0-9kK]/g, "").toUpperCase();
2630
+ if (cleaned.length < 2) {
2631
+ return failure(cleaned, "RUT_CL", "CL", "RUT vac\xEDo o muy corto.");
2632
+ }
2633
+ const body = cleaned.slice(0, -1);
2634
+ const dv = cleaned.slice(-1);
2635
+ if (!/^\d+$/.test(body)) {
2636
+ return failure(cleaned, "RUT_CL", "CL", "Cuerpo del RUT debe ser num\xE9rico.");
2637
+ }
2638
+ let sum = 0;
2639
+ let multiplier = 2;
2640
+ for (let i = body.length - 1; i >= 0; i--) {
2641
+ sum += Number(body[i]) * multiplier;
2642
+ multiplier = multiplier === 7 ? 2 : multiplier + 1;
2643
+ }
2644
+ const r = 11 - sum % 11;
2645
+ const expected = r === 11 ? "0" : r === 10 ? "K" : String(r);
2646
+ if (dv !== expected) {
2647
+ return failure(
2648
+ cleaned,
2649
+ "RUT_CL",
2650
+ "CL",
2651
+ `D\xEDgito verificador inv\xE1lido. Esperado: ${expected}, recibido: ${dv}.`
2652
+ );
2653
+ }
2654
+ return {
2655
+ valid: true,
2656
+ normalized: cleaned,
2657
+ formatted: `${formatThousands(body)}-${dv}`,
2658
+ type: "RUT_CL",
2659
+ country: "CL",
2660
+ error: null
2661
+ };
2662
+ }
2663
+ var CO_NIT_WEIGHTS = [3, 7, 13, 17, 19, 23, 29, 37, 41, 43, 47, 53, 59, 67, 71];
2664
+ function validateCO_NIT(input) {
2665
+ const cleaned = (input ?? "").replace(/[^0-9-]/g, "");
2666
+ const parts = cleaned.split("-");
2667
+ if (parts.length !== 2 || !/^\d+$/.test(parts[0]) || !/^\d$/.test(parts[1])) {
2668
+ return failure(
2669
+ cleaned,
2670
+ "NIT",
2671
+ "CO",
2672
+ "NIT debe tener formato: d\xEDgitos + '-' + d\xEDgito verificador."
2673
+ );
2674
+ }
2675
+ const body = parts[0];
2676
+ const dv = Number(parts[1]);
2677
+ if (body.length > CO_NIT_WEIGHTS.length) {
2678
+ return failure(cleaned, "NIT", "CO", "NIT excesivamente largo.");
2679
+ }
2680
+ let sum = 0;
2681
+ for (let i = 0; i < body.length; i++) {
2682
+ sum += Number(body[body.length - 1 - i]) * CO_NIT_WEIGHTS[i];
2683
+ }
2684
+ const r = sum % 11;
2685
+ const expected = r > 1 ? 11 - r : r;
2686
+ if (dv !== expected) {
2687
+ return failure(
2688
+ cleaned,
2689
+ "NIT",
2690
+ "CO",
2691
+ `D\xEDgito verificador inv\xE1lido. Esperado: ${expected}, recibido: ${dv}.`
2692
+ );
2693
+ }
2694
+ return {
2695
+ valid: true,
2696
+ normalized: body + parts[1],
2697
+ formatted: cleaned,
2698
+ type: "NIT",
2699
+ country: "CO",
2700
+ error: null
2701
+ };
2702
+ }
2703
+ var UY_RUT_WEIGHTS = [4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
2704
+ function validateUY_RUT(input) {
2705
+ const normalized = (input ?? "").replace(/\D/g, "");
2706
+ if (normalized.length !== 12) {
2707
+ return failure(
2708
+ normalized,
2709
+ "RUT_UY",
2710
+ "UY",
2711
+ `Debe tener 12 d\xEDgitos; recib\xED ${normalized.length}.`
2712
+ );
2713
+ }
2714
+ let sum = 0;
2715
+ for (let i = 0; i < 11; i++) {
2716
+ sum += Number(normalized[i]) * UY_RUT_WEIGHTS[i];
2717
+ }
2718
+ const r = sum % 11;
2719
+ const expected = r === 0 ? 0 : 11 - r;
2720
+ if (Number(normalized[11]) !== expected) {
2721
+ return failure(
2722
+ normalized,
2723
+ "RUT_UY",
2724
+ "UY",
2725
+ `D\xEDgito verificador inv\xE1lido. Esperado: ${expected}, recibido: ${normalized[11]}.`
2726
+ );
2727
+ }
2728
+ return {
2729
+ valid: true,
2730
+ normalized,
2731
+ formatted: normalized.replace(
2732
+ /^(\d{2})(\d{6})(\d{3})(\d{1})$/,
2733
+ "$1$2$3$4"
2734
+ ),
2735
+ type: "RUT_UY",
2736
+ country: "UY",
2737
+ error: null
2738
+ };
2739
+ }
2740
+ var PE_RUC_WEIGHTS = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2];
2741
+ function validatePE_RUC(input) {
2742
+ const normalized = (input ?? "").replace(/\D/g, "");
2743
+ if (normalized.length !== 11) {
2744
+ return failure(
2745
+ normalized,
2746
+ "RUC",
2747
+ "PE",
2748
+ `Debe tener 11 d\xEDgitos; recib\xED ${normalized.length}.`
2749
+ );
2750
+ }
2751
+ const prefix = normalized.slice(0, 2);
2752
+ if (!["10", "15", "17", "20"].includes(prefix)) {
2753
+ return failure(
2754
+ normalized,
2755
+ "RUC",
2756
+ "PE",
2757
+ `Prefijo ${prefix} no v\xE1lido. Debe ser 10, 15, 17 o 20.`
2758
+ );
2759
+ }
2760
+ let sum = 0;
2761
+ for (let i = 0; i < 10; i++) {
2762
+ sum += Number(normalized[i]) * PE_RUC_WEIGHTS[i];
2763
+ }
2764
+ const r = 11 - sum % 11;
2765
+ const expected = r === 11 ? 0 : r === 10 ? 1 : r;
2766
+ if (Number(normalized[10]) !== expected) {
2767
+ return failure(
2768
+ normalized,
2769
+ "RUC",
2770
+ "PE",
2771
+ `D\xEDgito verificador inv\xE1lido. Esperado: ${expected}, recibido: ${normalized[10]}.`
2772
+ );
2773
+ }
2774
+ return {
2775
+ valid: true,
2776
+ normalized,
2777
+ formatted: normalized,
2778
+ type: "RUC",
2779
+ country: "PE",
2780
+ error: null
2781
+ };
2782
+ }
2783
+ function failure(normalized, type, country, error) {
2784
+ return {
2785
+ valid: false,
2786
+ normalized,
2787
+ formatted: null,
2788
+ type,
2789
+ country,
2790
+ error
2791
+ };
2792
+ }
2793
+ function formatThousands(s) {
2794
+ return s.replace(/\B(?=(\d{3})+(?!\d))/g, ".");
2795
+ }
2796
+ function detectAndValidate(input, country) {
2797
+ const cleaned = input.replace(/\D/g, "");
2798
+ switch (country) {
2799
+ case "AR": {
2800
+ if (cleaned.length === 7 || cleaned.length === 8) return validateTaxId(input, "DNI");
2801
+ if (cleaned.length === 11) return validateTaxId(input, "CUIT");
2802
+ return null;
2803
+ }
2804
+ case "BR": {
2805
+ if (cleaned.length === 11) return validateTaxId(input, "CPF");
2806
+ if (cleaned.length === 14) return validateTaxId(input, "CNPJ");
2807
+ return null;
2808
+ }
2809
+ case "MX":
2810
+ return validateTaxId(input, "RFC");
2811
+ case "CL":
2812
+ return validateTaxId(input, "RUT_CL");
2813
+ case "CO":
2814
+ return validateTaxId(input, "NIT");
2815
+ case "UY":
2816
+ return validateTaxId(input, "RUT_UY");
2817
+ case "PE":
2818
+ return validateTaxId(input, "RUC");
2819
+ }
2820
+ }
2821
+
2432
2822
  // src/test-cards.ts
2433
2823
  var TEST_CARDS_AR = {
2434
2824
  VISA_CREDIT: {
@@ -2748,7 +3138,9 @@ var DEFAULT_DESCRIPTIONS = {
2748
3138
  confirm_3ds_challenge: "After the buyer completes a 3DS challenge (redirected back from challengeUrl), call this to poll MP and confirm whether the payment is now resolved. Polls get_payment up to N times with exponential backoff. Returns { payment, threeDs, resolved, attempts }. USE THIS as the FINAL step in the 3DS flow (after analyze_payment_3ds detected a challenge_required). Without confirming, the payment stays in 'pending' indefinitely from the buyer's perspective.",
2749
3139
  // ── v0.10 — Auto-paginate variants ──────────────────────────────────────
2750
3140
  search_payments_all: "Collect ALL payments matching a filter \u2014 auto-paginates under the hood. Returns an array (NOT paginated) so the agent doesn't have to manage offset/limit loops manually. SAFETY: pass `max_items` to cap; without it, MP traversal is bounded by the toolkit's internal max (10,000 items) to prevent runaway iterations. USE WHEN the agent needs to enumerate everything (e.g., monthly reconciliation 'all approved payments in March'). For agent flows that only need 'first N matches', pass `max_items` directly.",
2751
- list_settlements_all: "Collect ALL settlements matching a filter \u2014 auto-paginates. Pass `max_items` to cap. Use for monthly bank-conciliation reports."
3141
+ list_settlements_all: "Collect ALL settlements matching a filter \u2014 auto-paginates. Pass `max_items` to cap. Use for monthly bank-conciliation reports.",
3142
+ // ── v0.11 — TaxID validation cross-LATAM (pure) ──────────────────────────
3143
+ validate_tax_id: "PURE HELPER (no network, sub-ms) \u2014 validates a tax ID against the appropriate country algorithm. Supports AR (DNI/CUIT/CUIL with modulo-11), BR (CPF/CNPJ with two-step weighted modulo), MX (RFC structure), CL (RUT with K digit), CO (NIT modulo-11), UY (RUT 12-digit checksum), PE (RUC 11-digit + prefix validation). Returns { valid, normalized, formatted, type, country, error }. USE THIS BEFORE submitting buyer identification to MP \u2014 invalid tax IDs cause 4xx rejections. Surface the Spanish error verbatim."
2752
3144
  };
2753
3145
  function mercadoPagoTools(client, options) {
2754
3146
  const desc = (name) => options.descriptions?.[name] ?? DEFAULT_DESCRIPTIONS[name];
@@ -2876,7 +3268,45 @@ function mercadoPagoTools(client, options) {
2876
3268
  type: zod.z.enum(["DNI", "CUIT", "CUIL"]),
2877
3269
  number: zod.z.string()
2878
3270
  }).optional().describe("Payer identification \u2014 required for some payment types in AR"),
2879
- statement_descriptor: zod.z.string().max(13).optional().describe("Shows on buyer's card statement (max 13 chars)")
3271
+ statement_descriptor: zod.z.string().max(13).optional().describe("Shows on buyer's card statement (max 13 chars)"),
3272
+ // v0.11 — fraud scoring enrichment fields
3273
+ additional_info: zod.z.object({
3274
+ ip_address: zod.z.string().optional().describe(
3275
+ "Buyer's IP address (from req.headers X-Forwarded-For). STRONGLY RECOMMENDED for card payments \u2014 improves MP fraud scoring confidence and reduces false-positive rejections (3-5x lower per RG 5286/2023)."
3276
+ ),
3277
+ referral_url: zod.z.string().url().optional().describe("Page the buyer came from"),
3278
+ payer: zod.z.object({
3279
+ first_name: zod.z.string().optional(),
3280
+ last_name: zod.z.string().optional(),
3281
+ phone: zod.z.object({ area_code: zod.z.string().optional(), number: zod.z.string().optional() }).optional(),
3282
+ address: zod.z.object({
3283
+ zip_code: zod.z.string().optional(),
3284
+ street_name: zod.z.string().optional(),
3285
+ street_number: zod.z.number().optional()
3286
+ }).optional(),
3287
+ registration_date: zod.z.string().optional().describe("ISO 8601 \u2014 when the buyer registered on YOUR platform"),
3288
+ authentication_type: zod.z.string().optional(),
3289
+ is_prime_user: zod.z.boolean().optional(),
3290
+ is_first_purchase_online: zod.z.boolean().optional(),
3291
+ last_purchase: zod.z.string().optional()
3292
+ }).optional(),
3293
+ shipments: zod.z.object({
3294
+ receiver_address: zod.z.object({
3295
+ zip_code: zod.z.string().optional(),
3296
+ street_name: zod.z.string().optional(),
3297
+ street_number: zod.z.number().optional(),
3298
+ floor: zod.z.string().optional(),
3299
+ apartment: zod.z.string().optional(),
3300
+ city_name: zod.z.string().optional(),
3301
+ state_name: zod.z.string().optional(),
3302
+ country_name: zod.z.string().optional()
3303
+ }).optional(),
3304
+ express_shipment: zod.z.boolean().optional(),
3305
+ local_pickup: zod.z.boolean().optional()
3306
+ }).optional()
3307
+ }).optional().describe(
3308
+ "Fraud scoring enrichment. Pass IP address + payer profile + shipping address for materially better approval rates on card payments."
3309
+ )
2880
3310
  }),
2881
3311
  execute: async (input) => {
2882
3312
  const payment = await client.createPayment({
@@ -2889,6 +3319,7 @@ function mercadoPagoTools(client, options) {
2889
3319
  ...input.external_reference !== void 0 ? { externalReference: input.external_reference } : {},
2890
3320
  ...input.identification !== void 0 ? { identification: input.identification } : {},
2891
3321
  ...input.statement_descriptor !== void 0 ? { statementDescriptor: input.statement_descriptor } : {},
3322
+ ...input.additional_info !== void 0 ? { additionalInfo: input.additional_info } : {},
2892
3323
  ...options.notificationUrl !== void 0 ? { notificationUrl: options.notificationUrl } : {},
2893
3324
  // Deterministic idempotency key — safe to retry, same inputs always
2894
3325
  // produce the same key (MP dedupes on its side).
@@ -4642,6 +5073,34 @@ function mercadoPagoTools(client, options) {
4642
5073
  const all = await collect(paginateSettlements(client, filterClean, opts));
4643
5074
  return { ok: true, count: all.length, settlements: all };
4644
5075
  }
5076
+ }),
5077
+ // ─────────────────────────────────────────────────────────────────────
5078
+ // v0.11 — TaxID validation cross-LATAM (pure)
5079
+ // ─────────────────────────────────────────────────────────────────────
5080
+ validate_tax_id: ai.tool({
5081
+ description: desc("validate_tax_id"),
5082
+ inputSchema: zod.z.object({
5083
+ tax_id: zod.z.string().min(1).describe(
5084
+ "The tax ID to validate. Accepts any format with or without separators (20-41758101-5, 20.41758101.5, 20417581015 all work for AR CUIT)."
5085
+ ),
5086
+ type: zod.z.enum([
5087
+ "DNI",
5088
+ "CUIT",
5089
+ "CUIL",
5090
+ "CPF",
5091
+ "CNPJ",
5092
+ "RFC",
5093
+ "RUT_CL",
5094
+ "NIT",
5095
+ "RUT_UY",
5096
+ "RUC"
5097
+ ]).describe(
5098
+ "TaxID type. AR: DNI/CUIT/CUIL. BR: CPF (persona f\xEDsica) / CNPJ (persona jur\xEDdica). MX: RFC. CL: RUT_CL. CO: NIT. UY: RUT_UY. PE: RUC."
5099
+ )
5100
+ }),
5101
+ execute: async ({ tax_id, type }) => {
5102
+ return validateTaxId(tax_id, type);
5103
+ }
4645
5104
  })
4646
5105
  };
4647
5106
  }
@@ -5152,6 +5611,123 @@ function sleep2(ms) {
5152
5611
  return new Promise((resolve) => setTimeout(resolve, ms));
5153
5612
  }
5154
5613
 
5614
+ // src/middleware.ts
5615
+ function compose(...middlewares) {
5616
+ return (tool2) => {
5617
+ return middlewares.reduceRight((wrapped, mw) => mw(wrapped), tool2);
5618
+ };
5619
+ }
5620
+ function withAuditLog(logger, operation, actor) {
5621
+ return (tool2) => {
5622
+ const original = tool2.execute;
5623
+ if (!original) return tool2;
5624
+ return {
5625
+ ...tool2,
5626
+ execute: (async (input, opts) => {
5627
+ return logger.record({
5628
+ operation,
5629
+ input,
5630
+ ...actor !== void 0 ? { actor } : {},
5631
+ fn: () => original(input, opts)
5632
+ });
5633
+ })
5634
+ };
5635
+ };
5636
+ }
5637
+ function withRateLimit(limiter) {
5638
+ return (tool2) => {
5639
+ const original = tool2.execute;
5640
+ if (!original) return tool2;
5641
+ return {
5642
+ ...tool2,
5643
+ execute: (async (input, opts) => {
5644
+ await limiter.acquire();
5645
+ return original(input, opts);
5646
+ })
5647
+ };
5648
+ };
5649
+ }
5650
+ function withMetrics(toolName, hook) {
5651
+ return (tool2) => {
5652
+ const original = tool2.execute;
5653
+ if (!original) return tool2;
5654
+ return {
5655
+ ...tool2,
5656
+ execute: (async (input, opts) => {
5657
+ const t0 = Date.now();
5658
+ try {
5659
+ const result = await original(input, opts);
5660
+ hook.onMetric({
5661
+ toolName,
5662
+ durationMs: Date.now() - t0,
5663
+ success: true
5664
+ });
5665
+ return result;
5666
+ } catch (err) {
5667
+ hook.onMetric({
5668
+ toolName,
5669
+ durationMs: Date.now() - t0,
5670
+ success: false,
5671
+ errorCode: extractErrorCode2(err)
5672
+ });
5673
+ throw err;
5674
+ }
5675
+ })
5676
+ };
5677
+ };
5678
+ }
5679
+ var defaultShouldRetry = (err) => {
5680
+ if (err && typeof err === "object" && "status" in err) {
5681
+ const status = err.status;
5682
+ if (typeof status === "number" && status >= 400 && status < 500) {
5683
+ return false;
5684
+ }
5685
+ }
5686
+ return true;
5687
+ };
5688
+ function withRetry(opts = {}) {
5689
+ const maxAttempts = opts.maxAttempts ?? 3;
5690
+ const baseBackoff = opts.baseBackoffMs ?? 250;
5691
+ const shouldRetry = opts.shouldRetry ?? defaultShouldRetry;
5692
+ return (tool2) => {
5693
+ const original = tool2.execute;
5694
+ if (!original) return tool2;
5695
+ return {
5696
+ ...tool2,
5697
+ execute: (async (input, opts2) => {
5698
+ let attempt = 0;
5699
+ while (true) {
5700
+ try {
5701
+ return await original(input, opts2);
5702
+ } catch (err) {
5703
+ attempt++;
5704
+ if (attempt >= maxAttempts || !shouldRetry(err, attempt)) {
5705
+ throw err;
5706
+ }
5707
+ const delayMs = baseBackoff * Math.pow(2, attempt - 1);
5708
+ opts.onRetry?.({ attempt, error: err, delayMs });
5709
+ await new Promise((r) => setTimeout(r, delayMs));
5710
+ }
5711
+ }
5712
+ })
5713
+ };
5714
+ };
5715
+ }
5716
+ function applyToAllTools(tools, middleware) {
5717
+ const out = {};
5718
+ for (const [name, tool2] of Object.entries(tools)) {
5719
+ out[name] = middleware(tool2);
5720
+ }
5721
+ return out;
5722
+ }
5723
+ function extractErrorCode2(err) {
5724
+ if (err && typeof err === "object") {
5725
+ const e = err;
5726
+ return e.code ?? e.name ?? "unknown_error";
5727
+ }
5728
+ return "unknown_error";
5729
+ }
5730
+
5155
5731
  exports.AHORA_PROGRAM_PROMOS = AHORA_PROGRAM_PROMOS;
5156
5732
  exports.AR_ISSUER_PROMOS = AR_ISSUER_PROMOS;
5157
5733
  exports.AuditLogger = AuditLogger;
@@ -5178,12 +5754,15 @@ exports.TEST_PAYERS_AR = TEST_PAYERS_AR;
5178
5754
  exports.TokenBucketRateLimiter = TokenBucketRateLimiter;
5179
5755
  exports.WebhookDedup = WebhookDedup;
5180
5756
  exports.analyze3DS = analyze3DS;
5757
+ exports.applyToAllTools = applyToAllTools;
5181
5758
  exports.buildAuthorizeUrl = buildAuthorizeUrl;
5182
5759
  exports.buildTestCardScenario = buildTestCardScenario;
5183
5760
  exports.classifyError = classifyError;
5184
5761
  exports.collect = collect;
5762
+ exports.compose = compose;
5185
5763
  exports.computeMarketplaceFee = computeMarketplaceFee;
5186
5764
  exports.confirmChallengeAndPoll = confirmChallengeAndPoll;
5765
+ exports.detectAndValidate = detectAndValidate;
5187
5766
  exports.exchangeCodeForToken = exchangeCodeForToken;
5188
5767
  exports.expirationTimeMs = expirationTimeMs;
5189
5768
  exports.explainPaymentStatus = explainPaymentStatus;
@@ -5200,6 +5779,11 @@ exports.paginateSubscriptionPlans = paginateSubscriptionPlans;
5200
5779
  exports.paginateSubscriptions = paginateSubscriptions;
5201
5780
  exports.parseWebhookEvent = parseWebhookEvent;
5202
5781
  exports.refreshAccessToken = refreshAccessToken;
5782
+ exports.validateTaxId = validateTaxId;
5203
5783
  exports.verifyWebhookSignature = verifyWebhookSignature;
5784
+ exports.withAuditLog = withAuditLog;
5785
+ exports.withMetrics = withMetrics;
5786
+ exports.withRateLimit = withRateLimit;
5787
+ exports.withRetry = withRetry;
5204
5788
  //# sourceMappingURL=index.cjs.map
5205
5789
  //# sourceMappingURL=index.cjs.map