@productmaker/mcp 2.0.0 → 2.1.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.
Files changed (3) hide show
  1. package/dist/http.js +783 -323
  2. package/dist/stdio.js +783 -323
  3. package/package.json +1 -1
package/dist/http.js CHANGED
@@ -76,6 +76,17 @@ var ApiClient = class {
76
76
  });
77
77
  return this.parse(res);
78
78
  }
79
+ async putJson(path3, body) {
80
+ const res = await fetch(`${this.baseUrl}${path3}`, {
81
+ method: "PUT",
82
+ headers: {
83
+ ...this.authHeaders(),
84
+ "Content-Type": "application/json"
85
+ },
86
+ body: JSON.stringify(body)
87
+ });
88
+ return this.parse(res);
89
+ }
79
90
  async deleteJson(path3) {
80
91
  const res = await fetch(`${this.baseUrl}${path3}`, {
81
92
  method: "DELETE",
@@ -142,10 +153,10 @@ var ApiClient = class {
142
153
  };
143
154
 
144
155
  // src/index.ts
145
- var MCP_VERSION = "2.0.0";
156
+ var MCP_VERSION = "2.1.0";
146
157
 
147
158
  // src/tools/tasks.ts
148
- import { z as z39 } from "zod";
159
+ import { z as z40 } from "zod";
149
160
 
150
161
  // src/resolve-image.ts
151
162
  import { lookup as dnsLookup } from "dns/promises";
@@ -1392,6 +1403,8 @@ var INGEST_ALLOWED_IMAGE_MIME_TYPES = [
1392
1403
  var PRODUCT_CONTEXT_MAX_CHARS = 3e3;
1393
1404
  var INGEST_ALLOWED_IMAGE_MIME_TYPE_SET = new Set(INGEST_ALLOWED_IMAGE_MIME_TYPES);
1394
1405
  var IngestOptions = z13.object({
1406
+ /** Optional owner-scoped guided journey context. Never bypasses ingest gates. */
1407
+ dropshippingProjectId: z13.string().uuid().optional(),
1395
1408
  languageCode: LanguageCodeSchema.default("es-CO"),
1396
1409
  ugcFormat: VideoFormat.default("single_15s"),
1397
1410
  country: CountrySchema,
@@ -6546,17 +6559,185 @@ var RegenerateEditRequestSchema = z36.object({
6546
6559
  comment: z36.string().min(1).max(2e3)
6547
6560
  });
6548
6561
 
6549
- // ../../packages/types/src/scene-subject-contract.ts
6562
+ // ../../packages/types/src/dropshipping-project.ts
6550
6563
  import { z as z37 } from "zod";
6551
- var SceneSubjectContractSchema = z37.object({
6552
- maxPeople: z37.number().int().min(1).max(10),
6553
- minPeople: z37.number().int().min(0).max(10),
6554
- minDistinctPeople: z37.number().int().min(0).max(10),
6555
- maxHands: z37.number().int().min(0).max(20)
6564
+ var DropshippingProjectStatusSchema = z37.enum([
6565
+ "active",
6566
+ "completed",
6567
+ "archived"
6568
+ ]);
6569
+ var DropshippingProjectPhaseSchema = z37.enum([
6570
+ "research",
6571
+ "costing",
6572
+ "media",
6573
+ "generating",
6574
+ "storefront",
6575
+ "advertising",
6576
+ "complete"
6577
+ ]);
6578
+ var DropshippingProjectCostingInputSchema = z37.object({
6579
+ country: z37.string().min(2).max(3),
6580
+ supplierCost: z37.number().finite().positive(),
6581
+ shippingCost: z37.number().finite().nonnegative(),
6582
+ returnRate: z37.number().finite().min(0).lt(1),
6583
+ cancellationRate: z37.number().finite().min(0).lt(1),
6584
+ adsManagerCpa: z37.number().finite().nonnegative(),
6585
+ desiredMargin: z37.number().finite().min(0).lt(1)
6586
+ }).superRefine(({ returnRate, cancellationRate }, context) => {
6587
+ if (returnRate + cancellationRate >= 1) {
6588
+ context.addIssue({
6589
+ code: z37.ZodIssueCode.custom,
6590
+ path: [
6591
+ "cancellationRate"
6592
+ ],
6593
+ message: "delivery_rate_positive"
6594
+ });
6595
+ }
6596
+ });
6597
+ var DropshippingProjectCostingResultSchema = z37.object({
6598
+ country: z37.string().min(2).max(3),
6599
+ deliveryRate: z37.number().finite(),
6600
+ adjustedShippingCost: z37.number().finite(),
6601
+ adjustedCpa: z37.number().finite(),
6602
+ totalCost: z37.number().finite(),
6603
+ suggestedPrice: z37.number().finite(),
6604
+ baseCommercialPrice: z37.number().finite(),
6605
+ minimumSafePrice: z37.number().finite(),
6606
+ commercialPrice: z37.number().finite(),
6607
+ comparisonPrice: z37.number().finite(),
6608
+ estimatedProfit: z37.number().finite(),
6609
+ actualMargin: z37.number().finite(),
6610
+ breakEvenAdsManagerCpa: z37.number().finite(),
6611
+ wasPriceAutoAdjusted: z37.boolean(),
6612
+ profitabilityStatus: z37.literal("healthy")
6613
+ });
6614
+ var HttpUrlSchema = z37.string().url().max(2e3).refine((value) => {
6615
+ try {
6616
+ return [
6617
+ "http:",
6618
+ "https:"
6619
+ ].includes(new URL(value).protocol);
6620
+ } catch {
6621
+ return false;
6622
+ }
6623
+ }, "Product URL must use HTTP(S)");
6624
+ var ResearchCandidateOriginSchema = z37.object({
6625
+ type: z37.literal("research_candidate"),
6626
+ runId: z37.string().uuid(),
6627
+ candidateSourceId: z37.string().min(1).max(160)
6628
+ });
6629
+ var TaskOriginSchema = z37.object({
6630
+ type: z37.literal("task"),
6631
+ taskId: z37.string().uuid()
6632
+ });
6633
+ var ManualOriginSchema = z37.object({
6634
+ type: z37.literal("manual"),
6635
+ clientRequestId: z37.string().uuid(),
6636
+ country: z37.string().min(2).max(3).optional()
6637
+ });
6638
+ var CreateDropshippingProjectRequestSchema = z37.object({
6639
+ origin: z37.discriminatedUnion("type", [
6640
+ ResearchCandidateOriginSchema,
6641
+ TaskOriginSchema,
6642
+ ManualOriginSchema
6643
+ ]),
6644
+ productName: z37.string().trim().min(1).max(240).optional(),
6645
+ productUrl: HttpUrlSchema.optional(),
6646
+ country: z37.string().min(2).max(3).optional(),
6647
+ languageCode: z37.string().min(2).max(16).optional()
6648
+ }).strict();
6649
+ var SaveDropshippingProjectCostingRequestSchema = z37.object({
6650
+ input: DropshippingProjectCostingInputSchema,
6651
+ version: z37.number().int().positive()
6652
+ }).strict();
6653
+ var DropshippingProjectNextActionSchema = z37.discriminatedUnion("kind", [
6654
+ z37.object({
6655
+ kind: z37.literal("cost_product"),
6656
+ href: z37.string().min(1)
6657
+ }),
6658
+ z37.object({
6659
+ kind: z37.literal("provide_product_media"),
6660
+ href: z37.string().min(1)
6661
+ }),
6662
+ z37.object({
6663
+ kind: z37.literal("generate_creatives"),
6664
+ href: z37.string().min(1)
6665
+ }),
6666
+ z37.object({
6667
+ kind: z37.literal("wait_for_creatives"),
6668
+ href: z37.string().min(1)
6669
+ }),
6670
+ z37.object({
6671
+ kind: z37.literal("repair_creatives"),
6672
+ href: z37.string().min(1)
6673
+ }),
6674
+ z37.object({
6675
+ kind: z37.literal("complete_creative_set"),
6676
+ href: z37.string().min(1)
6677
+ }),
6678
+ z37.object({
6679
+ kind: z37.literal("connect_shopify"),
6680
+ href: z37.string().min(1)
6681
+ }),
6682
+ z37.object({
6683
+ kind: z37.literal("publish_storefront"),
6684
+ href: z37.string().min(1)
6685
+ }),
6686
+ z37.object({
6687
+ kind: z37.literal("wait_for_storefront"),
6688
+ href: z37.string().min(1)
6689
+ }),
6690
+ z37.object({
6691
+ kind: z37.literal("connect_meta"),
6692
+ href: z37.string().min(1)
6693
+ }),
6694
+ z37.object({
6695
+ kind: z37.literal("create_meta_campaign"),
6696
+ href: z37.string().min(1)
6697
+ }),
6698
+ z37.object({
6699
+ kind: z37.literal("repair_meta_campaign"),
6700
+ href: z37.string().min(1)
6701
+ }),
6702
+ z37.object({
6703
+ kind: z37.literal("review_paused_campaign"),
6704
+ href: z37.string().min(1)
6705
+ })
6706
+ ]);
6707
+ var DropshippingProjectResponseSchema = z37.object({
6708
+ id: z37.string().uuid(),
6709
+ taskId: z37.string().uuid().nullable(),
6710
+ status: DropshippingProjectStatusSchema,
6711
+ originKey: z37.string().min(1),
6712
+ researchRunId: z37.string().uuid().nullable(),
6713
+ candidateSourceId: z37.string().nullable(),
6714
+ productName: z37.string().nullable(),
6715
+ productUrl: HttpUrlSchema.nullable(),
6716
+ referenceImageUrl: z37.string().url().nullable(),
6717
+ hasReferenceImage: z37.boolean(),
6718
+ targetCountry: z37.string().nullable(),
6719
+ languageCode: z37.string().nullable(),
6720
+ costingInput: DropshippingProjectCostingInputSchema.nullable(),
6721
+ costingResult: DropshippingProjectCostingResultSchema.nullable(),
6722
+ costingVersion: z37.number().int().positive().nullable(),
6723
+ version: z37.number().int().positive(),
6724
+ phase: DropshippingProjectPhaseSchema,
6725
+ nextAction: DropshippingProjectNextActionSchema.nullable(),
6726
+ createdAt: z37.string().datetime(),
6727
+ updatedAt: z37.string().datetime()
6728
+ });
6729
+
6730
+ // ../../packages/types/src/scene-subject-contract.ts
6731
+ import { z as z38 } from "zod";
6732
+ var SceneSubjectContractSchema = z38.object({
6733
+ maxPeople: z38.number().int().min(1).max(10),
6734
+ minPeople: z38.number().int().min(0).max(10),
6735
+ minDistinctPeople: z38.number().int().min(0).max(10),
6736
+ maxHands: z38.number().int().min(0).max(20)
6556
6737
  }).superRefine((value, ctx) => {
6557
6738
  if (value.minPeople > value.maxPeople) {
6558
6739
  ctx.addIssue({
6559
- code: z37.ZodIssueCode.custom,
6740
+ code: z38.ZodIssueCode.custom,
6560
6741
  path: [
6561
6742
  "minPeople"
6562
6743
  ],
@@ -6565,7 +6746,7 @@ var SceneSubjectContractSchema = z37.object({
6565
6746
  }
6566
6747
  if (value.minDistinctPeople > value.maxPeople) {
6567
6748
  ctx.addIssue({
6568
- code: z37.ZodIssueCode.custom,
6749
+ code: z38.ZodIssueCode.custom,
6569
6750
  path: [
6570
6751
  "minDistinctPeople"
6571
6752
  ],
@@ -6574,7 +6755,7 @@ var SceneSubjectContractSchema = z37.object({
6574
6755
  }
6575
6756
  if (value.minDistinctPeople > value.minPeople) {
6576
6757
  ctx.addIssue({
6577
- code: z37.ZodIssueCode.custom,
6758
+ code: z38.ZodIssueCode.custom,
6578
6759
  path: [
6579
6760
  "minDistinctPeople"
6580
6761
  ],
@@ -6590,6 +6771,11 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
6590
6771
  confirmation: "credit_consuming",
6591
6772
  schemaHash: "bab794a9700a8d3f42b97f444a776e7ed4dcd833038c52588f951b50970ff3fa"
6592
6773
  },
6774
+ {
6775
+ name: "cost_dropshipping_project",
6776
+ confirmation: "state_change",
6777
+ schemaHash: "746797b5400b170ba3a0704d432afff635794f0a7ae0b9da07b26d49e3b82242"
6778
+ },
6593
6779
  {
6594
6780
  name: "cost_product",
6595
6781
  confirmation: "none",
@@ -6603,7 +6789,7 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
6603
6789
  {
6604
6790
  name: "create_product_task",
6605
6791
  confirmation: "credit_consuming",
6606
- schemaHash: "5346684b007a40f6e8d53cc0b56ebb20aeb7e5ba48cdde43bc9f4e6163ed2d3f"
6792
+ schemaHash: "828ee643bb3a05a63c6e63730459d35ce1621cbd559bbc76a6c5fcec9a45654d"
6607
6793
  },
6608
6794
  {
6609
6795
  name: "edit_task_draft",
@@ -6665,6 +6851,11 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
6665
6851
  confirmation: "none",
6666
6852
  schemaHash: "c96e7042368ba6f241773d26d4a443894b092801d1c81054f3798865138edfba"
6667
6853
  },
6854
+ {
6855
+ name: "guide_dropshipping_launch",
6856
+ confirmation: "none",
6857
+ schemaHash: "0e7a1cb5ad5c6681473df5689336edcd7a63b8d68853359982e44bfd98a65db8"
6858
+ },
6668
6859
  {
6669
6860
  name: "list_meta_ad_accounts",
6670
6861
  confirmation: "none",
@@ -6724,6 +6915,11 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
6724
6915
  name: "resolve_pending_action",
6725
6916
  confirmation: "credit_consuming",
6726
6917
  schemaHash: "3a9f33a26fce63b20bece3a528f8eb16e917dabc8a9ec5f04e746bad84757aa3"
6918
+ },
6919
+ {
6920
+ name: "start_dropshipping_project",
6921
+ confirmation: "state_change",
6922
+ schemaHash: "2b621702e8da8bb894d392060e5e4f677a8dc26f454e991dbb929053715a4f8b"
6727
6923
  }
6728
6924
  ];
6729
6925
  var PRODUCTMAKER_MCP_CAPABILITY_NAMES = PRODUCTMAKER_MCP_CAPABILITIES.map((capability) => capability.name);
@@ -6967,10 +7163,10 @@ async function resolveMediaRef(ref, opts) {
6967
7163
  __name(resolveMediaRef, "resolveMediaRef");
6968
7164
 
6969
7165
  // src/tools/shared.ts
6970
- import { z as z38 } from "zod";
6971
- var Lang = z38.string().min(2).max(10);
6972
- var Country = z38.string().min(2).max(3);
6973
- var LandingStrategyEnum = z38.enum([
7166
+ import { z as z39 } from "zod";
7167
+ var Lang = z39.string().min(2).max(10);
7168
+ var Country = z39.string().min(2).max(3);
7169
+ var LandingStrategyEnum = z39.enum([
6974
7170
  "auto",
6975
7171
  "classic",
6976
7172
  "pain_solution",
@@ -6979,12 +7175,12 @@ var LandingStrategyEnum = z38.enum([
6979
7175
  "offer_value",
6980
7176
  "lifestyle_identity"
6981
7177
  ]);
6982
- var LandingImageFormatEnum = z38.enum([
7178
+ var LandingImageFormatEnum = z39.enum([
6983
7179
  "9:16",
6984
7180
  "4:5",
6985
7181
  "1:1"
6986
7182
  ]);
6987
- var VideoPresetEnum = z38.enum([
7183
+ var VideoPresetEnum = z39.enum([
6988
7184
  "auto",
6989
7185
  "ugc_dynamic",
6990
7186
  "ugc_how_to",
@@ -6996,7 +7192,7 @@ var VideoPresetEnum = z38.enum([
6996
7192
  "pixar_problem",
6997
7193
  "pixar_sassy"
6998
7194
  ]);
6999
- var TaskCreativeVariantVideoPresetEnum = z38.enum([
7195
+ var TaskCreativeVariantVideoPresetEnum = z39.enum([
7000
7196
  "auto",
7001
7197
  "ugc_dynamic",
7002
7198
  "ugc_how_to",
@@ -7061,7 +7257,7 @@ function resolveVideoPreset(preset) {
7061
7257
  return VIDEO_PRESET_RESOLUTION[preset] ?? null;
7062
7258
  }
7063
7259
  __name(resolveVideoPreset, "resolveVideoPreset");
7064
- var VideoTierEnum = z38.enum([
7260
+ var VideoTierEnum = z39.enum([
7065
7261
  "basic"
7066
7262
  ]);
7067
7263
  function resolveVideoTier(_tier) {
@@ -7076,17 +7272,17 @@ function resolveVideoFormatForDuration(durationSeconds) {
7076
7272
  return durationSeconds === 30 ? "double_30s" : "single_15s";
7077
7273
  }
7078
7274
  __name(resolveVideoFormatForDuration, "resolveVideoFormatForDuration");
7079
- var VideoResolutionEnum = z38.enum([
7275
+ var VideoResolutionEnum = z39.enum([
7080
7276
  "480p",
7081
7277
  "720p"
7082
7278
  ]);
7083
- var VideoStyleEnum = z38.enum([
7279
+ var VideoStyleEnum = z39.enum([
7084
7280
  "ugc",
7085
7281
  "cartoon_3d",
7086
7282
  "product_only",
7087
7283
  "ai_dynamic"
7088
7284
  ]);
7089
- var NarrativeStyleEnum = z38.enum([
7285
+ var NarrativeStyleEnum = z39.enum([
7090
7286
  "review",
7091
7287
  "ugc_how_to",
7092
7288
  "problem_product_cta",
@@ -7103,7 +7299,7 @@ var NarrativeStyleEnum = z38.enum([
7103
7299
  "ugc_skeptic_confession",
7104
7300
  "ugc_demo_review"
7105
7301
  ]);
7106
- var PublicNarrativeStyleEnum = z38.enum([
7302
+ var PublicNarrativeStyleEnum = z39.enum([
7107
7303
  "review",
7108
7304
  "ugc_how_to",
7109
7305
  "ugc_unboxing_chaos",
@@ -7114,7 +7310,7 @@ var PublicNarrativeStyleEnum = z38.enum([
7114
7310
  "animated_problem_pixar",
7115
7311
  "sassy_product_pixar"
7116
7312
  ]);
7117
- var VideoModelEnum = z38.enum([
7313
+ var VideoModelEnum = z39.enum([
7118
7314
  "veo",
7119
7315
  "wan",
7120
7316
  "bytedance",
@@ -7122,25 +7318,25 @@ var VideoModelEnum = z38.enum([
7122
7318
  "grok",
7123
7319
  "kling"
7124
7320
  ]);
7125
- var ProductReferenceUserChoiceEnum = z38.enum([
7321
+ var ProductReferenceUserChoiceEnum = z39.enum([
7126
7322
  "same_product",
7127
7323
  "variants",
7128
7324
  "different_products",
7129
7325
  "combo_distinct"
7130
7326
  ]);
7131
- var ProductReferenceCandidateGroupingEnum = z38.enum([
7327
+ var ProductReferenceCandidateGroupingEnum = z39.enum([
7132
7328
  "same_product_same_variant",
7133
7329
  "same_product_different_variants",
7134
7330
  "different_products",
7135
7331
  "unclear"
7136
7332
  ]);
7137
- var GapDurationChoiceMirror = z38.object({
7138
- angleIndex: z38.number().int().nonnegative(),
7139
- recommendedSec: z38.union([
7140
- z38.literal(15),
7141
- z38.literal(30)
7333
+ var GapDurationChoiceMirror = z39.object({
7334
+ angleIndex: z39.number().int().nonnegative(),
7335
+ recommendedSec: z39.union([
7336
+ z39.literal(15),
7337
+ z39.literal(30)
7142
7338
  ]),
7143
- estimatedDeltaCredits: z38.number().int().nonnegative().optional()
7339
+ estimatedDeltaCredits: z39.number().int().nonnegative().optional()
7144
7340
  });
7145
7341
  function buildSelectedImageUrlsHash(urls) {
7146
7342
  const input = [
@@ -7154,13 +7350,13 @@ function buildSelectedImageUrlsHash(urls) {
7154
7350
  return `fnv1a:${hash.toString(16).padStart(8, "0")}:${urls.length}`;
7155
7351
  }
7156
7352
  __name(buildSelectedImageUrlsHash, "buildSelectedImageUrlsHash");
7157
- var ImageCreativeStyleEnum = z38.enum([
7353
+ var ImageCreativeStyleEnum = z39.enum([
7158
7354
  "studio",
7159
7355
  "floating",
7160
7356
  "ingredients",
7161
7357
  "in_use"
7162
7358
  ]);
7163
- var ImageCreativeArchetypeEnum = z38.enum([
7359
+ var ImageCreativeArchetypeEnum = z39.enum([
7164
7360
  "direct_response_product_ad",
7165
7361
  "hero_outcome",
7166
7362
  "offer_value_prop",
@@ -7169,23 +7365,23 @@ var ImageCreativeArchetypeEnum = z38.enum([
7169
7365
  "problem_solution",
7170
7366
  "lifestyle_identity"
7171
7367
  ]);
7172
- var ImageCreativeVisualTreatmentEnum = z38.enum([
7368
+ var ImageCreativeVisualTreatmentEnum = z39.enum([
7173
7369
  "performance_product_ad",
7174
7370
  "premium_dtc_carousel"
7175
7371
  ]);
7176
- var ImageCreativeAspectRatioEnum = z38.enum([
7372
+ var ImageCreativeAspectRatioEnum = z39.enum([
7177
7373
  "1:1",
7178
7374
  "4:5",
7179
7375
  "9:16",
7180
7376
  "16:9"
7181
7377
  ]);
7182
- var ImageRefSchema = z38.object({
7183
- url: z38.string().url().optional().describe('URL p\xFAblica HTTPS de la foto. Ej: una URL de Shopify CDN, Cloudinary, Imgur. \xDAsala cuando el usuario te diga "est\xE1 en X.com/foto.jpg" o cuando la foto ya est\xE9 hospedada.'),
7184
- path: z38.string().min(1).optional().describe('Ruta del archivo en el computador del usuario. Ej: "/Users/maria/Desktop/foto.jpg", "~/Pictures/camiseta.png", "C:\\Users\\Maria\\Desktop\\foto.jpg". El MCP local la lee del disco y la sube. SOLO funciona cuando el MCP corre local (stdio). NO funciona en mcp.productmaker.app (HTTP) \u2014 ah\xED usa `url`.')
7378
+ var ImageRefSchema = z39.object({
7379
+ url: z39.string().url().optional().describe('URL p\xFAblica HTTPS de la foto. Ej: una URL de Shopify CDN, Cloudinary, Imgur. \xDAsala cuando el usuario te diga "est\xE1 en X.com/foto.jpg" o cuando la foto ya est\xE9 hospedada.'),
7380
+ path: z39.string().min(1).optional().describe('Ruta del archivo en el computador del usuario. Ej: "/Users/maria/Desktop/foto.jpg", "~/Pictures/camiseta.png", "C:\\Users\\Maria\\Desktop\\foto.jpg". El MCP local la lee del disco y la sube. SOLO funciona cuando el MCP corre local (stdio). NO funciona en mcp.productmaker.app (HTTP) \u2014 ah\xED usa `url`.')
7185
7381
  }).refine((v) => Boolean(v.url) !== Boolean(v.path), {
7186
7382
  message: "each image entry must have exactly one of `url` or `path`"
7187
7383
  });
7188
- var ImagesInput = z38.array(ImageRefSchema).min(1).max(5).describe('1 a 5 fotos del MISMO producto. La PRIMERA es la principal (hero); las dem\xE1s son \xE1ngulos adicionales. NO mezcles productos distintos en una sola llamada.\n\nCada entrada tiene EXACTAMENTE uno de:\n\u2022 `url` \u2014 URL p\xFAblica HTTPS (Shopify CDN, Cloudinary). Prefi\xE9rela si la foto ya est\xE1 hospedada.\n\u2022 `path` \u2014 Ruta del archivo en el computador del usuario (ej. "/Users/maria/Desktop/foto.jpg", "~/Downloads/camiseta.png"). \xDAsala cuando el usuario te dijo d\xF3nde guard\xF3 la foto. Solo MCP local \u2014 el cliente web rechaza paths.\n\nSi el usuario te peg\xF3 la foto en el chat pero no te dio ni URL ni ruta: PREG\xDANTALE d\xF3nde la tiene guardada (Escritorio, Descargas, etc.). Si el `path` falla con IMAGE_PATH_NOT_FOUND, prueba 1-2 ubicaciones obvias alternativas (~/Desktop, ~/Downloads) antes de molestar al usuario. Tras 3 intentos fallidos, p\xEDdele la ruta completa.');
7384
+ var ImagesInput = z39.array(ImageRefSchema).min(1).max(5).describe('1 a 5 fotos del MISMO producto. La PRIMERA es la principal (hero); las dem\xE1s son \xE1ngulos adicionales. NO mezcles productos distintos en una sola llamada.\n\nCada entrada tiene EXACTAMENTE uno de:\n\u2022 `url` \u2014 URL p\xFAblica HTTPS (Shopify CDN, Cloudinary). Prefi\xE9rela si la foto ya est\xE1 hospedada.\n\u2022 `path` \u2014 Ruta del archivo en el computador del usuario (ej. "/Users/maria/Desktop/foto.jpg", "~/Downloads/camiseta.png"). \xDAsala cuando el usuario te dijo d\xF3nde guard\xF3 la foto. Solo MCP local \u2014 el cliente web rechaza paths.\n\nSi el usuario te peg\xF3 la foto en el chat pero no te dio ni URL ni ruta: PREG\xDANTALE d\xF3nde la tiene guardada (Escritorio, Descargas, etc.). Si el `path` falla con IMAGE_PATH_NOT_FOUND, prueba 1-2 ubicaciones obvias alternativas (~/Desktop, ~/Downloads) antes de molestar al usuario. Tras 3 intentos fallidos, p\xEDdele la ruta completa.');
7189
7385
  function asText(value) {
7190
7386
  return {
7191
7387
  content: [
@@ -7238,14 +7434,14 @@ async function submitCreative(api, opts) {
7238
7434
  });
7239
7435
  }
7240
7436
  __name(submitCreative, "submitCreative");
7241
- var ResearchTargetCountryEnum = z38.enum([
7437
+ var ResearchTargetCountryEnum = z39.enum([
7242
7438
  "CO",
7243
7439
  "MX",
7244
7440
  "PE",
7245
7441
  "EC",
7246
7442
  "CL"
7247
7443
  ]);
7248
- var ResearchCategoryEnum = z38.enum([
7444
+ var ResearchCategoryEnum = z39.enum([
7249
7445
  "auto",
7250
7446
  "automotive",
7251
7447
  "kitchen",
@@ -7291,6 +7487,15 @@ var McpErrorCode = {
7291
7487
  TASK_NOT_READY: "TASK_NOT_READY",
7292
7488
  CREATIVE_VARIANT_FAILED: "CREATIVE_VARIANT_FAILED",
7293
7489
  CREATIVE_VARIANT_DISPATCH_FAILED: "CREATIVE_VARIANT_DISPATCH_FAILED",
7490
+ // Guided dropshipping project / research
7491
+ DROPSHIPPING_PROJECT_NOT_FOUND: "DROPSHIPPING_PROJECT_NOT_FOUND",
7492
+ DROPSHIPPING_PROJECT_CANDIDATE_NOT_READY: "DROPSHIPPING_PROJECT_CANDIDATE_NOT_READY",
7493
+ DROPSHIPPING_PROJECT_VERSION_CONFLICT: "DROPSHIPPING_PROJECT_VERSION_CONFLICT",
7494
+ DROPSHIPPING_PROJECT_MEDIA_REQUIRED: "DROPSHIPPING_PROJECT_MEDIA_REQUIRED",
7495
+ DROPSHIPPING_PROJECT_DISABLED: "DROPSHIPPING_PROJECT_DISABLED",
7496
+ KALODATA_NOT_CONFIGURED: "KALODATA_NOT_CONFIGURED",
7497
+ META_AD_LIBRARY_NOT_CONFIGURED: "META_AD_LIBRARY_NOT_CONFIGURED",
7498
+ PRODUCT_RESEARCH_DISABLED: "PRODUCT_RESEARCH_DISABLED",
7294
7499
  PRODUCT_RESEARCH_IDEMPOTENCY_CONFLICT: "PRODUCT_RESEARCH_IDEMPOTENCY_CONFLICT",
7295
7500
  // Connection presence
7296
7501
  NO_SHOPIFY_SHOPS: "NO_SHOPIFY_SHOPS",
@@ -7301,6 +7506,7 @@ var McpErrorCode = {
7301
7506
  CAMPAIGN_MISSING_LANDING_URL: "CAMPAIGN_MISSING_LANDING_URL",
7302
7507
  CAMPAIGN_NO_CREATIVES: "CAMPAIGN_NO_CREATIVES",
7303
7508
  CAMPAIGN_MISSING_THUMBNAILS: "CAMPAIGN_MISSING_THUMBNAILS",
7509
+ CAMPAIGN_RECONCILIATION_REQUIRED: "CAMPAIGN_RECONCILIATION_REQUIRED",
7304
7510
  // Shopify
7305
7511
  SHOPIFY_NOT_CONNECTED: "SHOPIFY_NOT_CONNECTED",
7306
7512
  SHOPIFY_TOKEN_EXPIRED: "SHOPIFY_TOKEN_EXPIRED",
@@ -7405,6 +7611,7 @@ var CODE_MESSAGES = {
7405
7611
  CAMPAIGN_MISSING_LANDING_URL: "Missing landing URL for the campaign. Publish to Shopify first or pass `landingUrl` explicitly.",
7406
7612
  CAMPAIGN_NO_CREATIVES: "The task has no creatives (video/image) ready for the campaign. Wait for generation to finish.",
7407
7613
  CAMPAIGN_MISSING_THUMBNAILS: "Missing thumbnails for the videos. Verify that image/video generation has finished.",
7614
+ CAMPAIGN_RECONCILIATION_REQUIRED: "A campaign may already have been created. Do not retry creation; reopen the project and reconcile it or contact support.",
7408
7615
  // Connections
7409
7616
  NO_SHOPIFY_SHOPS: "No Shopify stores connected. Connect one at https://productmaker.app/integrations.",
7410
7617
  NO_META_AD_ACCOUNTS: "No Meta Ads accounts connected. Connect Facebook at https://productmaker.app/integrations.",
@@ -7413,6 +7620,14 @@ var CODE_MESSAGES = {
7413
7620
  TASK_NOT_READY: "The task is not ready for this action yet. Wait for it to finish and retry.",
7414
7621
  CREATIVE_VARIANT_FAILED: "The previous creative variant attempt failed. Ask for confirmation before starting a new paid attempt with a new clientRequestId.",
7415
7622
  CREATIVE_VARIANT_DISPATCH_FAILED: "The creative variant could not be queued. Retry as a new paid attempt with a new clientRequestId.",
7623
+ DROPSHIPPING_PROJECT_NOT_FOUND: "Project not found or it does not belong to this user.",
7624
+ DROPSHIPPING_PROJECT_CANDIDATE_NOT_READY: "That research candidate is no longer available. Return to the current research results and choose a fresh candidate.",
7625
+ DROPSHIPPING_PROJECT_VERSION_CONFLICT: "This project changed in another tab. Read it again, review the newer values, and retry with the fresh version.",
7626
+ DROPSHIPPING_PROJECT_MEDIA_REQUIRED: "This project needs a product photo before creating content. Upload one from the guided ingest step.",
7627
+ DROPSHIPPING_PROJECT_DISABLED: "Guided dropshipping projects are temporarily unavailable. Try again later.",
7628
+ KALODATA_NOT_CONFIGURED: "Product research is temporarily unavailable because the trend-data provider is not configured.",
7629
+ META_AD_LIBRARY_NOT_CONFIGURED: "Product research is temporarily unavailable because Meta Ad Library is not configured.",
7630
+ PRODUCT_RESEARCH_DISABLED: "Product research is temporarily unavailable. Try again later.",
7416
7631
  PRODUCT_RESEARCH_IDEMPOTENCY_CONFLICT: "A product research run with the same country and category is already running. Check it with `list_product_research_runs` instead of starting a new paid run.",
7417
7632
  // API key
7418
7633
  INVALID_API_KEY: "Invalid API key. Check your PM_API_KEY in the MCP client configuration.",
@@ -7449,6 +7664,11 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
7449
7664
  McpErrorCode.CAMPAIGN_NOT_FOUND,
7450
7665
  McpErrorCode.CAMPAIGN_TASK_NOT_FOUND,
7451
7666
  McpErrorCode.TASK_NOT_FOUND,
7667
+ McpErrorCode.DROPSHIPPING_PROJECT_NOT_FOUND,
7668
+ McpErrorCode.DROPSHIPPING_PROJECT_DISABLED,
7669
+ McpErrorCode.DROPSHIPPING_PROJECT_CANDIDATE_NOT_READY,
7670
+ McpErrorCode.DROPSHIPPING_PROJECT_VERSION_CONFLICT,
7671
+ McpErrorCode.DROPSHIPPING_PROJECT_MEDIA_REQUIRED,
7452
7672
  McpErrorCode.META_TASK_NOT_FOUND,
7453
7673
  McpErrorCode.META_INVALID_AD_ACCOUNT,
7454
7674
  McpErrorCode.META_PIXEL_NOT_FOUND,
@@ -7457,6 +7677,7 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
7457
7677
  McpErrorCode.TIKTOK_ADVERTISER_REQUIRED,
7458
7678
  McpErrorCode.TIKTOK_PIXEL_NOT_FOUND,
7459
7679
  McpErrorCode.CAMPAIGN_MISSING_LANDING_URL,
7680
+ McpErrorCode.CAMPAIGN_RECONCILIATION_REQUIRED,
7460
7681
  McpErrorCode.META_NO_LANDING_URL,
7461
7682
  McpErrorCode.META_DAILY_BUDGET_TOO_HIGH,
7462
7683
  McpErrorCode.META_CAMPAIGN_CONFIG_INVALID,
@@ -7494,6 +7715,14 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
7494
7715
  var PASSTHROUGH_CODES = /* @__PURE__ */ new Set([
7495
7716
  McpErrorCode.INSUFFICIENT_CREDITS,
7496
7717
  McpErrorCode.TASK_QUOTA_EXCEEDED,
7718
+ McpErrorCode.DROPSHIPPING_PROJECT_NOT_FOUND,
7719
+ McpErrorCode.DROPSHIPPING_PROJECT_DISABLED,
7720
+ McpErrorCode.DROPSHIPPING_PROJECT_CANDIDATE_NOT_READY,
7721
+ McpErrorCode.DROPSHIPPING_PROJECT_VERSION_CONFLICT,
7722
+ McpErrorCode.DROPSHIPPING_PROJECT_MEDIA_REQUIRED,
7723
+ McpErrorCode.KALODATA_NOT_CONFIGURED,
7724
+ McpErrorCode.META_AD_LIBRARY_NOT_CONFIGURED,
7725
+ McpErrorCode.PRODUCT_RESEARCH_DISABLED,
7497
7726
  McpErrorCode.CREATIVE_VARIANT_DISPATCH_FAILED,
7498
7727
  McpErrorCode.PRODUCT_RESEARCH_IDEMPOTENCY_CONFLICT,
7499
7728
  // Shopify
@@ -7548,6 +7777,7 @@ var PASSTHROUGH_CODES = /* @__PURE__ */ new Set([
7548
7777
  McpErrorCode.CAMPAIGN_MISSING_LANDING_URL,
7549
7778
  McpErrorCode.CAMPAIGN_NO_CREATIVES,
7550
7779
  McpErrorCode.CAMPAIGN_MISSING_THUMBNAILS,
7780
+ McpErrorCode.CAMPAIGN_RECONCILIATION_REQUIRED,
7551
7781
  // Connections
7552
7782
  McpErrorCode.NO_SHOPIFY_SHOPS,
7553
7783
  McpErrorCode.NO_META_AD_ACCOUNTS,
@@ -7858,66 +8088,67 @@ function renderOutputsMarkdown(outputs) {
7858
8088
  __name(renderOutputsMarkdown, "renderOutputsMarkdown");
7859
8089
 
7860
8090
  // src/tools/tasks.ts
7861
- var CreateProductTaskInput = z39.object({
7862
- images: ImagesInput,
7863
- productUrl: z39.string().url().optional().describe("Opcional. URL de la p\xE1gina del producto (Shopify, AliExpress, Amazon, etc.) para enriquecer el contexto. Si se omite, la IA infiere todo desde la imagen."),
8091
+ var CreateProductTaskInput = z40.object({
8092
+ images: ImagesInput.optional().describe("1\u20135 fotos del mismo producto. Omitir \xFAnicamente cuando projectId apunta a un proyecto propio con imagen guardada."),
8093
+ projectId: z40.string().uuid().optional().describe("Opcional. Proyecto guiado propio. Permite continuar sin volver a subir la imagen guardada; nunca evita cr\xE9ditos, cuotas ni confirmaciones."),
8094
+ productUrl: z40.string().url().optional().describe("Opcional. URL de la p\xE1gina del producto (Shopify, AliExpress, Amazon, etc.) para enriquecer el contexto. Si se omite, la IA infiere todo desde la imagen."),
7864
8095
  language: Lang.describe('C\xF3digo de idioma para el contenido generado. Ejemplos: "es", "en", "pt". REQUERIDO \u2014 si el usuario no lo especific\xF3, PREG\xDANTALE antes de invocar. NUNCA infieras desde el contexto del chat ni asumas "es" por default.'),
7865
8096
  country: Country.describe('C\xF3digo ISO del pa\xEDs objetivo. Ejemplos: "CO", "MX", "US", "AR". Define moneda, m\xE9todos de pago locales y tono. REQUERIDO \u2014 si el usuario no lo especific\xF3, PREG\xDANTALE antes de invocar. NUNCA infieras desde el contexto del chat.'),
7866
- price: z39.string().min(1).max(120).optional().describe('Precio que se mostrar\xE1 en la landing y video. Formato libre \u2014 ej. "$89.900", "USD 29.99", "MXN 599". Opcional pero MUY IMPACTANTE: si el usuario no lo mencion\xF3, PREG\xDANTALE antes de invocar. Si te dice "t\xFA elige", om\xEDtelo y el backend sugiere uno.'),
7867
- compareAtPrice: z39.string().min(1).max(120).optional().describe('Opcional. Precio anterior para comunicar un descuento real en landing e im\xE1genes. Ej. "$129.000" cuando price="$89.900".'),
7868
- offer: z39.string().min(1).max(240).optional().describe('Oferta o promoci\xF3n a destacar. Ej. "2x1", "Env\xEDo gratis hoy", "50% OFF". Opcional pero MUY IMPACTANTE: si el usuario no lo mencion\xF3, PREG\xDANTALE antes de invocar (es v\xE1lido que diga "sin oferta"). Si te dice "t\xFA elige", om\xEDtelo.'),
7869
- paymentMethods: z39.string().min(1).max(240).optional().describe('Opcional. M\xE9todos de pago que puede mostrar la landing. Ej. "Tarjeta, PSE, Nequi, Contraentrega". Si se omite, la landing no muestra m\xE9todos de pago.'),
7870
- productContext: z39.string().max(3e3).optional().describe("Opcional. Informacion adicional del producto que la IA debe usar como contexto prioritario: uso real, comprador, restricciones, diferenciadores o direccion creativa. No reemplaza la investigacion de competencia; si hay competencia, guia el primer angulo y los outputs no deben contradecirlo."),
7871
- productLabel: z39.string().trim().max(80).optional().describe('Opcional. Qu\xE9 es el producto, en pocas palabras (ej. "quita pelusas para ropa"). Es AUTORITATIVO: si lo env\xEDas, la IA usa esta identidad y no la reemplaza por otra categor\xEDa. Si lo omites, la IA detecta el producto desde la imagen.'),
8097
+ price: z40.string().min(1).max(120).optional().describe('Precio que se mostrar\xE1 en la landing y video. Formato libre \u2014 ej. "$89.900", "USD 29.99", "MXN 599". Opcional pero MUY IMPACTANTE: si el usuario no lo mencion\xF3, PREG\xDANTALE antes de invocar. Si te dice "t\xFA elige", om\xEDtelo y el backend sugiere uno.'),
8098
+ compareAtPrice: z40.string().min(1).max(120).optional().describe('Opcional. Precio anterior para comunicar un descuento real en landing e im\xE1genes. Ej. "$129.000" cuando price="$89.900".'),
8099
+ offer: z40.string().min(1).max(240).optional().describe('Oferta o promoci\xF3n a destacar. Ej. "2x1", "Env\xEDo gratis hoy", "50% OFF". Opcional pero MUY IMPACTANTE: si el usuario no lo mencion\xF3, PREG\xDANTALE antes de invocar (es v\xE1lido que diga "sin oferta"). Si te dice "t\xFA elige", om\xEDtelo.'),
8100
+ paymentMethods: z40.string().min(1).max(240).optional().describe('Opcional. M\xE9todos de pago que puede mostrar la landing. Ej. "Tarjeta, PSE, Nequi, Contraentrega". Si se omite, la landing no muestra m\xE9todos de pago.'),
8101
+ productContext: z40.string().max(3e3).optional().describe("Opcional. Informacion adicional del producto que la IA debe usar como contexto prioritario: uso real, comprador, restricciones, diferenciadores o direccion creativa. No reemplaza la investigacion de competencia; si hay competencia, guia el primer angulo y los outputs no deben contradecirlo."),
8102
+ productLabel: z40.string().trim().max(80).optional().describe('Opcional. Qu\xE9 es el producto, en pocas palabras (ej. "quita pelusas para ropa"). Es AUTORITATIVO: si lo env\xEDas, la IA usa esta identidad y no la reemplaza por otra categor\xEDa. Si lo omites, la IA detecta el producto desde la imagen.'),
7872
8103
  videoPreset: VideoPresetEnum.default("auto").describe('Opcional. Tipo/narrativa del video \u2014 \xFAnico campo de "estilo" que el usuario controla. Default "auto" (la IA escoge tras analizar el producto). Los presets funcionan a 15s y 30s. Opciones: "auto", "ugc_dynamic" (review con experiencia y prueba), "ugc_how_to" (tutorial con pasos y resultado visible), "ugc_unboxing_chaos" (apertura y primera prueba), "product_showcase" (producto ejecutando una funci\xF3n real), "wearable_showcase" (prenda o accesorio modelado), "cinematic" (TV spot con receta cinematogr\xE1fica), "wild_card" (una regla visual experimental original), "pixar_problem" (problema animado 3D), "pixar_sassy" (producto que habla). Si el usuario pide algo fuera de esta lista, p\xEDdele una opci\xF3n v\xE1lida.'),
7873
- durationSeconds: z39.union([
7874
- z39.literal(15),
7875
- z39.literal(30)
8104
+ durationSeconds: z40.union([
8105
+ z40.literal(15),
8106
+ z40.literal(30)
7876
8107
  ]).default(15).describe("Opcional. Duraci\xF3n objetivo del video: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
7877
- pauseForAngleReview: z39.boolean().optional().describe("Opcional. Si true, pausa despu\xE9s de generar el \xE1ngulo para que el usuario lo confirme o edite antes del render. \xDAsalo si quiere decidir 15/30s cuando aparezca un winner. Default false."),
8108
+ pauseForAngleReview: z40.boolean().optional().describe("Opcional. Si true, pausa despu\xE9s de generar el \xE1ngulo para que el usuario lo confirme o edite antes del render. \xDAsalo si quiere decidir 15/30s cuando aparezca un winner. Default false."),
7878
8109
  videoTier: VideoTierEnum.optional().describe('Opcional. Estilo del video. "basic" = "Realista" para 15s; al pedir durationSeconds=30 el sistema selecciona internamente Multiescena. No se expone el tier interno directamente.'),
7879
8110
  videoResolution: VideoResolutionEnum.optional().describe('Opcional. Resoluci\xF3n del render. En la web se pregunta "\xBFResoluci\xF3n del video?". "480p" = excelente calidad y m\xE1s econ\xF3mico (RECOMENDADO, default si se omite); "720p" = m\xE1xima nitidez (~el doble de precio). Si el usuario no menciona resoluci\xF3n, OMITE este campo (el backend usa 480p).'),
7880
- multiAngleVideos: z39.boolean().optional().describe("Opcional. Si true, genera m\xFAltiples \xE1ngulos de venta en paralelo (3-5 videos). Si false/omitido, genera 1 video del \xE1ngulo principal. Default false."),
8111
+ multiAngleVideos: z40.boolean().optional().describe("Opcional. Si true, genera m\xFAltiples \xE1ngulos de venta en paralelo (3-5 videos). Si false/omitido, genera 1 video del \xE1ngulo principal. Default false."),
7881
8112
  angleNarratives: PublicNarrativeStyleEnum.array().max(3).optional().describe('Opcional. Narrativa POR \xC1NGULO (posicional). Valores actuales: "review", "ugc_how_to", "ugc_unboxing_chaos", "product_showcase", "wearable_showcase", "cinematic", "wild_card", "animated_problem_pixar", "sassy_product_pixar". angleNarratives[i] fuerza la narrativa del \xE1ngulo i; las posiciones restantes las elige la IA. Requiere multiAngleVideos:true para varios \xE1ngulos.'),
7882
- outputs: z39.array(z39.enum([
8113
+ outputs: z40.array(z40.enum([
7883
8114
  "landing",
7884
8115
  "video",
7885
8116
  "image"
7886
8117
  ])).min(2).max(3).optional().describe('Opcional. Qu\xE9 outputs generar. Default: todos ["landing","video","image"]. Para 2 outputs: ["landing","image"] (sin video), ["video","image"] (sin landing), ["landing","video"] (sin imagen). Para 1 solo output, usa generate_landing / generate_video_creative / generate_image_creatives.'),
7887
- competitorScanEnabled: z39.boolean().optional().describe("Opcional. Si true (default), el sistema analiza ads de competencia en Meta Ad Library y enriquece los \xE1ngulos generados con hooks y painPoints validados por el mercado (\u2248$0.20-0.48 extra por task \u2014 var\xEDa con cu\xE1ntas transcripciones de video se capturen). Si false, el pipeline genera \xE1ngulos sin esa se\xF1al \u2014 usa esto SOLO si el usuario ya tiene su propia investigaci\xF3n de mercado o quiere ahorrar costos en productos muy nicho sin presencia en Meta. Default true."),
7888
- brandId: z39.string().uuid().optional().describe("Opcional. ID de una marca del usuario; si se env\xEDa, el sistema usa su voz, colores y persona para enriquecer los outputs generados."),
7889
- creativeDirection: z39.string().max(500).optional().describe('Opcional. Direcci\xF3n creativa en texto libre (colores, mood, estilo) que aplica a los outputs de IMAGEN y LANDING (no al video \u2014 el estilo/ambiente del video se dirige con scriptNotes). M\xC1XIMA PRIORIDAD: anula marca, colores y estilo por defecto. Ej. "amarillo y azul, minimalista". M\xE1x 500 caracteres.'),
8118
+ competitorScanEnabled: z40.boolean().optional().describe("Opcional. Si true (default), el sistema analiza ads de competencia en Meta Ad Library y enriquece los \xE1ngulos generados con hooks y painPoints validados por el mercado (\u2248$0.20-0.48 extra por task \u2014 var\xEDa con cu\xE1ntas transcripciones de video se capturen). Si false, el pipeline genera \xE1ngulos sin esa se\xF1al \u2014 usa esto SOLO si el usuario ya tiene su propia investigaci\xF3n de mercado o quiere ahorrar costos en productos muy nicho sin presencia en Meta. Default true."),
8119
+ brandId: z40.string().uuid().optional().describe("Opcional. ID de una marca del usuario; si se env\xEDa, el sistema usa su voz, colores y persona para enriquecer los outputs generados."),
8120
+ creativeDirection: z40.string().max(500).optional().describe('Opcional. Direcci\xF3n creativa en texto libre (colores, mood, estilo) que aplica a los outputs de IMAGEN y LANDING (no al video \u2014 el estilo/ambiente del video se dirige con scriptNotes). M\xC1XIMA PRIORIDAD: anula marca, colores y estilo por defecto. Ej. "amarillo y azul, minimalista". M\xE1x 500 caracteres.'),
7890
8121
  landingStrategy: LandingStrategyEnum.default("classic").describe('Opcional. Estructura de la landing cl\xE1sica cuando landing est\xE1 entre outputs: "classic" (estructura original probada y recomendada; default del primer rollout), "auto" (la IA decide; experimental), "pain_solution", "social_proof" (Landing Viral: UGC, WhatsApp, Instagram y evidencia social), "benefits_clarity", "offer_value" o "lifestyle_identity". Default "classic".'),
7891
8122
  landingImageFormat: LandingImageFormatEnum.default("9:16").describe('Opcional. Formato/relaci\xF3n de aspecto de las im\xE1genes de la landing cl\xE1sica cuando landing est\xE1 entre outputs: "9:16" (vertical, por defecto), "4:5" (vertical compacto para feed) o "1:1" (cuadrado). Default "9:16".')
7892
8123
  }).strict();
7893
- var GetTaskStatusInput = z39.object({
7894
- taskId: z39.string().min(1),
7895
- waitSeconds: z39.number().int().min(0).max(120).optional()
7896
- });
7897
- var ListTasksInput = z39.object({
7898
- limit: z39.number().int().min(1).max(50).optional(),
7899
- offset: z39.number().int().min(0).optional(),
7900
- search: z39.string().optional()
7901
- });
7902
- var EditTaskDraftInput = z39.object({
7903
- taskId: z39.string().min(1),
7904
- angleIndex: z39.number().int().min(0),
7905
- angle: z39.object({
7906
- angle: z39.string().min(1).max(480),
7907
- buyerPersona: z39.string().min(1).max(360),
7908
- actor: z39.string().min(1).max(360).optional(),
7909
- gender: z39.enum([
8124
+ var GetTaskStatusInput = z40.object({
8125
+ taskId: z40.string().min(1),
8126
+ waitSeconds: z40.number().int().min(0).max(120).optional()
8127
+ });
8128
+ var ListTasksInput = z40.object({
8129
+ limit: z40.number().int().min(1).max(50).optional(),
8130
+ offset: z40.number().int().min(0).optional(),
8131
+ search: z40.string().optional()
8132
+ });
8133
+ var EditTaskDraftInput = z40.object({
8134
+ taskId: z40.string().min(1),
8135
+ angleIndex: z40.number().int().min(0),
8136
+ angle: z40.object({
8137
+ angle: z40.string().min(1).max(480),
8138
+ buyerPersona: z40.string().min(1).max(360),
8139
+ actor: z40.string().min(1).max(360).optional(),
8140
+ gender: z40.enum([
7910
8141
  "female",
7911
8142
  "male"
7912
8143
  ]).optional(),
7913
- scriptNotes: z39.string().max(2e3).optional()
8144
+ scriptNotes: z40.string().max(2e3).optional()
7914
8145
  })
7915
8146
  });
7916
- var PendingActionPayload = z39.discriminatedUnion("kind", [
7917
- z39.object({
7918
- kind: z39.literal("product_reference_confirmation"),
8147
+ var PendingActionPayload = z40.discriminatedUnion("kind", [
8148
+ z40.object({
8149
+ kind: z40.literal("product_reference_confirmation"),
7919
8150
  userChoice: ProductReferenceUserChoiceEnum,
7920
- confirmedProductNames: z39.array(z39.string().min(1).max(120)).min(2).max(8).optional(),
8151
+ confirmedProductNames: z40.array(z40.string().min(1).max(120)).min(2).max(8).optional(),
7921
8152
  // SCOPED multi-scene opt-in — the ONLY way STANDARD ("Multiescena") is
7922
8153
  // reachable via MCP. Valid ONLY together with userChoice='variants' (same
7923
8154
  // product in several colors): renders one shot per color and stitches them,
@@ -7928,62 +8159,62 @@ var PendingActionPayload = z39.discriminatedUnion("kind", [
7928
8159
  // safe because multi-scene IS the intended render for a color variant set,
7929
8160
  // not a blind quality upsell. Maps to `videoTier: 'standard'` on the
7930
8161
  // confirm call; the backend re-gates it (variants + variant_set only).
7931
- useMultiSceneShowcase: z39.boolean().optional().describe('Opcional (SOLO con userChoice="variants"). true = render Multiescena: una toma por color, mostrando todas las variantes, hasta 30s. Se factura POR ESCENA (m\xE1s caro que el render normal de una sola toma) \u2014 AV\xCDSALE del mayor costo al usuario antes de activarlo. Omitir = render est\xE1ndar de una sola toma. Se ignora si userChoice no es "variants".')
8162
+ useMultiSceneShowcase: z40.boolean().optional().describe('Opcional (SOLO con userChoice="variants"). true = render Multiescena: una toma por color, mostrando todas las variantes, hasta 30s. Se factura POR ESCENA (m\xE1s caro que el render normal de una sola toma) \u2014 AV\xCDSALE del mayor costo al usuario antes de activarlo. Omitir = render est\xE1ndar de una sola toma. Se ignora si userChoice no es "variants".')
7932
8163
  }),
7933
- z39.object({
7934
- kind: z39.literal("angle_review_approval"),
8164
+ z40.object({
8165
+ kind: z40.literal("angle_review_approval"),
7935
8166
  // All edits are optional — empty action body just approves the
7936
8167
  // backend-generated draft as-is. Field shapes mirror the angle-review
7937
8168
  // request contracts in @packages/types/wizard.ts; the handler routes to
7938
8169
  // the dedicated video/image/autopilot endpoint based on StatusResponse.kind.
7939
- customAngle: z39.string().min(1).max(480).optional(),
8170
+ customAngle: z40.string().min(1).max(480).optional(),
7940
8171
  // Keep the shared MCP action within the strictest downstream contract:
7941
8172
  // autopilot/image resume endpoints cap these fields at 1500 characters.
7942
- painPoints: z39.string().max(1500).optional(),
7943
- benefits: z39.string().max(1500).optional(),
7944
- features: z39.array(z39.string().min(1).max(300)).max(10).optional(),
7945
- gapDurationSeconds: z39.union([
7946
- z39.literal(15),
7947
- z39.literal(30)
8173
+ painPoints: z40.string().max(1500).optional(),
8174
+ benefits: z40.string().max(1500).optional(),
8175
+ features: z40.array(z40.string().min(1).max(300)).max(10).optional(),
8176
+ gapDurationSeconds: z40.union([
8177
+ z40.literal(15),
8178
+ z40.literal(30)
7948
8179
  ]).optional().describe("Opcional. Solo para aceptar expl\xEDcitamente la recomendaci\xF3n gapDurationChoice del status. 30 env\xEDa el override double_30s para el \xE1ngulo recomendado; 15 u omitido conserva la duraci\xF3n cotizada.")
7949
8180
  })
7950
8181
  ]);
7951
- var ResolvePendingActionInput = z39.object({
7952
- taskId: z39.string().min(1),
8182
+ var ResolvePendingActionInput = z40.object({
8183
+ taskId: z40.string().min(1),
7953
8184
  action: PendingActionPayload
7954
8185
  }).strict();
7955
8186
  function resolveVariantConfirmVideoTier(action) {
7956
8187
  return action.useMultiSceneShowcase && action.userChoice === "variants" ? "standard" : void 0;
7957
8188
  }
7958
8189
  __name(resolveVariantConfirmVideoTier, "resolveVariantConfirmVideoTier");
7959
- var StatusForResolveSchema = z39.object({
8190
+ var StatusForResolveSchema = z40.object({
7960
8191
  // Metadata used to select the backend resume contract. Older API responses
7961
8192
  // may omit these fields, so the resolver keeps the generic fallback.
7962
- kind: z39.enum([
8193
+ kind: z40.enum([
7963
8194
  "autopilot",
7964
8195
  "video",
7965
8196
  "image",
7966
8197
  "landing",
7967
8198
  "mix"
7968
8199
  ]).optional(),
7969
- selectedOutputs: z39.array(z39.enum([
8200
+ selectedOutputs: z40.array(z40.enum([
7970
8201
  "landing",
7971
8202
  "video",
7972
8203
  "image"
7973
8204
  ])).optional(),
7974
- videoAngleCount: z39.number().int().min(1).max(3).optional(),
7975
- ugcFormat: z39.enum([
8205
+ videoAngleCount: z40.number().int().min(1).max(3).optional(),
8206
+ ugcFormat: z40.enum([
7976
8207
  "single_8s",
7977
8208
  "single_15s",
7978
8209
  "double_30s"
7979
8210
  ]).optional(),
7980
- videoStyle: z39.enum([
8211
+ videoStyle: z40.enum([
7981
8212
  "ugc",
7982
8213
  "cartoon_3d",
7983
8214
  "product_only",
7984
8215
  "ai_dynamic"
7985
8216
  ]).optional(),
7986
- videoModel: z39.enum([
8217
+ videoModel: z40.enum([
7987
8218
  "veo",
7988
8219
  "wan",
7989
8220
  "bytedance",
@@ -7991,26 +8222,26 @@ var StatusForResolveSchema = z39.object({
7991
8222
  "grok",
7992
8223
  "kling"
7993
8224
  ]).optional(),
7994
- videoTier: z39.enum([
8225
+ videoTier: z40.enum([
7995
8226
  "legacy",
7996
8227
  "standard",
7997
8228
  "pro",
7998
8229
  "single_call"
7999
8230
  ]).optional(),
8000
- videoResolution: z39.enum([
8231
+ videoResolution: z40.enum([
8001
8232
  "480p",
8002
8233
  "720p"
8003
8234
  ]).optional(),
8004
8235
  narrativeStyle: NarrativeStyleEnum.optional(),
8005
- pendingUserAction: z39.object({
8006
- kind: z39.string(),
8007
- candidateGrouping: z39.string().optional(),
8008
- selectedImageUrls: z39.array(z39.string()).optional(),
8009
- proposedProductNames: z39.array(z39.string()).optional(),
8010
- customAngleDraft: z39.string().optional(),
8011
- painPointsDraft: z39.string().optional(),
8012
- benefitsDraft: z39.string().optional(),
8013
- featuresDraft: z39.array(z39.string()).optional(),
8236
+ pendingUserAction: z40.object({
8237
+ kind: z40.string(),
8238
+ candidateGrouping: z40.string().optional(),
8239
+ selectedImageUrls: z40.array(z40.string()).optional(),
8240
+ proposedProductNames: z40.array(z40.string()).optional(),
8241
+ customAngleDraft: z40.string().optional(),
8242
+ painPointsDraft: z40.string().optional(),
8243
+ benefitsDraft: z40.string().optional(),
8244
+ featuresDraft: z40.array(z40.string()).optional(),
8014
8245
  // Recommendation consumed only when the caller explicitly sends
8015
8246
  // gapDurationSeconds: 30 in the angle approval action.
8016
8247
  gapDurationChoice: GapDurationChoiceMirror.optional()
@@ -8051,11 +8282,14 @@ function resolveStatusTaskKind(status) {
8051
8282
  }
8052
8283
  __name(resolveStatusTaskKind, "resolveStatusTaskKind");
8053
8284
  function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http" } = {}) {
8054
- server.tool("create_product_task", 'Crea una tarea de producto desde una o varias fotos. Por defecto genera los 3 outputs (landing + video + image creatives) EN UN MISMO TASK. Devuelve un taskId.\n\nINPUTS REQUERIDOS: images (1-5 fotos del MISMO producto; la primera es la hero), language, country.\n\nANTES de invocar, PREG\xDANTALE al usuario por price, offer y paymentMethods si no los mencion\xF3 \u2014 son inputs de alto impacto en la landing y nunca debes asumirlos. Si hay descuento, pregunta tambi\xE9n compareAtPrice. NO defaultes country/language desde el contexto del chat: preg\xFAntale expl\xEDcitamente, incluso si el usuario est\xE1 en LATAM. Si te dice "t\xFA elige", omite esos campos.\n\nANTES de invocar, DILE CLARAMENTE al usuario qu\xE9 outputs vas a generar y que `create_product_task` los genera juntos en un mismo task. Ejemplo: "Voy a generar landing + video en un mismo task; esto consume cr\xE9ditos de ambos outputs". Si el usuario s\xF3lo quiere un output, usa generate_landing / generate_video_creative / generate_image_creatives en vez de este tool.\n\nSi el usuario tiene varias fotos del MISMO producto (frente, lateral, detalle, uso), incl\xFAyelas todas en images[] hasta 5. Si tiene fotos de productos DIFERENTES, son llamadas separadas (una tarea por producto).\n\nOTROS OPCIONALES (puedes invocar sin ellos y dejar que la IA elija defaults sensatos):\n- videoPreset (tipo/narrativa del video \u2014 \xFAnico campo de estilo)\n- multiAngleVideos (booleano)\n- angleNarratives (override per-angle de la narrativa, avanzado)\n- outputs (subconjunto de {landing,video,image}); omitido = ["landing","video","image"] en un mismo task\n- productUrl\n- creativeDirection (direcci\xF3n creativa para im\xE1genes y landing; el estilo del video se dirige con scriptNotes)\n\nGUION: para revisar/editar el guion del video antes de renderizar, usa `generate_video_creative` con `pauseForVideoScriptReview: true` en vez de este tool. Despu\xE9s de la pausa: get_video_script para leer, approve_video_script para aprobar con ediciones.\n\nUsa SOLO valores declarados en los enums. Si el usuario pide algo no soportado (ej. "estilo anime", "modelo X"), p\xEDdele una opci\xF3n v\xE1lida del enum correspondiente.', CreateProductTaskInput.shape, async (input) => {
8285
+ server.tool("create_product_task", "Crea una tarea de producto desde fotos o desde un proyecto guiado propio. Por defecto genera los 3 outputs (landing + video + image creatives) EN UN MISMO TASK. Devuelve un taskId.\n\nINPUTS REQUERIDOS: images (1-5 fotos del MISMO producto; la primera es la hero) O projectId si el proyecto ya tiene una imagen guardada; adem\xE1s language y country.\n\nANTES de invocar, PREG\xDANTALE al usuario por price, offer y paymentMethods si no los mencion\xF3 \u2014 son inputs de alto impacto en la landing y nunca debes asumirlos. Si hay descuento, pregunta tambi\xE9n compareAtPrice. NO defaultes country/language desde el contexto del chat: preg\xFAntale expl\xEDcitamente, incluso si est\xE1 en LATAM.\n\nSi env\xEDas projectId, debe ser el proyecto propio que el usuario acaba de costear; el backend resolver\xE1 la imagen privada y conservar\xE1 todos los controles de cr\xE9ditos/confirmaci\xF3n. Si el usuario adjunta images junto con projectId, esas fotos reemplazan la referencia solo para este task.\n\nANTES de invocar, DILE CLARAMENTE qu\xE9 outputs vas a generar y que `create_product_task` los genera juntos en un mismo task.\n\nUsa SOLO valores declarados en los enums.", CreateProductTaskInput.shape, async (input) => {
8055
8286
  try {
8056
- const imgs = await resolveImages(input.images, {
8287
+ if (!input.images && !input.projectId) {
8288
+ throw new Error("images es requerido salvo que env\xEDes projectId.");
8289
+ }
8290
+ const imgs = input.images ? await resolveImages(input.images, {
8057
8291
  transport
8058
- });
8292
+ }) : [];
8059
8293
  const parts = imgs.map((img, i) => ({
8060
8294
  ...img,
8061
8295
  filename: `image-${i}.png`
@@ -8066,6 +8300,7 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
8066
8300
  const fields = flatten({
8067
8301
  "options.languageCode": input.language,
8068
8302
  "options.country": input.country,
8303
+ "options.dropshippingProjectId": input.projectId,
8069
8304
  "options.price": input.price,
8070
8305
  "options.compareAtPrice": input.compareAtPrice,
8071
8306
  "options.offer": input.offer,
@@ -8332,54 +8567,54 @@ __name(registerTaskTools, "registerTaskTools");
8332
8567
 
8333
8568
  // src/tools/creatives.ts
8334
8569
  import { randomUUID } from "crypto";
8335
- import { z as z40 } from "zod";
8570
+ import { z as z41 } from "zod";
8336
8571
  var CreativeRefs = {
8337
8572
  images: ImagesInput,
8338
8573
  language: Lang.describe('Idioma del contenido. Ej. "es", "en", "pt". REQUERIDO \u2014 si el usuario no lo dijo, PREG\xDANTALE antes de invocar. NUNCA infieras desde el contexto del chat ni asumas un default.'),
8339
8574
  country: Country.describe('Pa\xEDs objetivo ISO. Ej. "CO", "MX", "US", "AR". Define moneda, m\xE9todos de pago y tono. REQUERIDO \u2014 si el usuario no lo dijo, PREG\xDANTALE antes de invocar. NUNCA infieras desde el contexto del chat ni asumas un default.'),
8340
- price: z40.string().min(1).max(120).optional().describe('Precio en formato libre \u2014 ej. "$89.900", "USD 29.99". Opcional pero MUY IMPACTANTE para conversi\xF3n: si el usuario no lo mencion\xF3, PREG\xDANTALE antes de invocar. Si el usuario te dice "t\xFA elige", omite el campo y la IA del backend sugiere uno.'),
8341
- offer: z40.string().min(1).max(240).optional().describe('Oferta a destacar \u2014 ej. "2x1", "Env\xEDo gratis", "50% OFF". Opcional pero MUY IMPACTANTE: si el usuario no lo mencion\xF3, PREG\xDANTALE antes de invocar (es v\xE1lido que diga "sin oferta"). Si el usuario te dice "t\xFA elige", omite el campo.'),
8342
- compareAtPrice: z40.string().min(1).max(120).optional().describe('Opcional. Precio anterior o de referencia para mostrar descuento. Ej. "$129.000" cuando price="$89.900".'),
8343
- paymentMethods: z40.string().min(1).max(240).optional().describe('Opcional. M\xE9todos de pago a mostrar. Ej. "Tarjeta, PSE, Nequi, Contraentrega". Si se omite, la landing no muestra m\xE9todos de pago.'),
8344
- productContext: z40.string().max(3e3).optional().describe("Opcional. Informacion adicional del producto que la IA debe usar como contexto prioritario: uso real, comprador, restricciones, diferenciadores o direccion creativa. No reemplaza la investigacion de competencia; si hay competencia, guia el primer angulo y los outputs no deben contradecirlo."),
8345
- productLabel: z40.string().trim().max(80).optional().describe('Opcional. Nombre o tipo del producto en pocas palabras (ej. "collar de oro", "quita pelusas para ropa"). Es AUTORITATIVO: la IA usa esta identidad y no la reemplaza por otra categor\xEDa. Si lo omites, la IA lo detecta desde la imagen. NO salta la extracci\xF3n.'),
8346
- buyerPersona: z40.string().optional().describe('Opcional. Descripci\xF3n del comprador objetivo. Ej. "Mujeres 35-50, profesionales urbanas". Si se omite, la IA infiere desde el \xE1ngulo y producto.'),
8347
- isCombo: z40.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
8348
- competitorScanEnabled: z40.boolean().optional().describe("Opcional. Si true (default), el sistema analiza ads de competencia en Meta Ad Library y enriquece los \xE1ngulos generados con hooks y painPoints validados por el mercado (\u2248$0.20-0.48 extra por task \u2014 var\xEDa con cu\xE1ntas transcripciones de video se capturen). Si false, el pipeline genera \xE1ngulos sin esa se\xF1al \u2014 usa esto SOLO si el usuario ya tiene su propia investigaci\xF3n de mercado o quiere ahorrar costos en productos muy nicho sin presencia en Meta. Default true."),
8349
- brandId: z40.string().uuid().optional().describe("Opcional. ID de una marca del usuario; si se env\xEDa, el sistema usa su voz, colores y persona para enriquecer el creativo."),
8350
- creativeDirection: z40.string().max(500).optional().describe('Opcional. Direcci\xF3n creativa en texto libre (colores, mood, estilo). M\xC1XIMA PRIORIDAD: anula marca, colores y estilo por defecto. Aplica SOLO a im\xE1genes y landing. Ej. im\xE1genes: "amarillo, azul y rojo, minimalista"; landing: "elegante, paleta fr\xEDa". Para video usa scriptNotes (ambiente/escenario). M\xE1x 500 caracteres.')
8575
+ price: z41.string().min(1).max(120).optional().describe('Precio en formato libre \u2014 ej. "$89.900", "USD 29.99". Opcional pero MUY IMPACTANTE para conversi\xF3n: si el usuario no lo mencion\xF3, PREG\xDANTALE antes de invocar. Si el usuario te dice "t\xFA elige", omite el campo y la IA del backend sugiere uno.'),
8576
+ offer: z41.string().min(1).max(240).optional().describe('Oferta a destacar \u2014 ej. "2x1", "Env\xEDo gratis", "50% OFF". Opcional pero MUY IMPACTANTE: si el usuario no lo mencion\xF3, PREG\xDANTALE antes de invocar (es v\xE1lido que diga "sin oferta"). Si el usuario te dice "t\xFA elige", omite el campo.'),
8577
+ compareAtPrice: z41.string().min(1).max(120).optional().describe('Opcional. Precio anterior o de referencia para mostrar descuento. Ej. "$129.000" cuando price="$89.900".'),
8578
+ paymentMethods: z41.string().min(1).max(240).optional().describe('Opcional. M\xE9todos de pago a mostrar. Ej. "Tarjeta, PSE, Nequi, Contraentrega". Si se omite, la landing no muestra m\xE9todos de pago.'),
8579
+ productContext: z41.string().max(3e3).optional().describe("Opcional. Informacion adicional del producto que la IA debe usar como contexto prioritario: uso real, comprador, restricciones, diferenciadores o direccion creativa. No reemplaza la investigacion de competencia; si hay competencia, guia el primer angulo y los outputs no deben contradecirlo."),
8580
+ productLabel: z41.string().trim().max(80).optional().describe('Opcional. Nombre o tipo del producto en pocas palabras (ej. "collar de oro", "quita pelusas para ropa"). Es AUTORITATIVO: la IA usa esta identidad y no la reemplaza por otra categor\xEDa. Si lo omites, la IA lo detecta desde la imagen. NO salta la extracci\xF3n.'),
8581
+ buyerPersona: z41.string().optional().describe('Opcional. Descripci\xF3n del comprador objetivo. Ej. "Mujeres 35-50, profesionales urbanas". Si se omite, la IA infiere desde el \xE1ngulo y producto.'),
8582
+ isCombo: z41.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
8583
+ competitorScanEnabled: z41.boolean().optional().describe("Opcional. Si true (default), el sistema analiza ads de competencia en Meta Ad Library y enriquece los \xE1ngulos generados con hooks y painPoints validados por el mercado (\u2248$0.20-0.48 extra por task \u2014 var\xEDa con cu\xE1ntas transcripciones de video se capturen). Si false, el pipeline genera \xE1ngulos sin esa se\xF1al \u2014 usa esto SOLO si el usuario ya tiene su propia investigaci\xF3n de mercado o quiere ahorrar costos en productos muy nicho sin presencia en Meta. Default true."),
8584
+ brandId: z41.string().uuid().optional().describe("Opcional. ID de una marca del usuario; si se env\xEDa, el sistema usa su voz, colores y persona para enriquecer el creativo."),
8585
+ creativeDirection: z41.string().max(500).optional().describe('Opcional. Direcci\xF3n creativa en texto libre (colores, mood, estilo). M\xC1XIMA PRIORIDAD: anula marca, colores y estilo por defecto. Aplica SOLO a im\xE1genes y landing. Ej. im\xE1genes: "amarillo, azul y rojo, minimalista"; landing: "elegante, paleta fr\xEDa". Para video usa scriptNotes (ambiente/escenario). M\xE1x 500 caracteres.')
8351
8586
  };
8352
- var VideoInput = z40.object({
8587
+ var VideoInput = z41.object({
8353
8588
  images: CreativeRefs.images,
8354
- title: z40.string().min(1).describe('T\xEDtulo corto del producto. Aparece en la primera vista del video. Ej. "Crema Anti-Edad Premium".'),
8355
- primaryAngle: z40.string().min(1).describe('\xC1ngulo de venta principal (frase del beneficio). Ej. "Reduce arrugas en 14 d\xEDas" o "Para quienes odian las cremas grasosas".'),
8589
+ title: z41.string().min(1).describe('T\xEDtulo corto del producto. Aparece en la primera vista del video. Ej. "Crema Anti-Edad Premium".'),
8590
+ primaryAngle: z41.string().min(1).describe('\xC1ngulo de venta principal (frase del beneficio). Ej. "Reduce arrugas en 14 d\xEDas" o "Para quienes odian las cremas grasosas".'),
8356
8591
  language: CreativeRefs.language,
8357
8592
  country: CreativeRefs.country,
8358
- aspectRatio: z40.enum([
8593
+ aspectRatio: z41.enum([
8359
8594
  "9:16",
8360
8595
  "16:9",
8361
8596
  "Auto"
8362
8597
  ]).describe('Relaci\xF3n de aspecto. "9:16" = vertical (TikTok/Reels \u2014 default recomendado para ads). "16:9" = horizontal (YouTube). "Auto" = decide la IA.'),
8363
- durationSeconds: z40.union([
8364
- z40.literal(15),
8365
- z40.literal(30)
8598
+ durationSeconds: z41.union([
8599
+ z41.literal(15),
8600
+ z41.literal(30)
8366
8601
  ]).default(15).describe("Opcional. Duraci\xF3n objetivo: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
8367
8602
  videoPreset: VideoPresetEnum.default("auto").describe('Opcional. Tipo/narrativa del video \u2014 \xFAnico campo de "estilo" que el usuario controla. Default "auto" (la IA escoge tras analizar el producto). Los presets funcionan a 15s y 30s. Opciones: "auto", "ugc_dynamic" (review con experiencia y prueba), "ugc_how_to" (tutorial con pasos y resultado visible), "ugc_unboxing_chaos" (apertura y primera prueba), "product_showcase" (producto ejecutando una funci\xF3n real), "wearable_showcase" (prenda o accesorio modelado), "cinematic" (TV spot con receta cinematogr\xE1fica), "wild_card" (una regla visual experimental original), "pixar_problem" (problema animado 3D), "pixar_sassy" (producto que habla).'),
8368
8603
  videoTier: VideoTierEnum.optional().describe('Opcional. Estilo del video. "basic" = "Realista"; para durationSeconds=30 el sistema selecciona internamente Multiescena. No se expone el tier interno directamente.'),
8369
8604
  videoResolution: VideoResolutionEnum.optional().describe('Opcional. Resoluci\xF3n del render. En la web se pregunta "\xBFResoluci\xF3n del video?". "720p" = m\xE1xima nitidez (RECOMENDADO, default si se omite); "480p" = render m\xE1s econ\xF3mico. Si el usuario no menciona resoluci\xF3n, OMITE este campo (el backend usa 720p).'),
8370
- features: z40.array(z40.string()).optional().describe('Opcional. Lista de features/beneficios clave a mencionar. Ej. ["resistente al agua", "bater\xEDa 24h", "carga inal\xE1mbrica"].'),
8605
+ features: z41.array(z41.string()).optional().describe('Opcional. Lista de features/beneficios clave a mencionar. Ej. ["resistente al agua", "bater\xEDa 24h", "carga inal\xE1mbrica"].'),
8371
8606
  price: CreativeRefs.price,
8372
8607
  offer: CreativeRefs.offer,
8373
8608
  productContext: CreativeRefs.productContext,
8374
8609
  productLabel: CreativeRefs.productLabel,
8375
- cta: z40.string().optional().describe('Opcional. Call-to-action final del video. Ej. "Compra ahora", "Pide el tuyo hoy", "Link en bio". Si se omite, la IA escribe uno.'),
8376
- scriptNotes: z40.string().optional().describe('Opcional. Notas/restricciones para el guion (la IA las usa al redactar). Ej. "No mencionar la marca X", "Tono divertido, no cl\xEDnico", "Mencionar 2x1". NO es el guion literal \u2014 para editar el guion palabra-por-palabra, pasa `pauseForVideoScriptReview: true` y usa get_video_script / approve_video_script tras la pausa.'),
8377
- pauseForVideoScriptReview: z40.boolean().optional().describe("Opcional. Si true, la tarea pausa despu\xE9s de generar el guion y antes de renderizar. \xDAsalo cuando el usuario quiera editar el guion antes de gastar cr\xE9ditos en el video. Tras la pausa: get_video_script para leerlo, approve_video_script para aprobar (con o sin ediciones). Default false."),
8378
- pauseForAngleReview: z40.boolean().optional().describe("Opcional. Si true, pausa despu\xE9s de generar el \xE1ngulo para que el usuario lo confirme o edite antes del render. Default false."),
8610
+ cta: z41.string().optional().describe('Opcional. Call-to-action final del video. Ej. "Compra ahora", "Pide el tuyo hoy", "Link en bio". Si se omite, la IA escribe uno.'),
8611
+ scriptNotes: z41.string().optional().describe('Opcional. Notas/restricciones para el guion (la IA las usa al redactar). Ej. "No mencionar la marca X", "Tono divertido, no cl\xEDnico", "Mencionar 2x1". NO es el guion literal \u2014 para editar el guion palabra-por-palabra, pasa `pauseForVideoScriptReview: true` y usa get_video_script / approve_video_script tras la pausa.'),
8612
+ pauseForVideoScriptReview: z41.boolean().optional().describe("Opcional. Si true, la tarea pausa despu\xE9s de generar el guion y antes de renderizar. \xDAsalo cuando el usuario quiera editar el guion antes de gastar cr\xE9ditos en el video. Tras la pausa: get_video_script para leerlo, approve_video_script para aprobar (con o sin ediciones). Default false."),
8613
+ pauseForAngleReview: z41.boolean().optional().describe("Opcional. Si true, pausa despu\xE9s de generar el \xE1ngulo para que el usuario lo confirme o edite antes del render. Default false."),
8379
8614
  competitorScanEnabled: CreativeRefs.competitorScanEnabled,
8380
8615
  brandId: CreativeRefs.brandId
8381
8616
  }).strict();
8382
- var FreeVideoQualityEnum = z40.enum([
8617
+ var FreeVideoQualityEnum = z41.enum([
8383
8618
  "economical",
8384
8619
  "high_detail"
8385
8620
  ]);
@@ -8401,38 +8636,38 @@ var FREE_VIDEO_DURATIONS_BY_QUALITY = {
8401
8636
  15
8402
8637
  ]
8403
8638
  };
8404
- var FreeVideoInput = z40.object({
8639
+ var FreeVideoInput = z41.object({
8405
8640
  images: ImagesInput.optional().describe("Opcional. 1-5 fotos del producto. Om\xEDtelo para generar desde texto puro."),
8406
- rawPrompt: z40.string().trim().min(1).max(2e3).describe("Prompt o guion exacto del video. Si activas subt\xEDtulos, este mismo texto se usa como guion de subt\xEDtulos."),
8641
+ rawPrompt: z41.string().trim().min(1).max(2e3).describe("Prompt o guion exacto del video. Si activas subt\xEDtulos, este mismo texto se usa como guion de subt\xEDtulos."),
8407
8642
  quality: FreeVideoQualityEnum.describe('Calidad del video: "economical" = Econ\xF3mico o "high_detail" = Alto detalle.'),
8408
- durationSeconds: z40.number().int().min(1).max(15).describe("Duraci\xF3n exacta. Econ\xF3mico permite 6, 8, 10 o 15s; Alto detalle permite 4, 8, 12 o 15s."),
8643
+ durationSeconds: z41.number().int().min(1).max(15).describe("Duraci\xF3n exacta. Econ\xF3mico permite 6, 8, 10 o 15s; Alto detalle permite 4, 8, 12 o 15s."),
8409
8644
  videoResolution: VideoResolutionEnum.describe("Resoluci\xF3n del video: 480p o 720p."),
8410
- generateAiMusic: z40.boolean().default(false).describe("Opcional. Agrega m\xFAsica instrumental IA debajo del audio nativo. Default false."),
8411
- addSubtitles: z40.boolean().default(false).describe("Opcional. Quema subt\xEDtulos usando el rawPrompt exacto. Default false."),
8412
- idempotencyKey: z40.string().uuid().optional().describe("Opcional. UUID para reintentar exactamente esta misma solicitud sin crear ni cobrar otro video. Reutiliza el mismo valor si el cliente no recibi\xF3 la respuesta.")
8645
+ generateAiMusic: z41.boolean().default(false).describe("Opcional. Agrega m\xFAsica instrumental IA debajo del audio nativo. Default false."),
8646
+ addSubtitles: z41.boolean().default(false).describe("Opcional. Quema subt\xEDtulos usando el rawPrompt exacto. Default false."),
8647
+ idempotencyKey: z41.string().uuid().optional().describe("Opcional. UUID para reintentar exactamente esta misma solicitud sin crear ni cobrar otro video. Reutiliza el mismo valor si el cliente no recibi\xF3 la respuesta.")
8413
8648
  }).strict();
8414
8649
  var imageStyleList = ImageCreativeStyleEnum.options.join(" | ");
8415
8650
  var archetypeList = ImageCreativeArchetypeEnum.options.join(" | ");
8416
8651
  var visualTreatmentList = ImageCreativeVisualTreatmentEnum.options.join(" | ");
8417
8652
  var aspectRatioList = ImageCreativeAspectRatioEnum.options.join(" | ");
8418
- var ImageInput = z40.object({
8653
+ var ImageInput = z41.object({
8419
8654
  images: CreativeRefs.images,
8420
- productTitle: z40.string().min(1).describe('Nombre del producto. Ej. "Crema Anti-Edad Premium".'),
8421
- primaryAngle: z40.string().min(1).describe('\xC1ngulo de venta principal. Ej. "Reduce arrugas en 14 d\xEDas".'),
8655
+ productTitle: z41.string().min(1).describe('Nombre del producto. Ej. "Crema Anti-Edad Premium".'),
8656
+ primaryAngle: z41.string().min(1).describe('\xC1ngulo de venta principal. Ej. "Reduce arrugas en 14 d\xEDas".'),
8422
8657
  buyerPersona: CreativeRefs.buyerPersona,
8423
8658
  language: CreativeRefs.language,
8424
8659
  country: CreativeRefs.country,
8425
- nVariants: z40.number().int().min(1).max(6).optional().describe("Opcional. N\xFAmero de variantes a generar (1-6). Default 3."),
8426
- imageModel: z40.string().optional().describe('Opcional. Modelo de imagen. Ej. "gpt-image-2", "nano-banana-2". Om\xEDtelo para usar el default optimizado por costo/calidad.'),
8660
+ nVariants: z41.number().int().min(1).max(6).optional().describe("Opcional. N\xFAmero de variantes a generar (1-6). Default 3."),
8661
+ imageModel: z41.string().optional().describe('Opcional. Modelo de imagen. Ej. "gpt-image-2", "nano-banana-2". Om\xEDtelo para usar el default optimizado por costo/calidad.'),
8427
8662
  imageStyle: ImageCreativeStyleEnum.optional().describe(`Opcional. Estilo del shot del producto. Valores: ${imageStyleList}. Si se omite, la IA escoge seg\xFAn el archetype.`),
8428
8663
  creativeArchetype: ImageCreativeArchetypeEnum.optional().describe(`Opcional. Arquetipo CRO de la composici\xF3n. Valores: ${archetypeList}. Si se omite, la IA escoge seg\xFAn \xE1ngulo+oferta (ej. "offer_value_prop" cuando hay descuento, "social_proof_trust" cuando el \xE1ngulo es testimonial).`),
8429
8664
  visualTreatment: ImageCreativeVisualTreatmentEnum.optional().describe(`Opcional. Tratamiento gr\xE1fico. Valores: ${visualTreatmentList} ("performance_product_ad" = dealer-style alto CTR para Meta/TikTok ads; "premium_dtc_carousel" = fotograf\xEDa limpia estilo Allbirds/Glossier). Si se omite, la IA escoge.`),
8430
- styleMix: z40.boolean().optional().describe("Opcional. Si true, las N variantes mezclan distintos imageStyle para A/B test. Si false/omitido, todas comparten estilo. Default false."),
8665
+ styleMix: z41.boolean().optional().describe("Opcional. Si true, las N variantes mezclan distintos imageStyle para A/B test. Si false/omitido, todas comparten estilo. Default false."),
8431
8666
  aspectRatio: ImageCreativeAspectRatioEnum.optional().describe(`Opcional. Relaci\xF3n de aspecto. Valores: ${aspectRatioList}. Default "4:5" (vertical Instagram feed).`),
8432
- generateAdCopy: z40.boolean().optional().describe("Opcional. Si true, tambi\xE9n genera headline/body/CTA para el ad. Default false."),
8433
- pauseForAngleReview: z40.boolean().optional().describe("Opcional. Si true, pausa despu\xE9s de generar el \xE1ngulo y antes de las im\xE1genes para que el usuario edite v\xEDa edit_task_draft. Default false."),
8434
- features: z40.array(z40.string()).optional().describe('Opcional. Features/beneficios a destacar visualmente. Ej. ["resistente al agua", "ultra liviano"].'),
8435
- brandColors: z40.array(z40.string()).optional().describe('Opcional. Paleta de marca en hex. Ej. ["#FF6B35", "#004E89"]. Si se omite, la IA elige seg\xFAn el producto.'),
8667
+ generateAdCopy: z41.boolean().optional().describe("Opcional. Si true, tambi\xE9n genera headline/body/CTA para el ad. Default false."),
8668
+ pauseForAngleReview: z41.boolean().optional().describe("Opcional. Si true, pausa despu\xE9s de generar el \xE1ngulo y antes de las im\xE1genes para que el usuario edite v\xEDa edit_task_draft. Default false."),
8669
+ features: z41.array(z41.string()).optional().describe('Opcional. Features/beneficios a destacar visualmente. Ej. ["resistente al agua", "ultra liviano"].'),
8670
+ brandColors: z41.array(z41.string()).optional().describe('Opcional. Paleta de marca en hex. Ej. ["#FF6B35", "#004E89"]. Si se omite, la IA elige seg\xFAn el producto.'),
8436
8671
  price: CreativeRefs.price,
8437
8672
  compareAtPrice: CreativeRefs.compareAtPrice,
8438
8673
  offer: CreativeRefs.offer,
@@ -8443,11 +8678,11 @@ var ImageInput = z40.object({
8443
8678
  brandId: CreativeRefs.brandId,
8444
8679
  creativeDirection: CreativeRefs.creativeDirection
8445
8680
  });
8446
- var LandingInput = z40.object({
8681
+ var LandingInput = z41.object({
8447
8682
  images: CreativeRefs.images,
8448
8683
  language: CreativeRefs.language,
8449
8684
  country: CreativeRefs.country,
8450
- salesAngle: z40.string().optional().describe('Opcional. \xC1ngulo de venta principal. Ej. "Reduce arrugas en 14 d\xEDas". Si se omite, la IA lo deduce de la imagen.'),
8685
+ salesAngle: z41.string().optional().describe('Opcional. \xC1ngulo de venta principal. Ej. "Reduce arrugas en 14 d\xEDas". Si se omite, la IA lo deduce de la imagen.'),
8451
8686
  buyerPersona: CreativeRefs.buyerPersona,
8452
8687
  price: CreativeRefs.price,
8453
8688
  compareAtPrice: CreativeRefs.compareAtPrice,
@@ -8545,8 +8780,8 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
8545
8780
  __name(registerCreativeTools, "registerCreativeTools");
8546
8781
 
8547
8782
  // src/tools/creative-variants.ts
8548
- import { z as z41 } from "zod";
8549
- var LandingSectionKeyEnum = z41.enum([
8783
+ import { z as z42 } from "zod";
8784
+ var LandingSectionKeyEnum = z42.enum([
8550
8785
  "problem_hook",
8551
8786
  "problem_cost",
8552
8787
  "solution_mechanism",
@@ -8586,24 +8821,24 @@ var LandingSectionKeyEnum = z41.enum([
8586
8821
  "lifestyle_fit",
8587
8822
  "join_lifestyle"
8588
8823
  ]);
8589
- var CreativeVariantKindEnum = z41.enum([
8824
+ var CreativeVariantKindEnum = z42.enum([
8590
8825
  "video",
8591
8826
  "image",
8592
8827
  "landing"
8593
8828
  ]);
8594
- var EstimateCreativeVariantInputBase = z41.object({
8595
- sourceTaskId: z41.string().uuid().describe("ID de la tarea original completada."),
8829
+ var EstimateCreativeVariantInputBase = z42.object({
8830
+ sourceTaskId: z42.string().uuid().describe("ID de la tarea original completada."),
8596
8831
  kind: CreativeVariantKindEnum,
8597
8832
  videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Solo cambia el preset p\xFAblico; el servidor conserva el modelo/tier del task origen."),
8598
8833
  videoResolution: VideoResolutionEnum.optional().describe("Opcional para video. Resoluci\xF3n del nuevo render."),
8599
- nVariants: z41.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
8834
+ nVariants: z42.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
8600
8835
  sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si ya conoces la secci\xF3n, evita cotizar la clasificaci\xF3n con IA."),
8601
- instructions: z41.string().trim().min(1).max(2e3).optional().describe("Opcional para landing. Si se env\xEDa sin sectionKey, el estimado suma la clasificaci\xF3n pagada con IA.")
8836
+ instructions: z42.string().trim().min(1).max(2e3).optional().describe("Opcional para landing. Si se env\xEDa sin sectionKey, el estimado suma la clasificaci\xF3n pagada con IA.")
8602
8837
  }).strict();
8603
8838
  var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine((input, ctx) => {
8604
8839
  if (input.kind === "video" && input.nVariants !== void 0) {
8605
8840
  ctx.addIssue({
8606
- code: z41.ZodIssueCode.custom,
8841
+ code: z42.ZodIssueCode.custom,
8607
8842
  path: [
8608
8843
  "nVariants"
8609
8844
  ],
@@ -8613,7 +8848,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
8613
8848
  if (input.kind !== "video") {
8614
8849
  if (input.videoPreset !== void 0) {
8615
8850
  ctx.addIssue({
8616
- code: z41.ZodIssueCode.custom,
8851
+ code: z42.ZodIssueCode.custom,
8617
8852
  path: [
8618
8853
  "videoPreset"
8619
8854
  ],
@@ -8622,7 +8857,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
8622
8857
  }
8623
8858
  if (input.videoResolution !== void 0) {
8624
8859
  ctx.addIssue({
8625
- code: z41.ZodIssueCode.custom,
8860
+ code: z42.ZodIssueCode.custom,
8626
8861
  path: [
8627
8862
  "videoResolution"
8628
8863
  ],
@@ -8632,7 +8867,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
8632
8867
  }
8633
8868
  if (input.kind !== "image" && input.nVariants !== void 0) {
8634
8869
  ctx.addIssue({
8635
- code: z41.ZodIssueCode.custom,
8870
+ code: z42.ZodIssueCode.custom,
8636
8871
  path: [
8637
8872
  "nVariants"
8638
8873
  ],
@@ -8642,7 +8877,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
8642
8877
  if (input.kind !== "landing") {
8643
8878
  if (input.sectionKey !== void 0) {
8644
8879
  ctx.addIssue({
8645
- code: z41.ZodIssueCode.custom,
8880
+ code: z42.ZodIssueCode.custom,
8646
8881
  path: [
8647
8882
  "sectionKey"
8648
8883
  ],
@@ -8651,7 +8886,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
8651
8886
  }
8652
8887
  if (input.instructions !== void 0) {
8653
8888
  ctx.addIssue({
8654
- code: z41.ZodIssueCode.custom,
8889
+ code: z42.ZodIssueCode.custom,
8655
8890
  path: [
8656
8891
  "instructions"
8657
8892
  ],
@@ -8660,31 +8895,31 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
8660
8895
  }
8661
8896
  }
8662
8897
  });
8663
- var CreateCreativeVariantInputBase = z41.object({
8664
- sourceTaskId: z41.string().uuid().describe("ID de la tarea original completada."),
8898
+ var CreateCreativeVariantInputBase = z42.object({
8899
+ sourceTaskId: z42.string().uuid().describe("ID de la tarea original completada."),
8665
8900
  kind: CreativeVariantKindEnum,
8666
- confirm: z41.literal(true).describe("Confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrarle el estimado de cr\xE9ditos."),
8667
- clientRequestId: z41.string().uuid().describe("UUID estable y obligatorio para esta intenci\xF3n. Reutil\xEDzalo ante timeouts o transporte ambiguo. Si la API devuelve status=error, una nueva generaci\xF3n exige otra confirmaci\xF3n y un UUID nuevo."),
8668
- angle: z41.string().trim().min(1).max(500).optional().describe("\xC1ngulo nuevo para video o imagen."),
8669
- buyerPersona: z41.string().trim().min(1).max(360).optional().describe("Buyer persona coherente con el \xE1ngulo."),
8670
- scriptNotes: z41.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n breve del guion para una variante de video."),
8671
- creativeDirection: z41.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n creativa para una variante de imagen."),
8901
+ confirm: z42.literal(true).describe("Confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrarle el estimado de cr\xE9ditos."),
8902
+ clientRequestId: z42.string().uuid().describe("UUID estable y obligatorio para esta intenci\xF3n. Reutil\xEDzalo ante timeouts o transporte ambiguo. Si la API devuelve status=error, una nueva generaci\xF3n exige otra confirmaci\xF3n y un UUID nuevo."),
8903
+ angle: z42.string().trim().min(1).max(500).optional().describe("\xC1ngulo nuevo para video o imagen."),
8904
+ buyerPersona: z42.string().trim().min(1).max(360).optional().describe("Buyer persona coherente con el \xE1ngulo."),
8905
+ scriptNotes: z42.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n breve del guion para una variante de video."),
8906
+ creativeDirection: z42.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n creativa para una variante de imagen."),
8672
8907
  videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Preset p\xFAblico; el servidor resuelve modelo/tier desde el task origen."),
8673
8908
  videoResolution: VideoResolutionEnum.optional().describe("Opcional para video."),
8674
- generateAiMusic: z41.boolean().optional().describe("Opcional para video."),
8675
- nVariants: z41.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
8909
+ generateAiMusic: z42.boolean().optional().describe("Opcional para video."),
8910
+ nVariants: z42.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
8676
8911
  imageStyle: ImageCreativeStyleEnum.optional().describe("Opcional para imagen."),
8677
8912
  creativeArchetype: ImageCreativeArchetypeEnum.optional().describe("Opcional para imagen."),
8678
8913
  visualTreatment: ImageCreativeVisualTreatmentEnum.optional().describe("Opcional para imagen."),
8679
8914
  aspectRatio: ImageCreativeAspectRatioEnum.optional().describe("Opcional para imagen."),
8680
- styleMix: z41.boolean().optional().describe("Opcional para imagen."),
8915
+ styleMix: z42.boolean().optional().describe("Opcional para imagen."),
8681
8916
  sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si se omite, se clasifica con IA a partir de instructions."),
8682
- instructions: z41.string().trim().min(1).max(2e3).optional().describe("Solicitud libre para una nueva secci\xF3n de landing. La IA la clasifica si no env\xEDas sectionKey.")
8917
+ instructions: z42.string().trim().min(1).max(2e3).optional().describe("Solicitud libre para una nueva secci\xF3n de landing. La IA la clasifica si no env\xEDas sectionKey.")
8683
8918
  }).strict();
8684
8919
  var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((input, ctx) => {
8685
8920
  if ((input.kind === "video" || input.kind === "image") && !input.angle) {
8686
8921
  ctx.addIssue({
8687
- code: z41.ZodIssueCode.custom,
8922
+ code: z42.ZodIssueCode.custom,
8688
8923
  path: [
8689
8924
  "angle"
8690
8925
  ],
@@ -8693,7 +8928,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
8693
8928
  }
8694
8929
  if (input.kind === "landing" && !input.sectionKey && !input.instructions) {
8695
8930
  ctx.addIssue({
8696
- code: z41.ZodIssueCode.custom,
8931
+ code: z42.ZodIssueCode.custom,
8697
8932
  path: [
8698
8933
  "instructions"
8699
8934
  ],
@@ -8739,7 +8974,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
8739
8974
  for (const field of fieldsByKind[input.kind]) {
8740
8975
  if (input[field] !== void 0) {
8741
8976
  ctx.addIssue({
8742
- code: z41.ZodIssueCode.custom,
8977
+ code: z42.ZodIssueCode.custom,
8743
8978
  path: [
8744
8979
  field
8745
8980
  ],
@@ -8856,27 +9091,27 @@ function registerCreativeVariantTools(server, api) {
8856
9091
  __name(registerCreativeVariantTools, "registerCreativeVariantTools");
8857
9092
 
8858
9093
  // src/tools/script-review.ts
8859
- import { z as z42 } from "zod";
8860
- var GetVideoScriptInput = z42.object({
8861
- taskId: z42.string().min(1).describe("ID de la tarea (la misma que devolvi\xF3 create_product_task / generate_video_creative)."),
8862
- angleKey: z42.string().min(1).optional().describe('Clave del \xE1ngulo cuyo guion se quiere leer (formato "angle_0", "angle_1", ...). REQUERIDO para tareas multi-angle: si se omite, el backend resuelve a "angle_0" LITERAL (no "el primer pendiente"), as\xED que en multi-angle donde angle_0 ya est\xE9 aprobado/saltado obtendr\xEDas status="approved" y nunca alcanzar\xEDas el \xE1ngulo realmente pendiente. Para single-angle puede omitirse.')
9094
+ import { z as z43 } from "zod";
9095
+ var GetVideoScriptInput = z43.object({
9096
+ taskId: z43.string().min(1).describe("ID de la tarea (la misma que devolvi\xF3 create_product_task / generate_video_creative)."),
9097
+ angleKey: z43.string().min(1).optional().describe('Clave del \xE1ngulo cuyo guion se quiere leer (formato "angle_0", "angle_1", ...). REQUERIDO para tareas multi-angle: si se omite, el backend resuelve a "angle_0" LITERAL (no "el primer pendiente"), as\xED que en multi-angle donde angle_0 ya est\xE9 aprobado/saltado obtendr\xEDas status="approved" y nunca alcanzar\xEDas el \xE1ngulo realmente pendiente. Para single-angle puede omitirse.')
8863
9098
  }).strict();
8864
9099
  var VIDEO_SCRIPT_APPROVAL_MAX_SCENES2 = 50;
8865
9100
  var VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2 = 2e3;
8866
- var ApproveSceneEdit = z42.object({
8867
- index: z42.number().int().nonnegative().describe("\xCDndice de la escena (0-based, mismo que devuelve get_video_script)."),
8868
- dialogue: z42.string().min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2).refine((v) => v.trim().length > 0, {
9101
+ var ApproveSceneEdit = z43.object({
9102
+ index: z43.number().int().nonnegative().describe("\xCDndice de la escena (0-based, mismo que devuelve get_video_script)."),
9103
+ dialogue: z43.string().min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2).refine((v) => v.trim().length > 0, {
8869
9104
  message: "dialogue no puede ser s\xF3lo espacios"
8870
9105
  }).describe("Di\xE1logo final de la escena. NO puede estar vac\xEDo ni ser s\xF3lo espacios. Puede ser el original sin cambios o la edici\xF3n del usuario.")
8871
9106
  }).strict();
8872
- var ApproveVideoScriptInput = z42.object({
8873
- taskId: z42.string().min(1).describe("ID de la tarea."),
8874
- angleKey: z42.string().trim().min(1).max(120).optional().describe('Mismo angleKey usado en get_video_script. REQUERIDO en multi-angle (si se omite el backend resuelve a "angle_0" literal). En single-angle puede omitirse.'),
8875
- scenes: z42.array(ApproveSceneEdit).min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_SCENES2).describe("Escenas con los di\xE1logos finales. El backend mergea por `index` \u2014 las escenas NO incluidas conservan el di\xE1logo original. Debe haber entre 1 y 50 entradas. Para cada entrada que mandes con un dialogue distinto del original, el backend valida minWords \u2264 words \u2264 maxWords y rechaza con 400 si te sales del rango.")
9107
+ var ApproveVideoScriptInput = z43.object({
9108
+ taskId: z43.string().min(1).describe("ID de la tarea."),
9109
+ angleKey: z43.string().trim().min(1).max(120).optional().describe('Mismo angleKey usado en get_video_script. REQUERIDO en multi-angle (si se omite el backend resuelve a "angle_0" literal). En single-angle puede omitirse.'),
9110
+ scenes: z43.array(ApproveSceneEdit).min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_SCENES2).describe("Escenas con los di\xE1logos finales. El backend mergea por `index` \u2014 las escenas NO incluidas conservan el di\xE1logo original. Debe haber entre 1 y 50 entradas. Para cada entrada que mandes con un dialogue distinto del original, el backend valida minWords \u2264 words \u2264 maxWords y rechaza con 400 si te sales del rango.")
8876
9111
  }).strict();
8877
- var RegenerateVideoScriptInput = z42.object({
8878
- taskId: z42.string().min(1).describe("ID de la tarea."),
8879
- angleKey: z42.string().min(1).describe('Clave del \xE1ngulo a regenerar. Single-angle: pasa "angle_0". Multi-angle: el mismo que devuelve get_video_script. NO se puede omitir \u2014 el backend lo exige siempre.'),
9112
+ var RegenerateVideoScriptInput = z43.object({
9113
+ taskId: z43.string().min(1).describe("ID de la tarea."),
9114
+ angleKey: z43.string().min(1).describe('Clave del \xE1ngulo a regenerar. Single-angle: pasa "angle_0". Multi-angle: el mismo que devuelve get_video_script. NO se puede omitir \u2014 el backend lo exige siempre.'),
8880
9115
  narrativeStyle: NarrativeStyleEnum.describe('Narrativa para el nuevo guion. Incluye valores hist\xF3ricos porque esta herramienta tambi\xE9n regenera tareas antiguas. REQUERIDO \u2014 el backend NO acepta "mantener la actual"; siempre tienes que elegir una. Si el usuario quiere re-rolear con la misma narrativa, pasa la que ya tiene el \xE1ngulo (la ves en get_video_script o en get_task_status).')
8881
9116
  }).strict();
8882
9117
  function registerScriptReviewTools(server, api) {
@@ -8916,9 +9151,9 @@ function registerScriptReviewTools(server, api) {
8916
9151
  __name(registerScriptReviewTools, "registerScriptReviewTools");
8917
9152
 
8918
9153
  // src/tools/connections.ts
8919
- import { z as z43 } from "zod";
9154
+ import { z as z44 } from "zod";
8920
9155
  function registerConnectionTools(server, api) {
8921
- server.tool("list_shopify_shops", "Lista las tiendas Shopify conectadas por este usuario. Si la lista est\xE1 vac\xEDa, dile al usuario que conecte una tienda en productmaker.app/integrations antes de intentar publicar.", z43.object({}).shape, async () => {
9156
+ server.tool("list_shopify_shops", "Lista las tiendas Shopify conectadas por este usuario. Si la lista est\xE1 vac\xEDa, dile al usuario que conecte una tienda en productmaker.app/integrations antes de intentar publicar.", z44.object({}).shape, async () => {
8922
9157
  try {
8923
9158
  const r = await api.getJson("/v1/shopify/shops");
8924
9159
  return asText(r);
@@ -8926,10 +9161,10 @@ function registerConnectionTools(server, api) {
8926
9161
  throw translateError(e);
8927
9162
  }
8928
9163
  });
8929
- server.tool("list_shopify_products", 'Busca productos en una tienda Shopify conectada para elegir el target de una republicaci\xF3n. Usa el `id` devuelto junto con `publicationMode="existing"` en `publish_to_shopify`. Si `taskId` se proporciona, la respuesta tambi\xE9n indica el producto publicado actualmente para esa tarea y tienda. No modifica Shopify.', z43.object({
8930
- shopDomain: z43.string().min(3).max(120),
8931
- query: z43.string().trim().max(120).optional(),
8932
- taskId: z43.string().uuid().optional()
9164
+ server.tool("list_shopify_products", 'Busca productos en una tienda Shopify conectada para elegir el target de una republicaci\xF3n. Usa el `id` devuelto junto con `publicationMode="existing"` en `publish_to_shopify`. Si `taskId` se proporciona, la respuesta tambi\xE9n indica el producto publicado actualmente para esa tarea y tienda. No modifica Shopify.', z44.object({
9165
+ shopDomain: z44.string().min(3).max(120),
9166
+ query: z44.string().trim().max(120).optional(),
9167
+ taskId: z44.string().uuid().optional()
8933
9168
  }).shape, async ({ shopDomain, query, taskId }) => {
8934
9169
  try {
8935
9170
  const params = new URLSearchParams({
@@ -8943,7 +9178,7 @@ function registerConnectionTools(server, api) {
8943
9178
  throw translateError(e);
8944
9179
  }
8945
9180
  });
8946
- server.tool("list_meta_ad_accounts", "Lista las cuentas publicitarias de Meta conectadas. Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar Meta en productmaker.app/integrations.", z43.object({}).shape, async () => {
9181
+ server.tool("list_meta_ad_accounts", "Lista las cuentas publicitarias de Meta conectadas. Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar Meta en productmaker.app/integrations.", z44.object({}).shape, async () => {
8947
9182
  try {
8948
9183
  const r = await api.getJson("/v1/facebook/ad-accounts");
8949
9184
  return asText(r);
@@ -8951,7 +9186,7 @@ function registerConnectionTools(server, api) {
8951
9186
  throw translateError(e);
8952
9187
  }
8953
9188
  });
8954
- server.tool("list_tiktok_advertisers", "Lista las cuentas de TikTok Ads conectadas. Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar TikTok en productmaker.app/integrations.", z43.object({}).shape, async () => {
9189
+ server.tool("list_tiktok_advertisers", "Lista las cuentas de TikTok Ads conectadas. Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar TikTok en productmaker.app/integrations.", z44.object({}).shape, async () => {
8955
9190
  try {
8956
9191
  const r = await api.getJson("/v1/tiktok/advertisers");
8957
9192
  return asText(r);
@@ -8963,105 +9198,105 @@ function registerConnectionTools(server, api) {
8963
9198
  __name(registerConnectionTools, "registerConnectionTools");
8964
9199
 
8965
9200
  // src/tools/publish.ts
8966
- import { z as z44 } from "zod";
8967
- var ShopifyInput = z44.object({
8968
- taskId: z44.string().min(1),
8969
- shopDomain: z44.string().min(1),
9201
+ import { z as z45 } from "zod";
9202
+ var ShopifyInput = z45.object({
9203
+ taskId: z45.string().min(1),
9204
+ shopDomain: z45.string().min(1),
8970
9205
  /** Override: number of landing images to include. Default = ALL images the
8971
9206
  * task generated (the MCP fetches /v1/results first to count). Bounded
8972
9207
  * generously here; the backend validates each index against
8973
9208
  * landing.images.length and rejects out-of-range. */
8974
- numLandingImages: z44.number().int().min(1).max(20).optional(),
8975
- variantIndex: z44.number().int().min(0).optional(),
9209
+ numLandingImages: z45.number().int().min(1).max(20).optional(),
9210
+ variantIndex: z45.number().int().min(0).optional(),
8976
9211
  /** ASK THE USER before setting: "¿Tu producto tiene variantes (talla, color, etc.)?".
8977
9212
  * When true, the landing renders variant-picker pills; when false, it ships
8978
9213
  * a single-option product page. Defaults to false. */
8979
- showVariantPicker: z44.boolean().optional(),
9214
+ showVariantPicker: z45.boolean().optional(),
8980
9215
  /** Default `auto` updates the product previously published for this task and
8981
9216
  * shop. Use `existing` with targetProductId to choose another product, or
8982
9217
  * `new` only when the user explicitly wants a duplicate/new listing. */
8983
- publicationMode: z44.enum([
9218
+ publicationMode: z45.enum([
8984
9219
  "auto",
8985
9220
  "existing",
8986
9221
  "new"
8987
9222
  ]).optional(),
8988
9223
  /** Shopify product GID, numeric id, or admin product URL. Required when
8989
9224
  * publicationMode is `existing`. */
8990
- targetProductId: z44.string().trim().min(1).max(300).optional()
9225
+ targetProductId: z45.string().trim().min(1).max(300).optional()
8991
9226
  });
8992
- var McpCampaignConfig = z44.object({
9227
+ var McpCampaignConfig = z45.object({
8993
9228
  /** ALWAYS in US dollars, whatever the ad account's currency is. ProductMaker
8994
9229
  * converts to the account currency server-side. Do NOT pre-convert: on a COP
8995
9230
  * account the user's "60.000 al día" is `dailyBudgetUsd: 15`, not 60000. */
8996
- dailyBudgetUsd: z44.number().positive().optional(),
9231
+ dailyBudgetUsd: z45.number().positive().optional(),
8997
9232
  /** ISO-3166-1 alpha-2 country codes. Meta accepts max 1 (single-country
8998
9233
  * ad set). TikTok takes only the first entry. */
8999
- targetCountries: z44.array(z44.string().length(2)).max(1).optional(),
9000
- campaignName: z44.string().trim().min(1).max(100).optional()
9234
+ targetCountries: z45.array(z45.string().length(2)).max(1).optional(),
9235
+ campaignName: z45.string().trim().min(1).max(100).optional()
9001
9236
  });
9002
9237
  var McpAdAccountId = MetaAdAccountId.describe('Meta ad account id. Accepts "act_1234567890" or the bare numeric id "1234567890".');
9003
- var MetaInput = z44.object({
9004
- taskId: z44.string().min(1),
9238
+ var MetaInput = z45.object({
9239
+ taskId: z45.string().min(1),
9005
9240
  adAccountId: McpAdAccountId,
9006
- pixelId: z44.string().min(1).optional(),
9007
- landingUrl: z44.string().url().optional(),
9241
+ pixelId: z45.string().min(1).optional(),
9242
+ landingUrl: z45.string().url().optional(),
9008
9243
  /** Select which angles to publish by 0-based index. Omit to publish all
9009
9244
  * successful angles. Example: a task with 3 angles → `[0,2]` publishes #0 + #2. */
9010
- angleIndexes: z44.array(z44.number().int().nonnegative()).optional(),
9245
+ angleIndexes: z45.array(z45.number().int().nonnegative()).optional(),
9011
9246
  campaignConfig: McpCampaignConfig.optional(),
9012
9247
  /** Override the ad's primary text (body copy). If omitted, ProductMaker
9013
9248
  * uses the auto-generated copy attached to the task. Capped at 500 chars
9014
9249
  * (Meta's primary-text limit). */
9015
- primaryText: z44.string().trim().min(1).max(500).optional(),
9250
+ primaryText: z45.string().trim().min(1).max(500).optional(),
9016
9251
  /** Optional WhatsApp number. When set, the ad button opens a WhatsApp chat
9017
9252
  * (wa.me) instead of a landing page. */
9018
- phoneNumber: z44.string().min(8).max(32).optional()
9253
+ phoneNumber: z45.string().min(8).max(32).optional()
9019
9254
  });
9020
- var GroupedTaskSource = z44.object({
9021
- type: z44.literal("task"),
9022
- taskId: z44.string().min(1),
9023
- angleIndex: z44.number().int().min(0).optional(),
9024
- videoVariant: z44.enum([
9255
+ var GroupedTaskSource = z45.object({
9256
+ type: z45.literal("task"),
9257
+ taskId: z45.string().min(1),
9258
+ angleIndex: z45.number().int().min(0).optional(),
9259
+ videoVariant: z45.enum([
9025
9260
  "with_subtitles",
9026
9261
  "without_subtitles"
9027
9262
  ]).optional(),
9028
- imageVariantIndex: z44.number().int().min(0).optional()
9029
- });
9030
- var GroupedMediaSource = z44.object({
9031
- type: z44.literal("media"),
9032
- url: z44.string().url().optional(),
9033
- path: z44.string().min(1).optional(),
9034
- thumbnailUrl: z44.string().url().optional(),
9035
- thumbnailPath: z44.string().min(1).optional()
9263
+ imageVariantIndex: z45.number().int().min(0).optional()
9264
+ });
9265
+ var GroupedMediaSource = z45.object({
9266
+ type: z45.literal("media"),
9267
+ url: z45.string().url().optional(),
9268
+ path: z45.string().min(1).optional(),
9269
+ thumbnailUrl: z45.string().url().optional(),
9270
+ thumbnailPath: z45.string().min(1).optional()
9036
9271
  }).refine((value) => Boolean(value.url) !== Boolean(value.path), {
9037
9272
  message: "media source needs exactly one of `url` or `path`"
9038
9273
  }).refine((value) => !(value.thumbnailUrl && value.thumbnailPath), {
9039
9274
  message: "media source needs at most one of `thumbnailUrl` or `thumbnailPath`"
9040
9275
  });
9041
- var GroupedCreative = z44.object({
9042
- type: z44.enum([
9276
+ var GroupedCreative = z45.object({
9277
+ type: z45.enum([
9043
9278
  "image",
9044
9279
  "video"
9045
9280
  ]),
9046
- source: z44.union([
9281
+ source: z45.union([
9047
9282
  GroupedTaskSource,
9048
9283
  GroupedMediaSource
9049
9284
  ]),
9050
- title: z44.string().max(120).optional(),
9051
- description: z44.string().max(500).optional(),
9285
+ title: z45.string().max(120).optional(),
9286
+ description: z45.string().max(500).optional(),
9052
9287
  cta: MetaCtaType.optional(),
9053
- creativeId: z44.string().min(1).optional()
9288
+ creativeId: z45.string().min(1).optional()
9054
9289
  });
9055
- var GroupedCampaignConfig = z44.object({
9290
+ var GroupedCampaignConfig = z45.object({
9056
9291
  campaignType: MetaCampaignType.default("sales"),
9057
9292
  optimizationMode: MetaOptimizationMode.default("advantage_plus"),
9058
9293
  budgetMode: MetaBudgetMode.default("campaign"),
9059
- campaignDailyBudget: z44.number().int().positive().optional(),
9060
- adSets: z44.array(z44.object({
9061
- tempId: z44.string().min(1).max(80),
9062
- name: z44.string().min(1).max(100),
9063
- dailyBudget: z44.number().int().positive().optional(),
9064
- creativeIds: z44.array(z44.string().min(1)).min(1)
9294
+ campaignDailyBudget: z45.number().int().positive().optional(),
9295
+ adSets: z45.array(z45.object({
9296
+ tempId: z45.string().min(1).max(80),
9297
+ name: z45.string().min(1).max(100),
9298
+ dailyBudget: z45.number().int().positive().optional(),
9299
+ creativeIds: z45.array(z45.string().min(1)).min(1)
9065
9300
  })).min(1).max(CAMPAIGN_LIMITS.MAX_AD_SETS),
9066
9301
  /**
9067
9302
  * Valid values are 'landing' and 'whatsapp' — there is no 'website'. This
@@ -9069,22 +9304,22 @@ var GroupedCampaignConfig = z44.object({
9069
9304
  * opaque `invalid_literal` and made the WhatsApp destination — supported by
9070
9305
  * the API since #849 — unreachable from this tool.
9071
9306
  */
9072
- destination: z44.discriminatedUnion("type", [
9073
- z44.object({
9074
- type: z44.literal("landing"),
9075
- landingUrl: z44.string().url()
9307
+ destination: z45.discriminatedUnion("type", [
9308
+ z45.object({
9309
+ type: z45.literal("landing"),
9310
+ landingUrl: z45.string().url()
9076
9311
  }),
9077
- z44.object({
9078
- type: z44.literal("whatsapp"),
9079
- phoneNumber: z44.string().min(8).max(32)
9312
+ z45.object({
9313
+ type: z45.literal("whatsapp"),
9314
+ phoneNumber: z45.string().min(8).max(32)
9080
9315
  })
9081
9316
  ]),
9082
- saveAsDefault: z44.boolean().default(false),
9317
+ saveAsDefault: z45.boolean().default(false),
9083
9318
  budgetPolicy: MetaBudgetPolicy.optional()
9084
9319
  }).superRefine((value, ctx) => {
9085
9320
  if (value.budgetMode === "campaign" && value.campaignDailyBudget === void 0) {
9086
9321
  ctx.addIssue({
9087
- code: z44.ZodIssueCode.custom,
9322
+ code: z45.ZodIssueCode.custom,
9088
9323
  path: [
9089
9324
  "campaignDailyBudget"
9090
9325
  ],
@@ -9095,7 +9330,7 @@ var GroupedCampaignConfig = z44.object({
9095
9330
  value.adSets.forEach((adSet, index) => {
9096
9331
  if (adSet.dailyBudget === void 0) {
9097
9332
  ctx.addIssue({
9098
- code: z44.ZodIssueCode.custom,
9333
+ code: z45.ZodIssueCode.custom,
9099
9334
  path: [
9100
9335
  "adSets",
9101
9336
  index,
@@ -9107,22 +9342,22 @@ var GroupedCampaignConfig = z44.object({
9107
9342
  });
9108
9343
  }
9109
9344
  });
9110
- var PublishCreativesToMetaInputBase = z44.object({
9111
- creatives: z44.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
9345
+ var PublishCreativesToMetaInputBase = z45.object({
9346
+ creatives: z45.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
9112
9347
  adAccountId: McpAdAccountId,
9113
- pixelId: z44.string().min(1).optional(),
9348
+ pixelId: z45.string().min(1).optional(),
9114
9349
  /** Required unless `campaignConfig.destination` is whatsapp — see the
9115
9350
  * superRefine below. Mirrors the API's `UploadToMetaRequest`. */
9116
- landingUrl: z44.string().url().optional(),
9117
- targetCountry: z44.string().length(2),
9118
- campaignName: z44.string().trim().min(1).max(100),
9351
+ landingUrl: z45.string().url().optional(),
9352
+ targetCountry: z45.string().length(2),
9353
+ campaignName: z45.string().trim().min(1).max(100),
9119
9354
  campaignConfig: GroupedCampaignConfig.optional()
9120
9355
  });
9121
9356
  var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((value, ctx) => {
9122
9357
  const destination = value.campaignConfig?.destination;
9123
9358
  if (destination?.type !== "whatsapp" && !value.landingUrl) {
9124
9359
  ctx.addIssue({
9125
- code: z44.ZodIssueCode.custom,
9360
+ code: z45.ZodIssueCode.custom,
9126
9361
  path: [
9127
9362
  "landingUrl"
9128
9363
  ],
@@ -9131,7 +9366,7 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
9131
9366
  }
9132
9367
  if (destination?.type === "landing" && destination.landingUrl !== value.landingUrl) {
9133
9368
  ctx.addIssue({
9134
- code: z44.ZodIssueCode.custom,
9369
+ code: z45.ZodIssueCode.custom,
9135
9370
  path: [
9136
9371
  "campaignConfig",
9137
9372
  "destination",
@@ -9141,12 +9376,12 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
9141
9376
  });
9142
9377
  }
9143
9378
  });
9144
- var PresignUploadResponseSchema = z44.object({
9145
- uploadUrl: z44.string().url().refine((value) => {
9379
+ var PresignUploadResponseSchema = z45.object({
9380
+ uploadUrl: z45.string().url().refine((value) => {
9146
9381
  const url = new URL(value);
9147
9382
  return !url.username && !url.password && url.protocol === "https:";
9148
9383
  }, "presigned upload URL must be HTTPS without embedded credentials"),
9149
- publicUrl: z44.string().url().refine((value) => {
9384
+ publicUrl: z45.string().url().refine((value) => {
9150
9385
  const url = new URL(value);
9151
9386
  return !url.username && !url.password;
9152
9387
  }, "public media URL must not contain embedded credentials")
@@ -9171,16 +9406,16 @@ async function resolveAndUploadMedia(api, ref, expectedKind, transport) {
9171
9406
  return uploadResolvedMedia(api, media);
9172
9407
  }
9173
9408
  __name(resolveAndUploadMedia, "resolveAndUploadMedia");
9174
- var TikTokInput = z44.object({
9175
- taskId: z44.string().min(1),
9176
- advertiserId: z44.string().min(1),
9177
- pixelId: z44.string().min(1).optional(),
9178
- landingUrl: z44.string().url().optional(),
9179
- angleIndexes: z44.array(z44.number().int().nonnegative()).optional(),
9409
+ var TikTokInput = z45.object({
9410
+ taskId: z45.string().min(1),
9411
+ advertiserId: z45.string().min(1),
9412
+ pixelId: z45.string().min(1).optional(),
9413
+ landingUrl: z45.string().url().optional(),
9414
+ angleIndexes: z45.array(z45.number().int().nonnegative()).optional(),
9180
9415
  campaignConfig: McpCampaignConfig.optional(),
9181
9416
  /** Override the ad's description (TikTok's body copy). If omitted,
9182
9417
  * ProductMaker uses the auto-generated copy attached to the task. */
9183
- primaryText: z44.string().trim().min(1).max(500).optional()
9418
+ primaryText: z45.string().trim().min(1).max(500).optional()
9184
9419
  });
9185
9420
  function registerPublishTools(server, api, opts = {}) {
9186
9421
  const transport = opts.transport ?? "http";
@@ -9391,13 +9626,13 @@ function toTikTokCampaignConfig(cfg) {
9391
9626
  __name(toTikTokCampaignConfig, "toTikTokCampaignConfig");
9392
9627
 
9393
9628
  // src/tools/campaigns.ts
9394
- import { z as z45 } from "zod";
9395
- var MetaStatusInput = z45.object({
9396
- campaignId: z45.string().min(1).describe("Meta campaign ID returned by publish_to_meta.")
9629
+ import { z as z46 } from "zod";
9630
+ var MetaStatusInput = z46.object({
9631
+ campaignId: z46.string().min(1).describe("Meta campaign ID returned by publish_to_meta.")
9397
9632
  });
9398
- var TikTokStatusInput = z45.object({
9399
- campaignId: z45.string().min(1).describe("TikTok campaign ID returned by publish_to_tiktok."),
9400
- advertiserId: z45.string().min(1).describe("The same advertiserId used to publish the campaign.")
9633
+ var TikTokStatusInput = z46.object({
9634
+ campaignId: z46.string().min(1).describe("TikTok campaign ID returned by publish_to_tiktok."),
9635
+ advertiserId: z46.string().min(1).describe("The same advertiserId used to publish the campaign.")
9401
9636
  });
9402
9637
  function registerCampaignTools(server, api) {
9403
9638
  server.tool("get_meta_campaign_status", "Consulta el estado actual + m\xE9tricas lifetime (spend, impressions, clicks, reach) de una campa\xF1a Meta publicada. Llama esto DESPU\xC9S de `publish_to_meta` para confirmar que la campa\xF1a se cre\xF3 correctamente y para reportar performance al usuario. Es read-only \u2014 no modifica nada en Meta Ads Manager.", MetaStatusInput.shape, async (input) => {
@@ -9494,10 +9729,10 @@ function renderTikTokStatusMarkdown(r) {
9494
9729
  __name(renderTikTokStatusMarkdown, "renderTikTokStatusMarkdown");
9495
9730
 
9496
9731
  // ../../packages/product-economics/src/product-costing.ts
9497
- import { z as z47 } from "zod";
9732
+ import { z as z48 } from "zod";
9498
9733
 
9499
9734
  // ../../packages/product-economics/src/product-costing-countries.ts
9500
- import { z as z46 } from "zod";
9735
+ import { z as z47 } from "zod";
9501
9736
 
9502
9737
  // ../../packages/product-economics/src/product-costing-shipping-references.json
9503
9738
  var product_costing_shipping_references_default = {
@@ -9561,21 +9796,21 @@ var product_costing_shipping_references_default = {
9561
9796
  };
9562
9797
 
9563
9798
  // ../../packages/product-economics/src/product-costing-countries.ts
9564
- var ShippingReferenceSchema = z46.object({
9565
- reference: z46.number().positive(),
9566
- min: z46.number().nonnegative(),
9567
- max: z46.number().positive(),
9568
- updatedAt: z46.string().date(),
9569
- confidence: z46.enum([
9799
+ var ShippingReferenceSchema = z47.object({
9800
+ reference: z47.number().positive(),
9801
+ min: z47.number().nonnegative(),
9802
+ max: z47.number().positive(),
9803
+ updatedAt: z47.string().date(),
9804
+ confidence: z47.enum([
9570
9805
  "high",
9571
9806
  "medium",
9572
9807
  "medium-low"
9573
9808
  ]),
9574
- sources: z46.array(z46.string().url().startsWith("https://")).min(1).max(3)
9809
+ sources: z47.array(z47.string().url().startsWith("https://")).min(1).max(3)
9575
9810
  }).superRefine(({ reference, min, max }, context) => {
9576
9811
  if (min > reference || reference > max) {
9577
9812
  context.addIssue({
9578
- code: z46.ZodIssueCode.custom,
9813
+ code: z47.ZodIssueCode.custom,
9579
9814
  message: "shipping_reference_outside_range",
9580
9815
  path: [
9581
9816
  "reference"
@@ -9583,7 +9818,7 @@ var ShippingReferenceSchema = z46.object({
9583
9818
  });
9584
9819
  }
9585
9820
  });
9586
- var SHIPPING_REFERENCES = z46.record(ShippingReferenceSchema).parse(product_costing_shipping_references_default);
9821
+ var SHIPPING_REFERENCES = z47.record(ShippingReferenceSchema).parse(product_costing_shipping_references_default);
9587
9822
  var PRODUCT_COSTING_COUNTRY_CODES = Object.freeze(Object.keys(SHIPPING_REFERENCES));
9588
9823
  var SPANISH_LOCALE_COUNTRIES = /* @__PURE__ */ new Set([
9589
9824
  "MX",
@@ -9765,18 +10000,18 @@ function getProductCostingCountryName(country) {
9765
10000
  __name(getProductCostingCountryName, "getProductCostingCountryName");
9766
10001
 
9767
10002
  // ../../packages/product-economics/src/product-costing.ts
9768
- var ProductCostingInputSchema = z47.object({
9769
- country: z47.string().refine(isProductCostingCountryCode, "unsupported_country"),
9770
- supplierCost: z47.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").positive("supplier_cost_positive"),
9771
- shippingCost: z47.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("shipping_cost_nonnegative"),
9772
- returnRate: z47.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
9773
- cancellationRate: z47.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
9774
- adsManagerCpa: z47.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("cpa_nonnegative"),
9775
- desiredMargin: z47.number().finite().min(0, "margin_nonnegative").lt(1, "margin_below_one")
10003
+ var ProductCostingInputSchema = z48.object({
10004
+ country: z48.string().refine(isProductCostingCountryCode, "unsupported_country"),
10005
+ supplierCost: z48.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").positive("supplier_cost_positive"),
10006
+ shippingCost: z48.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("shipping_cost_nonnegative"),
10007
+ returnRate: z48.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
10008
+ cancellationRate: z48.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
10009
+ adsManagerCpa: z48.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("cpa_nonnegative"),
10010
+ desiredMargin: z48.number().finite().min(0, "margin_nonnegative").lt(1, "margin_below_one")
9776
10011
  }).superRefine(({ returnRate, cancellationRate }, context) => {
9777
10012
  if (returnRate + cancellationRate >= 1) {
9778
10013
  context.addIssue({
9779
- code: z47.ZodIssueCode.custom,
10014
+ code: z48.ZodIssueCode.custom,
9780
10015
  message: "delivery_rate_positive",
9781
10016
  path: [
9782
10017
  "cancellationRate"
@@ -9843,14 +10078,14 @@ function calculateProductCosting(rawInput) {
9843
10078
  __name(calculateProductCosting, "calculateProductCosting");
9844
10079
 
9845
10080
  // src/tools/product-costing.ts
9846
- import { z as z48 } from "zod";
9847
- var optionalAmount = /* @__PURE__ */ __name((description) => z48.number().finite().max(Number.MAX_SAFE_INTEGER).nonnegative().optional().describe(description), "optionalAmount");
9848
- var optionalPercent = /* @__PURE__ */ __name((description) => z48.number().finite().min(0).lt(100).optional().describe(description), "optionalPercent");
9849
- var ProductCostingToolInput = z48.object({
9850
- country: z48.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
10081
+ import { z as z49 } from "zod";
10082
+ var optionalAmount = /* @__PURE__ */ __name((description) => z49.number().finite().max(Number.MAX_SAFE_INTEGER).nonnegative().optional().describe(description), "optionalAmount");
10083
+ var optionalPercent = /* @__PURE__ */ __name((description) => z49.number().finite().min(0).lt(100).optional().describe(description), "optionalPercent");
10084
+ var ProductCostingToolInput = z49.object({
10085
+ country: z49.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
9851
10086
  message: "Pa\xEDs no soportado por ProductMaker."
9852
10087
  }).optional().describe("C\xF3digo ISO-2 de cualquiera de los pa\xEDses disponibles en ProductMaker. Omite para Colombia."),
9853
- supplierCost: z48.number().finite().max(Number.MAX_SAFE_INTEGER).positive().optional().describe("Costo real de una unidad con el proveedor, en la moneda del pa\xEDs. Requerido salvo que uses supplierCostCop para Colombia."),
10088
+ supplierCost: z49.number().finite().max(Number.MAX_SAFE_INTEGER).positive().optional().describe("Costo real de una unidad con el proveedor, en la moneda del pa\xEDs. Requerido salvo que uses supplierCostCop para Colombia."),
9854
10089
  supplierCostCop: optionalAmount("Alias anterior para costo del proveedor en Colombia. Prefiere supplierCost."),
9855
10090
  shippingCost: optionalAmount("Costo de un env\xEDo en la moneda del pa\xEDs. Omite para usar el valor gu\xEDa vigente."),
9856
10091
  shippingCostCop: optionalAmount("Alias anterior para flete en Colombia. Prefiere shippingCost."),
@@ -9982,7 +10217,7 @@ function formatPercent(value) {
9982
10217
  __name(formatPercent, "formatPercent");
9983
10218
 
9984
10219
  // src/tools/product-research.ts
9985
- import { z as z49 } from "zod";
10220
+ import { z as z50 } from "zod";
9986
10221
 
9987
10222
  // src/tools/product-research-idempotency.ts
9988
10223
  import { createHash as createHash2 } from "crypto";
@@ -10098,17 +10333,17 @@ var TERMINAL2 = /* @__PURE__ */ new Set([
10098
10333
  ]);
10099
10334
  var POLL_INTERVAL_MS = 5e3;
10100
10335
  var MAX_WAIT_SECONDS = 120;
10101
- var FindInput = z49.object({
10336
+ var FindInput = z50.object({
10102
10337
  targetCountry: ResearchTargetCountryEnum.describe("Pa\xEDs objetivo. S\xF3lo CO, MX, PE, EC y CL tienen cobertura de investigaci\xF3n."),
10103
10338
  categoryKey: ResearchCategoryEnum.default("auto").describe('Categor\xEDa a investigar. Usa "auto" para que la IA elija seg\xFAn el mercado.'),
10104
- confirmed: z49.boolean().describe(`Ponlo en true SOLO despu\xE9s de que el usuario haya aceptado el cargo de ${PRICE_CREDITS} cr\xE9ditos. No lo asumas.`),
10105
- waitSeconds: z49.number().int().min(0).max(MAX_WAIT_SECONDS).optional().describe("Segundos a esperar el reporte antes de devolver el runId. M\xE1ximo 120.")
10339
+ confirmed: z50.boolean().describe(`Ponlo en true SOLO despu\xE9s de que el usuario haya aceptado el cargo de ${PRICE_CREDITS} cr\xE9ditos. No lo asumas.`),
10340
+ waitSeconds: z50.number().int().min(0).max(MAX_WAIT_SECONDS).optional().describe("Segundos a esperar el reporte antes de devolver el runId. M\xE1ximo 120.")
10106
10341
  });
10107
- var GetInput = z49.object({
10108
- runId: z49.string().uuid().describe("ID del run devuelto por find_winning_products.")
10342
+ var GetInput = z50.object({
10343
+ runId: z50.string().uuid().describe("ID del run devuelto por find_winning_products.")
10109
10344
  });
10110
- var ListInput = z49.object({
10111
- limit: z49.number().int().min(1).max(20).default(10)
10345
+ var ListInput = z50.object({
10346
+ limit: z50.number().int().min(1).max(20).default(10)
10112
10347
  });
10113
10348
  function textResult(text) {
10114
10349
  return {
@@ -10199,6 +10434,230 @@ _Esta llamada no cobr\xF3 cr\xE9ditos: se reutiliz\xF3 una investigaci\xF3n reci
10199
10434
  }
10200
10435
  __name(registerProductResearchTools, "registerProductResearchTools");
10201
10436
 
10437
+ // src/tools/dropshipping-projects.ts
10438
+ import { z as z51 } from "zod";
10439
+ var ProjectId = z51.string().uuid();
10440
+ var DropshippingProjectStatus = z51.enum([
10441
+ "active",
10442
+ "completed",
10443
+ "archived"
10444
+ ]);
10445
+ var DropshippingProjectPhase = z51.enum([
10446
+ "research",
10447
+ "costing",
10448
+ "media",
10449
+ "generating",
10450
+ "storefront",
10451
+ "advertising",
10452
+ "complete"
10453
+ ]);
10454
+ var TargetCountry = z51.enum([
10455
+ "CO",
10456
+ "MX",
10457
+ "PE",
10458
+ "EC",
10459
+ "CL"
10460
+ ]);
10461
+ var ResearchCategory = z51.enum([
10462
+ "auto",
10463
+ "automotive",
10464
+ "kitchen",
10465
+ "pets",
10466
+ "home_organization",
10467
+ "beauty",
10468
+ "wellness",
10469
+ "fitness",
10470
+ "baby",
10471
+ "tech",
10472
+ "tools",
10473
+ "garden",
10474
+ "cleaning"
10475
+ ]);
10476
+ var HttpUrl3 = z51.string().url().max(2e3).refine((value) => {
10477
+ try {
10478
+ return [
10479
+ "http:",
10480
+ "https:"
10481
+ ].includes(new URL(value).protocol);
10482
+ } catch {
10483
+ return false;
10484
+ }
10485
+ }, "URL must use HTTP(S)");
10486
+ var ResearchCandidateOrigin = z51.object({
10487
+ type: z51.literal("research_candidate"),
10488
+ runId: ProjectId,
10489
+ candidateSourceId: z51.string().min(1).max(160)
10490
+ });
10491
+ var TaskOrigin = z51.object({
10492
+ type: z51.literal("task"),
10493
+ taskId: ProjectId
10494
+ });
10495
+ var ManualOrigin = z51.object({
10496
+ type: z51.literal("manual"),
10497
+ clientRequestId: ProjectId,
10498
+ country: z51.string().min(2).max(3).optional()
10499
+ });
10500
+ var StartProjectInput = z51.object({
10501
+ origin: z51.discriminatedUnion("type", [
10502
+ ResearchCandidateOrigin,
10503
+ TaskOrigin,
10504
+ ManualOrigin
10505
+ ]),
10506
+ productName: z51.string().trim().min(1).max(240).optional(),
10507
+ productUrl: HttpUrl3.optional(),
10508
+ country: z51.string().min(2).max(3).optional(),
10509
+ languageCode: z51.string().min(2).max(16).optional()
10510
+ }).strict();
10511
+ var CostProjectInput = z51.object({
10512
+ projectId: ProjectId,
10513
+ version: z51.number().int().positive(),
10514
+ country: z51.string().trim().min(2).max(3),
10515
+ supplierCost: z51.number().finite().positive(),
10516
+ shippingCost: z51.number().finite().nonnegative(),
10517
+ returnRate: z51.number().finite().min(0).lt(1),
10518
+ cancellationRate: z51.number().finite().min(0).lt(1),
10519
+ adsManagerCpa: z51.number().finite().nonnegative(),
10520
+ desiredMargin: z51.number().finite().min(0).lt(1)
10521
+ });
10522
+ var GuideInput = z51.object({
10523
+ projectId: ProjectId.optional()
10524
+ }).strict();
10525
+ var DropshippingProjectNextAction = z51.discriminatedUnion("kind", [
10526
+ "cost_product",
10527
+ "provide_product_media",
10528
+ "generate_creatives",
10529
+ "wait_for_creatives",
10530
+ "repair_creatives",
10531
+ "complete_creative_set",
10532
+ "connect_shopify",
10533
+ "publish_storefront",
10534
+ "wait_for_storefront",
10535
+ "connect_meta",
10536
+ "create_meta_campaign",
10537
+ "repair_meta_campaign",
10538
+ "review_paused_campaign"
10539
+ ].map((kind) => z51.object({
10540
+ kind: z51.literal(kind),
10541
+ href: z51.string().min(1)
10542
+ })));
10543
+ var CostingInputSchema = z51.object({
10544
+ country: z51.string().min(2).max(3),
10545
+ supplierCost: z51.number().finite().positive(),
10546
+ shippingCost: z51.number().finite().nonnegative(),
10547
+ returnRate: z51.number().finite().min(0).lt(1),
10548
+ cancellationRate: z51.number().finite().min(0).lt(1),
10549
+ adsManagerCpa: z51.number().finite().nonnegative(),
10550
+ desiredMargin: z51.number().finite().min(0).lt(1)
10551
+ }).superRefine(({ returnRate, cancellationRate }, context) => {
10552
+ if (returnRate + cancellationRate >= 1) context.addIssue({
10553
+ code: z51.ZodIssueCode.custom,
10554
+ path: [
10555
+ "cancellationRate"
10556
+ ]
10557
+ });
10558
+ });
10559
+ var CostingResultSchema = z51.object({
10560
+ country: z51.string().min(2).max(3),
10561
+ deliveryRate: z51.number().finite(),
10562
+ adjustedShippingCost: z51.number().finite(),
10563
+ adjustedCpa: z51.number().finite(),
10564
+ totalCost: z51.number().finite(),
10565
+ suggestedPrice: z51.number().finite(),
10566
+ baseCommercialPrice: z51.number().finite(),
10567
+ minimumSafePrice: z51.number().finite(),
10568
+ commercialPrice: z51.number().finite(),
10569
+ comparisonPrice: z51.number().finite(),
10570
+ estimatedProfit: z51.number().finite(),
10571
+ actualMargin: z51.number().finite(),
10572
+ breakEvenAdsManagerCpa: z51.number().finite(),
10573
+ wasPriceAutoAdjusted: z51.boolean(),
10574
+ profitabilityStatus: z51.literal("healthy")
10575
+ });
10576
+ var ProjectResponseSchema = z51.object({
10577
+ id: ProjectId,
10578
+ taskId: ProjectId.nullable(),
10579
+ status: DropshippingProjectStatus,
10580
+ originKey: z51.string().min(1),
10581
+ researchRunId: ProjectId.nullable(),
10582
+ candidateSourceId: z51.string().nullable(),
10583
+ productName: z51.string().nullable(),
10584
+ productUrl: HttpUrl3.nullable(),
10585
+ referenceImageUrl: HttpUrl3.nullable(),
10586
+ targetCountry: z51.string().nullable(),
10587
+ languageCode: z51.string().nullable(),
10588
+ hasReferenceImage: z51.boolean(),
10589
+ costingInput: CostingInputSchema.nullable(),
10590
+ costingResult: CostingResultSchema.nullable(),
10591
+ costingVersion: z51.number().int().positive().nullable(),
10592
+ version: z51.number().int().positive(),
10593
+ phase: DropshippingProjectPhase,
10594
+ nextAction: DropshippingProjectNextAction.nullable(),
10595
+ createdAt: z51.string().datetime(),
10596
+ updatedAt: z51.string().datetime()
10597
+ }).passthrough();
10598
+ function projectText(raw) {
10599
+ const project = ProjectResponseSchema.parse(raw);
10600
+ const next = project.nextAction ? `${project.nextAction.kind} (${project.nextAction.href})` : "ninguna";
10601
+ return {
10602
+ content: [
10603
+ {
10604
+ type: "text",
10605
+ text: JSON.stringify({
10606
+ projectId: project.id,
10607
+ productName: project.productName,
10608
+ phase: project.phase,
10609
+ nextAction: project.nextAction,
10610
+ hasReferenceImage: project.hasReferenceImage,
10611
+ costingSaved: Boolean(project.costingResult),
10612
+ version: project.version,
10613
+ message: `Siguiente paso: ${next}. Relee este proyecto despu\xE9s de cada mutaci\xF3n.`
10614
+ })
10615
+ }
10616
+ ]
10617
+ };
10618
+ }
10619
+ __name(projectText, "projectText");
10620
+ function registerDropshippingProjectTools(server, api) {
10621
+ server.tool("start_dropshipping_project", "Inicia o reanuda un proyecto opcional de dropshipping. \xDAsalo cuando el usuario quiera seguir un producto hasta publicarlo; para una operaci\xF3n puntual usa el tool directo. La creaci\xF3n es idempotente y no cobra cr\xE9ditos.", StartProjectInput.shape, async (input) => {
10622
+ try {
10623
+ return projectText(await api.postJson("/v1/dropshipping-projects", input));
10624
+ } catch (error) {
10625
+ throw translateError(error);
10626
+ }
10627
+ });
10628
+ server.tool("cost_dropshipping_project", "Guarda el costeo de un proyecto guiado. Escribe solo inputs; el backend recalcula el resultado can\xF3nico y controla la versi\xF3n. Pide al usuario el costo real del proveedor: nunca lo inventes. No crea tarea ni publica nada.", CostProjectInput.shape, async (input) => {
10629
+ try {
10630
+ if (input.returnRate + input.cancellationRate >= 1) {
10631
+ throw new Error("returnRate + cancellationRate debe ser menor que 1.");
10632
+ }
10633
+ const { projectId, version, ...costingInput } = input;
10634
+ return projectText(await api.putJson(`/v1/dropshipping-projects/${encodeURIComponent(projectId)}/costing`, {
10635
+ version,
10636
+ input: costingInput
10637
+ }));
10638
+ } catch (error) {
10639
+ throw translateError(error);
10640
+ }
10641
+ });
10642
+ server.tool("guide_dropshipping_launch", "Gu\xEDa read-only del lanzamiento. Sin projectId explica cu\xE1ndo usar un flujo directo versus uno guiado; con projectId devuelve la fase y la \xFAnica nextAction calculada por ProductMaker. Despu\xE9s de cada mutaci\xF3n vuelve a llamar este tool o lee el proyecto. Nunca activa Meta.", GuideInput.shape, async (input) => {
10643
+ try {
10644
+ if (!input.projectId) {
10645
+ return asText({
10646
+ mode: "choose",
10647
+ guided: "Usa start_dropshipping_project cuando el usuario quiera encontrar un producto y dejarlo listo para publicar.",
10648
+ direct: "En modo directo usa cost_product, create_product_task, publish_to_shopify o publish_to_meta cuando pidi\xF3 una operaci\xF3n espec\xEDfica.",
10649
+ confirmation: "Antes de acciones pagadas o externas, muestra el estimado y pide confirmaci\xF3n.",
10650
+ safety: "Las campa\xF1as Meta quedan PAUSED; el usuario las activa manualmente en Ads Manager."
10651
+ });
10652
+ }
10653
+ return projectText(await api.getJson(`/v1/dropshipping-projects/${encodeURIComponent(input.projectId)}`));
10654
+ } catch (error) {
10655
+ throw translateError(error);
10656
+ }
10657
+ });
10658
+ }
10659
+ __name(registerDropshippingProjectTools, "registerDropshippingProjectTools");
10660
+
10202
10661
  // src/server.ts
10203
10662
  function createServer(opts) {
10204
10663
  const { apiKey, apiBaseUrl, proxyBaseUrl = null, transport = "http" } = opts;
@@ -10216,6 +10675,7 @@ function createServer(opts) {
10216
10675
  const api = new ApiClient(apiBaseUrl, apiKey);
10217
10676
  registerProductCostingTools(server);
10218
10677
  registerProductResearchTools(server, api);
10678
+ registerDropshippingProjectTools(server, api);
10219
10679
  registerTaskTools(server, api, {
10220
10680
  proxyBaseUrl,
10221
10681
  transport