@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/stdio.js CHANGED
@@ -73,6 +73,17 @@ var ApiClient = class {
73
73
  });
74
74
  return this.parse(res);
75
75
  }
76
+ async putJson(path3, body) {
77
+ const res = await fetch(`${this.baseUrl}${path3}`, {
78
+ method: "PUT",
79
+ headers: {
80
+ ...this.authHeaders(),
81
+ "Content-Type": "application/json"
82
+ },
83
+ body: JSON.stringify(body)
84
+ });
85
+ return this.parse(res);
86
+ }
76
87
  async deleteJson(path3) {
77
88
  const res = await fetch(`${this.baseUrl}${path3}`, {
78
89
  method: "DELETE",
@@ -139,10 +150,10 @@ var ApiClient = class {
139
150
  };
140
151
 
141
152
  // src/index.ts
142
- var MCP_VERSION = "2.0.0";
153
+ var MCP_VERSION = "2.1.0";
143
154
 
144
155
  // src/tools/tasks.ts
145
- import { z as z39 } from "zod";
156
+ import { z as z40 } from "zod";
146
157
 
147
158
  // src/resolve-image.ts
148
159
  import { lookup as dnsLookup } from "dns/promises";
@@ -1389,6 +1400,8 @@ var INGEST_ALLOWED_IMAGE_MIME_TYPES = [
1389
1400
  var PRODUCT_CONTEXT_MAX_CHARS = 3e3;
1390
1401
  var INGEST_ALLOWED_IMAGE_MIME_TYPE_SET = new Set(INGEST_ALLOWED_IMAGE_MIME_TYPES);
1391
1402
  var IngestOptions = z13.object({
1403
+ /** Optional owner-scoped guided journey context. Never bypasses ingest gates. */
1404
+ dropshippingProjectId: z13.string().uuid().optional(),
1392
1405
  languageCode: LanguageCodeSchema.default("es-CO"),
1393
1406
  ugcFormat: VideoFormat.default("single_15s"),
1394
1407
  country: CountrySchema,
@@ -6543,17 +6556,185 @@ var RegenerateEditRequestSchema = z36.object({
6543
6556
  comment: z36.string().min(1).max(2e3)
6544
6557
  });
6545
6558
 
6546
- // ../../packages/types/src/scene-subject-contract.ts
6559
+ // ../../packages/types/src/dropshipping-project.ts
6547
6560
  import { z as z37 } from "zod";
6548
- var SceneSubjectContractSchema = z37.object({
6549
- maxPeople: z37.number().int().min(1).max(10),
6550
- minPeople: z37.number().int().min(0).max(10),
6551
- minDistinctPeople: z37.number().int().min(0).max(10),
6552
- maxHands: z37.number().int().min(0).max(20)
6561
+ var DropshippingProjectStatusSchema = z37.enum([
6562
+ "active",
6563
+ "completed",
6564
+ "archived"
6565
+ ]);
6566
+ var DropshippingProjectPhaseSchema = z37.enum([
6567
+ "research",
6568
+ "costing",
6569
+ "media",
6570
+ "generating",
6571
+ "storefront",
6572
+ "advertising",
6573
+ "complete"
6574
+ ]);
6575
+ var DropshippingProjectCostingInputSchema = z37.object({
6576
+ country: z37.string().min(2).max(3),
6577
+ supplierCost: z37.number().finite().positive(),
6578
+ shippingCost: z37.number().finite().nonnegative(),
6579
+ returnRate: z37.number().finite().min(0).lt(1),
6580
+ cancellationRate: z37.number().finite().min(0).lt(1),
6581
+ adsManagerCpa: z37.number().finite().nonnegative(),
6582
+ desiredMargin: z37.number().finite().min(0).lt(1)
6583
+ }).superRefine(({ returnRate, cancellationRate }, context) => {
6584
+ if (returnRate + cancellationRate >= 1) {
6585
+ context.addIssue({
6586
+ code: z37.ZodIssueCode.custom,
6587
+ path: [
6588
+ "cancellationRate"
6589
+ ],
6590
+ message: "delivery_rate_positive"
6591
+ });
6592
+ }
6593
+ });
6594
+ var DropshippingProjectCostingResultSchema = z37.object({
6595
+ country: z37.string().min(2).max(3),
6596
+ deliveryRate: z37.number().finite(),
6597
+ adjustedShippingCost: z37.number().finite(),
6598
+ adjustedCpa: z37.number().finite(),
6599
+ totalCost: z37.number().finite(),
6600
+ suggestedPrice: z37.number().finite(),
6601
+ baseCommercialPrice: z37.number().finite(),
6602
+ minimumSafePrice: z37.number().finite(),
6603
+ commercialPrice: z37.number().finite(),
6604
+ comparisonPrice: z37.number().finite(),
6605
+ estimatedProfit: z37.number().finite(),
6606
+ actualMargin: z37.number().finite(),
6607
+ breakEvenAdsManagerCpa: z37.number().finite(),
6608
+ wasPriceAutoAdjusted: z37.boolean(),
6609
+ profitabilityStatus: z37.literal("healthy")
6610
+ });
6611
+ var HttpUrlSchema = z37.string().url().max(2e3).refine((value) => {
6612
+ try {
6613
+ return [
6614
+ "http:",
6615
+ "https:"
6616
+ ].includes(new URL(value).protocol);
6617
+ } catch {
6618
+ return false;
6619
+ }
6620
+ }, "Product URL must use HTTP(S)");
6621
+ var ResearchCandidateOriginSchema = z37.object({
6622
+ type: z37.literal("research_candidate"),
6623
+ runId: z37.string().uuid(),
6624
+ candidateSourceId: z37.string().min(1).max(160)
6625
+ });
6626
+ var TaskOriginSchema = z37.object({
6627
+ type: z37.literal("task"),
6628
+ taskId: z37.string().uuid()
6629
+ });
6630
+ var ManualOriginSchema = z37.object({
6631
+ type: z37.literal("manual"),
6632
+ clientRequestId: z37.string().uuid(),
6633
+ country: z37.string().min(2).max(3).optional()
6634
+ });
6635
+ var CreateDropshippingProjectRequestSchema = z37.object({
6636
+ origin: z37.discriminatedUnion("type", [
6637
+ ResearchCandidateOriginSchema,
6638
+ TaskOriginSchema,
6639
+ ManualOriginSchema
6640
+ ]),
6641
+ productName: z37.string().trim().min(1).max(240).optional(),
6642
+ productUrl: HttpUrlSchema.optional(),
6643
+ country: z37.string().min(2).max(3).optional(),
6644
+ languageCode: z37.string().min(2).max(16).optional()
6645
+ }).strict();
6646
+ var SaveDropshippingProjectCostingRequestSchema = z37.object({
6647
+ input: DropshippingProjectCostingInputSchema,
6648
+ version: z37.number().int().positive()
6649
+ }).strict();
6650
+ var DropshippingProjectNextActionSchema = z37.discriminatedUnion("kind", [
6651
+ z37.object({
6652
+ kind: z37.literal("cost_product"),
6653
+ href: z37.string().min(1)
6654
+ }),
6655
+ z37.object({
6656
+ kind: z37.literal("provide_product_media"),
6657
+ href: z37.string().min(1)
6658
+ }),
6659
+ z37.object({
6660
+ kind: z37.literal("generate_creatives"),
6661
+ href: z37.string().min(1)
6662
+ }),
6663
+ z37.object({
6664
+ kind: z37.literal("wait_for_creatives"),
6665
+ href: z37.string().min(1)
6666
+ }),
6667
+ z37.object({
6668
+ kind: z37.literal("repair_creatives"),
6669
+ href: z37.string().min(1)
6670
+ }),
6671
+ z37.object({
6672
+ kind: z37.literal("complete_creative_set"),
6673
+ href: z37.string().min(1)
6674
+ }),
6675
+ z37.object({
6676
+ kind: z37.literal("connect_shopify"),
6677
+ href: z37.string().min(1)
6678
+ }),
6679
+ z37.object({
6680
+ kind: z37.literal("publish_storefront"),
6681
+ href: z37.string().min(1)
6682
+ }),
6683
+ z37.object({
6684
+ kind: z37.literal("wait_for_storefront"),
6685
+ href: z37.string().min(1)
6686
+ }),
6687
+ z37.object({
6688
+ kind: z37.literal("connect_meta"),
6689
+ href: z37.string().min(1)
6690
+ }),
6691
+ z37.object({
6692
+ kind: z37.literal("create_meta_campaign"),
6693
+ href: z37.string().min(1)
6694
+ }),
6695
+ z37.object({
6696
+ kind: z37.literal("repair_meta_campaign"),
6697
+ href: z37.string().min(1)
6698
+ }),
6699
+ z37.object({
6700
+ kind: z37.literal("review_paused_campaign"),
6701
+ href: z37.string().min(1)
6702
+ })
6703
+ ]);
6704
+ var DropshippingProjectResponseSchema = z37.object({
6705
+ id: z37.string().uuid(),
6706
+ taskId: z37.string().uuid().nullable(),
6707
+ status: DropshippingProjectStatusSchema,
6708
+ originKey: z37.string().min(1),
6709
+ researchRunId: z37.string().uuid().nullable(),
6710
+ candidateSourceId: z37.string().nullable(),
6711
+ productName: z37.string().nullable(),
6712
+ productUrl: HttpUrlSchema.nullable(),
6713
+ referenceImageUrl: z37.string().url().nullable(),
6714
+ hasReferenceImage: z37.boolean(),
6715
+ targetCountry: z37.string().nullable(),
6716
+ languageCode: z37.string().nullable(),
6717
+ costingInput: DropshippingProjectCostingInputSchema.nullable(),
6718
+ costingResult: DropshippingProjectCostingResultSchema.nullable(),
6719
+ costingVersion: z37.number().int().positive().nullable(),
6720
+ version: z37.number().int().positive(),
6721
+ phase: DropshippingProjectPhaseSchema,
6722
+ nextAction: DropshippingProjectNextActionSchema.nullable(),
6723
+ createdAt: z37.string().datetime(),
6724
+ updatedAt: z37.string().datetime()
6725
+ });
6726
+
6727
+ // ../../packages/types/src/scene-subject-contract.ts
6728
+ import { z as z38 } from "zod";
6729
+ var SceneSubjectContractSchema = z38.object({
6730
+ maxPeople: z38.number().int().min(1).max(10),
6731
+ minPeople: z38.number().int().min(0).max(10),
6732
+ minDistinctPeople: z38.number().int().min(0).max(10),
6733
+ maxHands: z38.number().int().min(0).max(20)
6553
6734
  }).superRefine((value, ctx) => {
6554
6735
  if (value.minPeople > value.maxPeople) {
6555
6736
  ctx.addIssue({
6556
- code: z37.ZodIssueCode.custom,
6737
+ code: z38.ZodIssueCode.custom,
6557
6738
  path: [
6558
6739
  "minPeople"
6559
6740
  ],
@@ -6562,7 +6743,7 @@ var SceneSubjectContractSchema = z37.object({
6562
6743
  }
6563
6744
  if (value.minDistinctPeople > value.maxPeople) {
6564
6745
  ctx.addIssue({
6565
- code: z37.ZodIssueCode.custom,
6746
+ code: z38.ZodIssueCode.custom,
6566
6747
  path: [
6567
6748
  "minDistinctPeople"
6568
6749
  ],
@@ -6571,7 +6752,7 @@ var SceneSubjectContractSchema = z37.object({
6571
6752
  }
6572
6753
  if (value.minDistinctPeople > value.minPeople) {
6573
6754
  ctx.addIssue({
6574
- code: z37.ZodIssueCode.custom,
6755
+ code: z38.ZodIssueCode.custom,
6575
6756
  path: [
6576
6757
  "minDistinctPeople"
6577
6758
  ],
@@ -6587,6 +6768,11 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
6587
6768
  confirmation: "credit_consuming",
6588
6769
  schemaHash: "bab794a9700a8d3f42b97f444a776e7ed4dcd833038c52588f951b50970ff3fa"
6589
6770
  },
6771
+ {
6772
+ name: "cost_dropshipping_project",
6773
+ confirmation: "state_change",
6774
+ schemaHash: "746797b5400b170ba3a0704d432afff635794f0a7ae0b9da07b26d49e3b82242"
6775
+ },
6590
6776
  {
6591
6777
  name: "cost_product",
6592
6778
  confirmation: "none",
@@ -6600,7 +6786,7 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
6600
6786
  {
6601
6787
  name: "create_product_task",
6602
6788
  confirmation: "credit_consuming",
6603
- schemaHash: "5346684b007a40f6e8d53cc0b56ebb20aeb7e5ba48cdde43bc9f4e6163ed2d3f"
6789
+ schemaHash: "828ee643bb3a05a63c6e63730459d35ce1621cbd559bbc76a6c5fcec9a45654d"
6604
6790
  },
6605
6791
  {
6606
6792
  name: "edit_task_draft",
@@ -6662,6 +6848,11 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
6662
6848
  confirmation: "none",
6663
6849
  schemaHash: "c96e7042368ba6f241773d26d4a443894b092801d1c81054f3798865138edfba"
6664
6850
  },
6851
+ {
6852
+ name: "guide_dropshipping_launch",
6853
+ confirmation: "none",
6854
+ schemaHash: "0e7a1cb5ad5c6681473df5689336edcd7a63b8d68853359982e44bfd98a65db8"
6855
+ },
6665
6856
  {
6666
6857
  name: "list_meta_ad_accounts",
6667
6858
  confirmation: "none",
@@ -6721,6 +6912,11 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
6721
6912
  name: "resolve_pending_action",
6722
6913
  confirmation: "credit_consuming",
6723
6914
  schemaHash: "3a9f33a26fce63b20bece3a528f8eb16e917dabc8a9ec5f04e746bad84757aa3"
6915
+ },
6916
+ {
6917
+ name: "start_dropshipping_project",
6918
+ confirmation: "state_change",
6919
+ schemaHash: "2b621702e8da8bb894d392060e5e4f677a8dc26f454e991dbb929053715a4f8b"
6724
6920
  }
6725
6921
  ];
6726
6922
  var PRODUCTMAKER_MCP_CAPABILITY_NAMES = PRODUCTMAKER_MCP_CAPABILITIES.map((capability) => capability.name);
@@ -6964,10 +7160,10 @@ async function resolveMediaRef(ref, opts) {
6964
7160
  __name(resolveMediaRef, "resolveMediaRef");
6965
7161
 
6966
7162
  // src/tools/shared.ts
6967
- import { z as z38 } from "zod";
6968
- var Lang = z38.string().min(2).max(10);
6969
- var Country = z38.string().min(2).max(3);
6970
- var LandingStrategyEnum = z38.enum([
7163
+ import { z as z39 } from "zod";
7164
+ var Lang = z39.string().min(2).max(10);
7165
+ var Country = z39.string().min(2).max(3);
7166
+ var LandingStrategyEnum = z39.enum([
6971
7167
  "auto",
6972
7168
  "classic",
6973
7169
  "pain_solution",
@@ -6976,12 +7172,12 @@ var LandingStrategyEnum = z38.enum([
6976
7172
  "offer_value",
6977
7173
  "lifestyle_identity"
6978
7174
  ]);
6979
- var LandingImageFormatEnum = z38.enum([
7175
+ var LandingImageFormatEnum = z39.enum([
6980
7176
  "9:16",
6981
7177
  "4:5",
6982
7178
  "1:1"
6983
7179
  ]);
6984
- var VideoPresetEnum = z38.enum([
7180
+ var VideoPresetEnum = z39.enum([
6985
7181
  "auto",
6986
7182
  "ugc_dynamic",
6987
7183
  "ugc_how_to",
@@ -6993,7 +7189,7 @@ var VideoPresetEnum = z38.enum([
6993
7189
  "pixar_problem",
6994
7190
  "pixar_sassy"
6995
7191
  ]);
6996
- var TaskCreativeVariantVideoPresetEnum = z38.enum([
7192
+ var TaskCreativeVariantVideoPresetEnum = z39.enum([
6997
7193
  "auto",
6998
7194
  "ugc_dynamic",
6999
7195
  "ugc_how_to",
@@ -7058,7 +7254,7 @@ function resolveVideoPreset(preset) {
7058
7254
  return VIDEO_PRESET_RESOLUTION[preset] ?? null;
7059
7255
  }
7060
7256
  __name(resolveVideoPreset, "resolveVideoPreset");
7061
- var VideoTierEnum = z38.enum([
7257
+ var VideoTierEnum = z39.enum([
7062
7258
  "basic"
7063
7259
  ]);
7064
7260
  function resolveVideoTier(_tier) {
@@ -7073,17 +7269,17 @@ function resolveVideoFormatForDuration(durationSeconds) {
7073
7269
  return durationSeconds === 30 ? "double_30s" : "single_15s";
7074
7270
  }
7075
7271
  __name(resolveVideoFormatForDuration, "resolveVideoFormatForDuration");
7076
- var VideoResolutionEnum = z38.enum([
7272
+ var VideoResolutionEnum = z39.enum([
7077
7273
  "480p",
7078
7274
  "720p"
7079
7275
  ]);
7080
- var VideoStyleEnum = z38.enum([
7276
+ var VideoStyleEnum = z39.enum([
7081
7277
  "ugc",
7082
7278
  "cartoon_3d",
7083
7279
  "product_only",
7084
7280
  "ai_dynamic"
7085
7281
  ]);
7086
- var NarrativeStyleEnum = z38.enum([
7282
+ var NarrativeStyleEnum = z39.enum([
7087
7283
  "review",
7088
7284
  "ugc_how_to",
7089
7285
  "problem_product_cta",
@@ -7100,7 +7296,7 @@ var NarrativeStyleEnum = z38.enum([
7100
7296
  "ugc_skeptic_confession",
7101
7297
  "ugc_demo_review"
7102
7298
  ]);
7103
- var PublicNarrativeStyleEnum = z38.enum([
7299
+ var PublicNarrativeStyleEnum = z39.enum([
7104
7300
  "review",
7105
7301
  "ugc_how_to",
7106
7302
  "ugc_unboxing_chaos",
@@ -7111,7 +7307,7 @@ var PublicNarrativeStyleEnum = z38.enum([
7111
7307
  "animated_problem_pixar",
7112
7308
  "sassy_product_pixar"
7113
7309
  ]);
7114
- var VideoModelEnum = z38.enum([
7310
+ var VideoModelEnum = z39.enum([
7115
7311
  "veo",
7116
7312
  "wan",
7117
7313
  "bytedance",
@@ -7119,25 +7315,25 @@ var VideoModelEnum = z38.enum([
7119
7315
  "grok",
7120
7316
  "kling"
7121
7317
  ]);
7122
- var ProductReferenceUserChoiceEnum = z38.enum([
7318
+ var ProductReferenceUserChoiceEnum = z39.enum([
7123
7319
  "same_product",
7124
7320
  "variants",
7125
7321
  "different_products",
7126
7322
  "combo_distinct"
7127
7323
  ]);
7128
- var ProductReferenceCandidateGroupingEnum = z38.enum([
7324
+ var ProductReferenceCandidateGroupingEnum = z39.enum([
7129
7325
  "same_product_same_variant",
7130
7326
  "same_product_different_variants",
7131
7327
  "different_products",
7132
7328
  "unclear"
7133
7329
  ]);
7134
- var GapDurationChoiceMirror = z38.object({
7135
- angleIndex: z38.number().int().nonnegative(),
7136
- recommendedSec: z38.union([
7137
- z38.literal(15),
7138
- z38.literal(30)
7330
+ var GapDurationChoiceMirror = z39.object({
7331
+ angleIndex: z39.number().int().nonnegative(),
7332
+ recommendedSec: z39.union([
7333
+ z39.literal(15),
7334
+ z39.literal(30)
7139
7335
  ]),
7140
- estimatedDeltaCredits: z38.number().int().nonnegative().optional()
7336
+ estimatedDeltaCredits: z39.number().int().nonnegative().optional()
7141
7337
  });
7142
7338
  function buildSelectedImageUrlsHash(urls) {
7143
7339
  const input = [
@@ -7151,13 +7347,13 @@ function buildSelectedImageUrlsHash(urls) {
7151
7347
  return `fnv1a:${hash.toString(16).padStart(8, "0")}:${urls.length}`;
7152
7348
  }
7153
7349
  __name(buildSelectedImageUrlsHash, "buildSelectedImageUrlsHash");
7154
- var ImageCreativeStyleEnum = z38.enum([
7350
+ var ImageCreativeStyleEnum = z39.enum([
7155
7351
  "studio",
7156
7352
  "floating",
7157
7353
  "ingredients",
7158
7354
  "in_use"
7159
7355
  ]);
7160
- var ImageCreativeArchetypeEnum = z38.enum([
7356
+ var ImageCreativeArchetypeEnum = z39.enum([
7161
7357
  "direct_response_product_ad",
7162
7358
  "hero_outcome",
7163
7359
  "offer_value_prop",
@@ -7166,23 +7362,23 @@ var ImageCreativeArchetypeEnum = z38.enum([
7166
7362
  "problem_solution",
7167
7363
  "lifestyle_identity"
7168
7364
  ]);
7169
- var ImageCreativeVisualTreatmentEnum = z38.enum([
7365
+ var ImageCreativeVisualTreatmentEnum = z39.enum([
7170
7366
  "performance_product_ad",
7171
7367
  "premium_dtc_carousel"
7172
7368
  ]);
7173
- var ImageCreativeAspectRatioEnum = z38.enum([
7369
+ var ImageCreativeAspectRatioEnum = z39.enum([
7174
7370
  "1:1",
7175
7371
  "4:5",
7176
7372
  "9:16",
7177
7373
  "16:9"
7178
7374
  ]);
7179
- var ImageRefSchema = z38.object({
7180
- 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.'),
7181
- 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`.')
7375
+ var ImageRefSchema = z39.object({
7376
+ 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.'),
7377
+ 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`.')
7182
7378
  }).refine((v) => Boolean(v.url) !== Boolean(v.path), {
7183
7379
  message: "each image entry must have exactly one of `url` or `path`"
7184
7380
  });
7185
- 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.');
7381
+ 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.');
7186
7382
  function asText(value) {
7187
7383
  return {
7188
7384
  content: [
@@ -7235,14 +7431,14 @@ async function submitCreative(api, opts) {
7235
7431
  });
7236
7432
  }
7237
7433
  __name(submitCreative, "submitCreative");
7238
- var ResearchTargetCountryEnum = z38.enum([
7434
+ var ResearchTargetCountryEnum = z39.enum([
7239
7435
  "CO",
7240
7436
  "MX",
7241
7437
  "PE",
7242
7438
  "EC",
7243
7439
  "CL"
7244
7440
  ]);
7245
- var ResearchCategoryEnum = z38.enum([
7441
+ var ResearchCategoryEnum = z39.enum([
7246
7442
  "auto",
7247
7443
  "automotive",
7248
7444
  "kitchen",
@@ -7288,6 +7484,15 @@ var McpErrorCode = {
7288
7484
  TASK_NOT_READY: "TASK_NOT_READY",
7289
7485
  CREATIVE_VARIANT_FAILED: "CREATIVE_VARIANT_FAILED",
7290
7486
  CREATIVE_VARIANT_DISPATCH_FAILED: "CREATIVE_VARIANT_DISPATCH_FAILED",
7487
+ // Guided dropshipping project / research
7488
+ DROPSHIPPING_PROJECT_NOT_FOUND: "DROPSHIPPING_PROJECT_NOT_FOUND",
7489
+ DROPSHIPPING_PROJECT_CANDIDATE_NOT_READY: "DROPSHIPPING_PROJECT_CANDIDATE_NOT_READY",
7490
+ DROPSHIPPING_PROJECT_VERSION_CONFLICT: "DROPSHIPPING_PROJECT_VERSION_CONFLICT",
7491
+ DROPSHIPPING_PROJECT_MEDIA_REQUIRED: "DROPSHIPPING_PROJECT_MEDIA_REQUIRED",
7492
+ DROPSHIPPING_PROJECT_DISABLED: "DROPSHIPPING_PROJECT_DISABLED",
7493
+ KALODATA_NOT_CONFIGURED: "KALODATA_NOT_CONFIGURED",
7494
+ META_AD_LIBRARY_NOT_CONFIGURED: "META_AD_LIBRARY_NOT_CONFIGURED",
7495
+ PRODUCT_RESEARCH_DISABLED: "PRODUCT_RESEARCH_DISABLED",
7291
7496
  PRODUCT_RESEARCH_IDEMPOTENCY_CONFLICT: "PRODUCT_RESEARCH_IDEMPOTENCY_CONFLICT",
7292
7497
  // Connection presence
7293
7498
  NO_SHOPIFY_SHOPS: "NO_SHOPIFY_SHOPS",
@@ -7298,6 +7503,7 @@ var McpErrorCode = {
7298
7503
  CAMPAIGN_MISSING_LANDING_URL: "CAMPAIGN_MISSING_LANDING_URL",
7299
7504
  CAMPAIGN_NO_CREATIVES: "CAMPAIGN_NO_CREATIVES",
7300
7505
  CAMPAIGN_MISSING_THUMBNAILS: "CAMPAIGN_MISSING_THUMBNAILS",
7506
+ CAMPAIGN_RECONCILIATION_REQUIRED: "CAMPAIGN_RECONCILIATION_REQUIRED",
7301
7507
  // Shopify
7302
7508
  SHOPIFY_NOT_CONNECTED: "SHOPIFY_NOT_CONNECTED",
7303
7509
  SHOPIFY_TOKEN_EXPIRED: "SHOPIFY_TOKEN_EXPIRED",
@@ -7402,6 +7608,7 @@ var CODE_MESSAGES = {
7402
7608
  CAMPAIGN_MISSING_LANDING_URL: "Missing landing URL for the campaign. Publish to Shopify first or pass `landingUrl` explicitly.",
7403
7609
  CAMPAIGN_NO_CREATIVES: "The task has no creatives (video/image) ready for the campaign. Wait for generation to finish.",
7404
7610
  CAMPAIGN_MISSING_THUMBNAILS: "Missing thumbnails for the videos. Verify that image/video generation has finished.",
7611
+ CAMPAIGN_RECONCILIATION_REQUIRED: "A campaign may already have been created. Do not retry creation; reopen the project and reconcile it or contact support.",
7405
7612
  // Connections
7406
7613
  NO_SHOPIFY_SHOPS: "No Shopify stores connected. Connect one at https://productmaker.app/integrations.",
7407
7614
  NO_META_AD_ACCOUNTS: "No Meta Ads accounts connected. Connect Facebook at https://productmaker.app/integrations.",
@@ -7410,6 +7617,14 @@ var CODE_MESSAGES = {
7410
7617
  TASK_NOT_READY: "The task is not ready for this action yet. Wait for it to finish and retry.",
7411
7618
  CREATIVE_VARIANT_FAILED: "The previous creative variant attempt failed. Ask for confirmation before starting a new paid attempt with a new clientRequestId.",
7412
7619
  CREATIVE_VARIANT_DISPATCH_FAILED: "The creative variant could not be queued. Retry as a new paid attempt with a new clientRequestId.",
7620
+ DROPSHIPPING_PROJECT_NOT_FOUND: "Project not found or it does not belong to this user.",
7621
+ DROPSHIPPING_PROJECT_CANDIDATE_NOT_READY: "That research candidate is no longer available. Return to the current research results and choose a fresh candidate.",
7622
+ DROPSHIPPING_PROJECT_VERSION_CONFLICT: "This project changed in another tab. Read it again, review the newer values, and retry with the fresh version.",
7623
+ DROPSHIPPING_PROJECT_MEDIA_REQUIRED: "This project needs a product photo before creating content. Upload one from the guided ingest step.",
7624
+ DROPSHIPPING_PROJECT_DISABLED: "Guided dropshipping projects are temporarily unavailable. Try again later.",
7625
+ KALODATA_NOT_CONFIGURED: "Product research is temporarily unavailable because the trend-data provider is not configured.",
7626
+ META_AD_LIBRARY_NOT_CONFIGURED: "Product research is temporarily unavailable because Meta Ad Library is not configured.",
7627
+ PRODUCT_RESEARCH_DISABLED: "Product research is temporarily unavailable. Try again later.",
7413
7628
  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.",
7414
7629
  // API key
7415
7630
  INVALID_API_KEY: "Invalid API key. Check your PM_API_KEY in the MCP client configuration.",
@@ -7446,6 +7661,11 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
7446
7661
  McpErrorCode.CAMPAIGN_NOT_FOUND,
7447
7662
  McpErrorCode.CAMPAIGN_TASK_NOT_FOUND,
7448
7663
  McpErrorCode.TASK_NOT_FOUND,
7664
+ McpErrorCode.DROPSHIPPING_PROJECT_NOT_FOUND,
7665
+ McpErrorCode.DROPSHIPPING_PROJECT_DISABLED,
7666
+ McpErrorCode.DROPSHIPPING_PROJECT_CANDIDATE_NOT_READY,
7667
+ McpErrorCode.DROPSHIPPING_PROJECT_VERSION_CONFLICT,
7668
+ McpErrorCode.DROPSHIPPING_PROJECT_MEDIA_REQUIRED,
7449
7669
  McpErrorCode.META_TASK_NOT_FOUND,
7450
7670
  McpErrorCode.META_INVALID_AD_ACCOUNT,
7451
7671
  McpErrorCode.META_PIXEL_NOT_FOUND,
@@ -7454,6 +7674,7 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
7454
7674
  McpErrorCode.TIKTOK_ADVERTISER_REQUIRED,
7455
7675
  McpErrorCode.TIKTOK_PIXEL_NOT_FOUND,
7456
7676
  McpErrorCode.CAMPAIGN_MISSING_LANDING_URL,
7677
+ McpErrorCode.CAMPAIGN_RECONCILIATION_REQUIRED,
7457
7678
  McpErrorCode.META_NO_LANDING_URL,
7458
7679
  McpErrorCode.META_DAILY_BUDGET_TOO_HIGH,
7459
7680
  McpErrorCode.META_CAMPAIGN_CONFIG_INVALID,
@@ -7491,6 +7712,14 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
7491
7712
  var PASSTHROUGH_CODES = /* @__PURE__ */ new Set([
7492
7713
  McpErrorCode.INSUFFICIENT_CREDITS,
7493
7714
  McpErrorCode.TASK_QUOTA_EXCEEDED,
7715
+ McpErrorCode.DROPSHIPPING_PROJECT_NOT_FOUND,
7716
+ McpErrorCode.DROPSHIPPING_PROJECT_DISABLED,
7717
+ McpErrorCode.DROPSHIPPING_PROJECT_CANDIDATE_NOT_READY,
7718
+ McpErrorCode.DROPSHIPPING_PROJECT_VERSION_CONFLICT,
7719
+ McpErrorCode.DROPSHIPPING_PROJECT_MEDIA_REQUIRED,
7720
+ McpErrorCode.KALODATA_NOT_CONFIGURED,
7721
+ McpErrorCode.META_AD_LIBRARY_NOT_CONFIGURED,
7722
+ McpErrorCode.PRODUCT_RESEARCH_DISABLED,
7494
7723
  McpErrorCode.CREATIVE_VARIANT_DISPATCH_FAILED,
7495
7724
  McpErrorCode.PRODUCT_RESEARCH_IDEMPOTENCY_CONFLICT,
7496
7725
  // Shopify
@@ -7545,6 +7774,7 @@ var PASSTHROUGH_CODES = /* @__PURE__ */ new Set([
7545
7774
  McpErrorCode.CAMPAIGN_MISSING_LANDING_URL,
7546
7775
  McpErrorCode.CAMPAIGN_NO_CREATIVES,
7547
7776
  McpErrorCode.CAMPAIGN_MISSING_THUMBNAILS,
7777
+ McpErrorCode.CAMPAIGN_RECONCILIATION_REQUIRED,
7548
7778
  // Connections
7549
7779
  McpErrorCode.NO_SHOPIFY_SHOPS,
7550
7780
  McpErrorCode.NO_META_AD_ACCOUNTS,
@@ -7814,66 +8044,67 @@ function renderOutputsMarkdown(outputs) {
7814
8044
  __name(renderOutputsMarkdown, "renderOutputsMarkdown");
7815
8045
 
7816
8046
  // src/tools/tasks.ts
7817
- var CreateProductTaskInput = z39.object({
7818
- images: ImagesInput,
7819
- 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."),
8047
+ var CreateProductTaskInput = z40.object({
8048
+ images: ImagesInput.optional().describe("1\u20135 fotos del mismo producto. Omitir \xFAnicamente cuando projectId apunta a un proyecto propio con imagen guardada."),
8049
+ 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."),
8050
+ 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."),
7820
8051
  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.'),
7821
8052
  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.'),
7822
- 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.'),
7823
- 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".'),
7824
- 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.'),
7825
- 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.'),
7826
- 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."),
7827
- 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.'),
8053
+ 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.'),
8054
+ 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".'),
8055
+ 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.'),
8056
+ 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.'),
8057
+ 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."),
8058
+ 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.'),
7828
8059
  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.'),
7829
- durationSeconds: z39.union([
7830
- z39.literal(15),
7831
- z39.literal(30)
8060
+ durationSeconds: z40.union([
8061
+ z40.literal(15),
8062
+ z40.literal(30)
7832
8063
  ]).default(15).describe("Opcional. Duraci\xF3n objetivo del video: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
7833
- 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."),
8064
+ 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."),
7834
8065
  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.'),
7835
8066
  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).'),
7836
- 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."),
8067
+ 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."),
7837
8068
  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.'),
7838
- outputs: z39.array(z39.enum([
8069
+ outputs: z40.array(z40.enum([
7839
8070
  "landing",
7840
8071
  "video",
7841
8072
  "image"
7842
8073
  ])).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.'),
7843
- 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."),
7844
- 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."),
7845
- 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.'),
8074
+ 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."),
8075
+ 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."),
8076
+ 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.'),
7846
8077
  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".'),
7847
8078
  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".')
7848
8079
  }).strict();
7849
- var GetTaskStatusInput = z39.object({
7850
- taskId: z39.string().min(1),
7851
- waitSeconds: z39.number().int().min(0).max(120).optional()
7852
- });
7853
- var ListTasksInput = z39.object({
7854
- limit: z39.number().int().min(1).max(50).optional(),
7855
- offset: z39.number().int().min(0).optional(),
7856
- search: z39.string().optional()
7857
- });
7858
- var EditTaskDraftInput = z39.object({
7859
- taskId: z39.string().min(1),
7860
- angleIndex: z39.number().int().min(0),
7861
- angle: z39.object({
7862
- angle: z39.string().min(1).max(480),
7863
- buyerPersona: z39.string().min(1).max(360),
7864
- actor: z39.string().min(1).max(360).optional(),
7865
- gender: z39.enum([
8080
+ var GetTaskStatusInput = z40.object({
8081
+ taskId: z40.string().min(1),
8082
+ waitSeconds: z40.number().int().min(0).max(120).optional()
8083
+ });
8084
+ var ListTasksInput = z40.object({
8085
+ limit: z40.number().int().min(1).max(50).optional(),
8086
+ offset: z40.number().int().min(0).optional(),
8087
+ search: z40.string().optional()
8088
+ });
8089
+ var EditTaskDraftInput = z40.object({
8090
+ taskId: z40.string().min(1),
8091
+ angleIndex: z40.number().int().min(0),
8092
+ angle: z40.object({
8093
+ angle: z40.string().min(1).max(480),
8094
+ buyerPersona: z40.string().min(1).max(360),
8095
+ actor: z40.string().min(1).max(360).optional(),
8096
+ gender: z40.enum([
7866
8097
  "female",
7867
8098
  "male"
7868
8099
  ]).optional(),
7869
- scriptNotes: z39.string().max(2e3).optional()
8100
+ scriptNotes: z40.string().max(2e3).optional()
7870
8101
  })
7871
8102
  });
7872
- var PendingActionPayload = z39.discriminatedUnion("kind", [
7873
- z39.object({
7874
- kind: z39.literal("product_reference_confirmation"),
8103
+ var PendingActionPayload = z40.discriminatedUnion("kind", [
8104
+ z40.object({
8105
+ kind: z40.literal("product_reference_confirmation"),
7875
8106
  userChoice: ProductReferenceUserChoiceEnum,
7876
- confirmedProductNames: z39.array(z39.string().min(1).max(120)).min(2).max(8).optional(),
8107
+ confirmedProductNames: z40.array(z40.string().min(1).max(120)).min(2).max(8).optional(),
7877
8108
  // SCOPED multi-scene opt-in — the ONLY way STANDARD ("Multiescena") is
7878
8109
  // reachable via MCP. Valid ONLY together with userChoice='variants' (same
7879
8110
  // product in several colors): renders one shot per color and stitches them,
@@ -7884,62 +8115,62 @@ var PendingActionPayload = z39.discriminatedUnion("kind", [
7884
8115
  // safe because multi-scene IS the intended render for a color variant set,
7885
8116
  // not a blind quality upsell. Maps to `videoTier: 'standard'` on the
7886
8117
  // confirm call; the backend re-gates it (variants + variant_set only).
7887
- 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".')
8118
+ 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".')
7888
8119
  }),
7889
- z39.object({
7890
- kind: z39.literal("angle_review_approval"),
8120
+ z40.object({
8121
+ kind: z40.literal("angle_review_approval"),
7891
8122
  // All edits are optional — empty action body just approves the
7892
8123
  // backend-generated draft as-is. Field shapes mirror the angle-review
7893
8124
  // request contracts in @packages/types/wizard.ts; the handler routes to
7894
8125
  // the dedicated video/image/autopilot endpoint based on StatusResponse.kind.
7895
- customAngle: z39.string().min(1).max(480).optional(),
8126
+ customAngle: z40.string().min(1).max(480).optional(),
7896
8127
  // Keep the shared MCP action within the strictest downstream contract:
7897
8128
  // autopilot/image resume endpoints cap these fields at 1500 characters.
7898
- painPoints: z39.string().max(1500).optional(),
7899
- benefits: z39.string().max(1500).optional(),
7900
- features: z39.array(z39.string().min(1).max(300)).max(10).optional(),
7901
- gapDurationSeconds: z39.union([
7902
- z39.literal(15),
7903
- z39.literal(30)
8129
+ painPoints: z40.string().max(1500).optional(),
8130
+ benefits: z40.string().max(1500).optional(),
8131
+ features: z40.array(z40.string().min(1).max(300)).max(10).optional(),
8132
+ gapDurationSeconds: z40.union([
8133
+ z40.literal(15),
8134
+ z40.literal(30)
7904
8135
  ]).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.")
7905
8136
  })
7906
8137
  ]);
7907
- var ResolvePendingActionInput = z39.object({
7908
- taskId: z39.string().min(1),
8138
+ var ResolvePendingActionInput = z40.object({
8139
+ taskId: z40.string().min(1),
7909
8140
  action: PendingActionPayload
7910
8141
  }).strict();
7911
8142
  function resolveVariantConfirmVideoTier(action) {
7912
8143
  return action.useMultiSceneShowcase && action.userChoice === "variants" ? "standard" : void 0;
7913
8144
  }
7914
8145
  __name(resolveVariantConfirmVideoTier, "resolveVariantConfirmVideoTier");
7915
- var StatusForResolveSchema = z39.object({
8146
+ var StatusForResolveSchema = z40.object({
7916
8147
  // Metadata used to select the backend resume contract. Older API responses
7917
8148
  // may omit these fields, so the resolver keeps the generic fallback.
7918
- kind: z39.enum([
8149
+ kind: z40.enum([
7919
8150
  "autopilot",
7920
8151
  "video",
7921
8152
  "image",
7922
8153
  "landing",
7923
8154
  "mix"
7924
8155
  ]).optional(),
7925
- selectedOutputs: z39.array(z39.enum([
8156
+ selectedOutputs: z40.array(z40.enum([
7926
8157
  "landing",
7927
8158
  "video",
7928
8159
  "image"
7929
8160
  ])).optional(),
7930
- videoAngleCount: z39.number().int().min(1).max(3).optional(),
7931
- ugcFormat: z39.enum([
8161
+ videoAngleCount: z40.number().int().min(1).max(3).optional(),
8162
+ ugcFormat: z40.enum([
7932
8163
  "single_8s",
7933
8164
  "single_15s",
7934
8165
  "double_30s"
7935
8166
  ]).optional(),
7936
- videoStyle: z39.enum([
8167
+ videoStyle: z40.enum([
7937
8168
  "ugc",
7938
8169
  "cartoon_3d",
7939
8170
  "product_only",
7940
8171
  "ai_dynamic"
7941
8172
  ]).optional(),
7942
- videoModel: z39.enum([
8173
+ videoModel: z40.enum([
7943
8174
  "veo",
7944
8175
  "wan",
7945
8176
  "bytedance",
@@ -7947,26 +8178,26 @@ var StatusForResolveSchema = z39.object({
7947
8178
  "grok",
7948
8179
  "kling"
7949
8180
  ]).optional(),
7950
- videoTier: z39.enum([
8181
+ videoTier: z40.enum([
7951
8182
  "legacy",
7952
8183
  "standard",
7953
8184
  "pro",
7954
8185
  "single_call"
7955
8186
  ]).optional(),
7956
- videoResolution: z39.enum([
8187
+ videoResolution: z40.enum([
7957
8188
  "480p",
7958
8189
  "720p"
7959
8190
  ]).optional(),
7960
8191
  narrativeStyle: NarrativeStyleEnum.optional(),
7961
- pendingUserAction: z39.object({
7962
- kind: z39.string(),
7963
- candidateGrouping: z39.string().optional(),
7964
- selectedImageUrls: z39.array(z39.string()).optional(),
7965
- proposedProductNames: z39.array(z39.string()).optional(),
7966
- customAngleDraft: z39.string().optional(),
7967
- painPointsDraft: z39.string().optional(),
7968
- benefitsDraft: z39.string().optional(),
7969
- featuresDraft: z39.array(z39.string()).optional(),
8192
+ pendingUserAction: z40.object({
8193
+ kind: z40.string(),
8194
+ candidateGrouping: z40.string().optional(),
8195
+ selectedImageUrls: z40.array(z40.string()).optional(),
8196
+ proposedProductNames: z40.array(z40.string()).optional(),
8197
+ customAngleDraft: z40.string().optional(),
8198
+ painPointsDraft: z40.string().optional(),
8199
+ benefitsDraft: z40.string().optional(),
8200
+ featuresDraft: z40.array(z40.string()).optional(),
7970
8201
  // Recommendation consumed only when the caller explicitly sends
7971
8202
  // gapDurationSeconds: 30 in the angle approval action.
7972
8203
  gapDurationChoice: GapDurationChoiceMirror.optional()
@@ -8007,11 +8238,14 @@ function resolveStatusTaskKind(status) {
8007
8238
  }
8008
8239
  __name(resolveStatusTaskKind, "resolveStatusTaskKind");
8009
8240
  function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http" } = {}) {
8010
- 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) => {
8241
+ 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) => {
8011
8242
  try {
8012
- const imgs = await resolveImages(input.images, {
8243
+ if (!input.images && !input.projectId) {
8244
+ throw new Error("images es requerido salvo que env\xEDes projectId.");
8245
+ }
8246
+ const imgs = input.images ? await resolveImages(input.images, {
8013
8247
  transport
8014
- });
8248
+ }) : [];
8015
8249
  const parts = imgs.map((img, i) => ({
8016
8250
  ...img,
8017
8251
  filename: `image-${i}.png`
@@ -8022,6 +8256,7 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
8022
8256
  const fields = flatten({
8023
8257
  "options.languageCode": input.language,
8024
8258
  "options.country": input.country,
8259
+ "options.dropshippingProjectId": input.projectId,
8025
8260
  "options.price": input.price,
8026
8261
  "options.compareAtPrice": input.compareAtPrice,
8027
8262
  "options.offer": input.offer,
@@ -8288,54 +8523,54 @@ __name(registerTaskTools, "registerTaskTools");
8288
8523
 
8289
8524
  // src/tools/creatives.ts
8290
8525
  import { randomUUID } from "crypto";
8291
- import { z as z40 } from "zod";
8526
+ import { z as z41 } from "zod";
8292
8527
  var CreativeRefs = {
8293
8528
  images: ImagesInput,
8294
8529
  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.'),
8295
8530
  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.'),
8296
- 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.'),
8297
- 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.'),
8298
- 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".'),
8299
- 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.'),
8300
- 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."),
8301
- 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.'),
8302
- 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.'),
8303
- isCombo: z40.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
8304
- 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."),
8305
- 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."),
8306
- 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.')
8531
+ 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.'),
8532
+ 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.'),
8533
+ 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".'),
8534
+ 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.'),
8535
+ 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."),
8536
+ 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.'),
8537
+ 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.'),
8538
+ isCombo: z41.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
8539
+ 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."),
8540
+ 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."),
8541
+ 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.')
8307
8542
  };
8308
- var VideoInput = z40.object({
8543
+ var VideoInput = z41.object({
8309
8544
  images: CreativeRefs.images,
8310
- title: z40.string().min(1).describe('T\xEDtulo corto del producto. Aparece en la primera vista del video. Ej. "Crema Anti-Edad Premium".'),
8311
- 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".'),
8545
+ title: z41.string().min(1).describe('T\xEDtulo corto del producto. Aparece en la primera vista del video. Ej. "Crema Anti-Edad Premium".'),
8546
+ 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".'),
8312
8547
  language: CreativeRefs.language,
8313
8548
  country: CreativeRefs.country,
8314
- aspectRatio: z40.enum([
8549
+ aspectRatio: z41.enum([
8315
8550
  "9:16",
8316
8551
  "16:9",
8317
8552
  "Auto"
8318
8553
  ]).describe('Relaci\xF3n de aspecto. "9:16" = vertical (TikTok/Reels \u2014 default recomendado para ads). "16:9" = horizontal (YouTube). "Auto" = decide la IA.'),
8319
- durationSeconds: z40.union([
8320
- z40.literal(15),
8321
- z40.literal(30)
8554
+ durationSeconds: z41.union([
8555
+ z41.literal(15),
8556
+ z41.literal(30)
8322
8557
  ]).default(15).describe("Opcional. Duraci\xF3n objetivo: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
8323
8558
  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).'),
8324
8559
  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.'),
8325
8560
  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).'),
8326
- 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"].'),
8561
+ 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"].'),
8327
8562
  price: CreativeRefs.price,
8328
8563
  offer: CreativeRefs.offer,
8329
8564
  productContext: CreativeRefs.productContext,
8330
8565
  productLabel: CreativeRefs.productLabel,
8331
- 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.'),
8332
- 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.'),
8333
- 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."),
8334
- 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."),
8566
+ 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.'),
8567
+ 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.'),
8568
+ 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."),
8569
+ 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."),
8335
8570
  competitorScanEnabled: CreativeRefs.competitorScanEnabled,
8336
8571
  brandId: CreativeRefs.brandId
8337
8572
  }).strict();
8338
- var FreeVideoQualityEnum = z40.enum([
8573
+ var FreeVideoQualityEnum = z41.enum([
8339
8574
  "economical",
8340
8575
  "high_detail"
8341
8576
  ]);
@@ -8357,38 +8592,38 @@ var FREE_VIDEO_DURATIONS_BY_QUALITY = {
8357
8592
  15
8358
8593
  ]
8359
8594
  };
8360
- var FreeVideoInput = z40.object({
8595
+ var FreeVideoInput = z41.object({
8361
8596
  images: ImagesInput.optional().describe("Opcional. 1-5 fotos del producto. Om\xEDtelo para generar desde texto puro."),
8362
- 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."),
8597
+ 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."),
8363
8598
  quality: FreeVideoQualityEnum.describe('Calidad del video: "economical" = Econ\xF3mico o "high_detail" = Alto detalle.'),
8364
- 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."),
8599
+ 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."),
8365
8600
  videoResolution: VideoResolutionEnum.describe("Resoluci\xF3n del video: 480p o 720p."),
8366
- generateAiMusic: z40.boolean().default(false).describe("Opcional. Agrega m\xFAsica instrumental IA debajo del audio nativo. Default false."),
8367
- addSubtitles: z40.boolean().default(false).describe("Opcional. Quema subt\xEDtulos usando el rawPrompt exacto. Default false."),
8368
- 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.")
8601
+ generateAiMusic: z41.boolean().default(false).describe("Opcional. Agrega m\xFAsica instrumental IA debajo del audio nativo. Default false."),
8602
+ addSubtitles: z41.boolean().default(false).describe("Opcional. Quema subt\xEDtulos usando el rawPrompt exacto. Default false."),
8603
+ 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.")
8369
8604
  }).strict();
8370
8605
  var imageStyleList = ImageCreativeStyleEnum.options.join(" | ");
8371
8606
  var archetypeList = ImageCreativeArchetypeEnum.options.join(" | ");
8372
8607
  var visualTreatmentList = ImageCreativeVisualTreatmentEnum.options.join(" | ");
8373
8608
  var aspectRatioList = ImageCreativeAspectRatioEnum.options.join(" | ");
8374
- var ImageInput = z40.object({
8609
+ var ImageInput = z41.object({
8375
8610
  images: CreativeRefs.images,
8376
- productTitle: z40.string().min(1).describe('Nombre del producto. Ej. "Crema Anti-Edad Premium".'),
8377
- primaryAngle: z40.string().min(1).describe('\xC1ngulo de venta principal. Ej. "Reduce arrugas en 14 d\xEDas".'),
8611
+ productTitle: z41.string().min(1).describe('Nombre del producto. Ej. "Crema Anti-Edad Premium".'),
8612
+ primaryAngle: z41.string().min(1).describe('\xC1ngulo de venta principal. Ej. "Reduce arrugas en 14 d\xEDas".'),
8378
8613
  buyerPersona: CreativeRefs.buyerPersona,
8379
8614
  language: CreativeRefs.language,
8380
8615
  country: CreativeRefs.country,
8381
- nVariants: z40.number().int().min(1).max(6).optional().describe("Opcional. N\xFAmero de variantes a generar (1-6). Default 3."),
8382
- 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.'),
8616
+ nVariants: z41.number().int().min(1).max(6).optional().describe("Opcional. N\xFAmero de variantes a generar (1-6). Default 3."),
8617
+ 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.'),
8383
8618
  imageStyle: ImageCreativeStyleEnum.optional().describe(`Opcional. Estilo del shot del producto. Valores: ${imageStyleList}. Si se omite, la IA escoge seg\xFAn el archetype.`),
8384
8619
  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).`),
8385
8620
  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.`),
8386
- 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."),
8621
+ 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."),
8387
8622
  aspectRatio: ImageCreativeAspectRatioEnum.optional().describe(`Opcional. Relaci\xF3n de aspecto. Valores: ${aspectRatioList}. Default "4:5" (vertical Instagram feed).`),
8388
- generateAdCopy: z40.boolean().optional().describe("Opcional. Si true, tambi\xE9n genera headline/body/CTA para el ad. Default false."),
8389
- 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."),
8390
- features: z40.array(z40.string()).optional().describe('Opcional. Features/beneficios a destacar visualmente. Ej. ["resistente al agua", "ultra liviano"].'),
8391
- 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.'),
8623
+ generateAdCopy: z41.boolean().optional().describe("Opcional. Si true, tambi\xE9n genera headline/body/CTA para el ad. Default false."),
8624
+ 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."),
8625
+ features: z41.array(z41.string()).optional().describe('Opcional. Features/beneficios a destacar visualmente. Ej. ["resistente al agua", "ultra liviano"].'),
8626
+ 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.'),
8392
8627
  price: CreativeRefs.price,
8393
8628
  compareAtPrice: CreativeRefs.compareAtPrice,
8394
8629
  offer: CreativeRefs.offer,
@@ -8399,11 +8634,11 @@ var ImageInput = z40.object({
8399
8634
  brandId: CreativeRefs.brandId,
8400
8635
  creativeDirection: CreativeRefs.creativeDirection
8401
8636
  });
8402
- var LandingInput = z40.object({
8637
+ var LandingInput = z41.object({
8403
8638
  images: CreativeRefs.images,
8404
8639
  language: CreativeRefs.language,
8405
8640
  country: CreativeRefs.country,
8406
- 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.'),
8641
+ 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.'),
8407
8642
  buyerPersona: CreativeRefs.buyerPersona,
8408
8643
  price: CreativeRefs.price,
8409
8644
  compareAtPrice: CreativeRefs.compareAtPrice,
@@ -8501,8 +8736,8 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
8501
8736
  __name(registerCreativeTools, "registerCreativeTools");
8502
8737
 
8503
8738
  // src/tools/creative-variants.ts
8504
- import { z as z41 } from "zod";
8505
- var LandingSectionKeyEnum = z41.enum([
8739
+ import { z as z42 } from "zod";
8740
+ var LandingSectionKeyEnum = z42.enum([
8506
8741
  "problem_hook",
8507
8742
  "problem_cost",
8508
8743
  "solution_mechanism",
@@ -8542,24 +8777,24 @@ var LandingSectionKeyEnum = z41.enum([
8542
8777
  "lifestyle_fit",
8543
8778
  "join_lifestyle"
8544
8779
  ]);
8545
- var CreativeVariantKindEnum = z41.enum([
8780
+ var CreativeVariantKindEnum = z42.enum([
8546
8781
  "video",
8547
8782
  "image",
8548
8783
  "landing"
8549
8784
  ]);
8550
- var EstimateCreativeVariantInputBase = z41.object({
8551
- sourceTaskId: z41.string().uuid().describe("ID de la tarea original completada."),
8785
+ var EstimateCreativeVariantInputBase = z42.object({
8786
+ sourceTaskId: z42.string().uuid().describe("ID de la tarea original completada."),
8552
8787
  kind: CreativeVariantKindEnum,
8553
8788
  videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Solo cambia el preset p\xFAblico; el servidor conserva el modelo/tier del task origen."),
8554
8789
  videoResolution: VideoResolutionEnum.optional().describe("Opcional para video. Resoluci\xF3n del nuevo render."),
8555
- nVariants: z41.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
8790
+ nVariants: z42.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
8556
8791
  sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si ya conoces la secci\xF3n, evita cotizar la clasificaci\xF3n con IA."),
8557
- 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.")
8792
+ 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.")
8558
8793
  }).strict();
8559
8794
  var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine((input, ctx) => {
8560
8795
  if (input.kind === "video" && input.nVariants !== void 0) {
8561
8796
  ctx.addIssue({
8562
- code: z41.ZodIssueCode.custom,
8797
+ code: z42.ZodIssueCode.custom,
8563
8798
  path: [
8564
8799
  "nVariants"
8565
8800
  ],
@@ -8569,7 +8804,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
8569
8804
  if (input.kind !== "video") {
8570
8805
  if (input.videoPreset !== void 0) {
8571
8806
  ctx.addIssue({
8572
- code: z41.ZodIssueCode.custom,
8807
+ code: z42.ZodIssueCode.custom,
8573
8808
  path: [
8574
8809
  "videoPreset"
8575
8810
  ],
@@ -8578,7 +8813,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
8578
8813
  }
8579
8814
  if (input.videoResolution !== void 0) {
8580
8815
  ctx.addIssue({
8581
- code: z41.ZodIssueCode.custom,
8816
+ code: z42.ZodIssueCode.custom,
8582
8817
  path: [
8583
8818
  "videoResolution"
8584
8819
  ],
@@ -8588,7 +8823,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
8588
8823
  }
8589
8824
  if (input.kind !== "image" && input.nVariants !== void 0) {
8590
8825
  ctx.addIssue({
8591
- code: z41.ZodIssueCode.custom,
8826
+ code: z42.ZodIssueCode.custom,
8592
8827
  path: [
8593
8828
  "nVariants"
8594
8829
  ],
@@ -8598,7 +8833,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
8598
8833
  if (input.kind !== "landing") {
8599
8834
  if (input.sectionKey !== void 0) {
8600
8835
  ctx.addIssue({
8601
- code: z41.ZodIssueCode.custom,
8836
+ code: z42.ZodIssueCode.custom,
8602
8837
  path: [
8603
8838
  "sectionKey"
8604
8839
  ],
@@ -8607,7 +8842,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
8607
8842
  }
8608
8843
  if (input.instructions !== void 0) {
8609
8844
  ctx.addIssue({
8610
- code: z41.ZodIssueCode.custom,
8845
+ code: z42.ZodIssueCode.custom,
8611
8846
  path: [
8612
8847
  "instructions"
8613
8848
  ],
@@ -8616,31 +8851,31 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
8616
8851
  }
8617
8852
  }
8618
8853
  });
8619
- var CreateCreativeVariantInputBase = z41.object({
8620
- sourceTaskId: z41.string().uuid().describe("ID de la tarea original completada."),
8854
+ var CreateCreativeVariantInputBase = z42.object({
8855
+ sourceTaskId: z42.string().uuid().describe("ID de la tarea original completada."),
8621
8856
  kind: CreativeVariantKindEnum,
8622
- confirm: z41.literal(true).describe("Confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrarle el estimado de cr\xE9ditos."),
8623
- 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."),
8624
- angle: z41.string().trim().min(1).max(500).optional().describe("\xC1ngulo nuevo para video o imagen."),
8625
- buyerPersona: z41.string().trim().min(1).max(360).optional().describe("Buyer persona coherente con el \xE1ngulo."),
8626
- scriptNotes: z41.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n breve del guion para una variante de video."),
8627
- creativeDirection: z41.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n creativa para una variante de imagen."),
8857
+ confirm: z42.literal(true).describe("Confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrarle el estimado de cr\xE9ditos."),
8858
+ 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."),
8859
+ angle: z42.string().trim().min(1).max(500).optional().describe("\xC1ngulo nuevo para video o imagen."),
8860
+ buyerPersona: z42.string().trim().min(1).max(360).optional().describe("Buyer persona coherente con el \xE1ngulo."),
8861
+ scriptNotes: z42.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n breve del guion para una variante de video."),
8862
+ creativeDirection: z42.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n creativa para una variante de imagen."),
8628
8863
  videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Preset p\xFAblico; el servidor resuelve modelo/tier desde el task origen."),
8629
8864
  videoResolution: VideoResolutionEnum.optional().describe("Opcional para video."),
8630
- generateAiMusic: z41.boolean().optional().describe("Opcional para video."),
8631
- nVariants: z41.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
8865
+ generateAiMusic: z42.boolean().optional().describe("Opcional para video."),
8866
+ nVariants: z42.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
8632
8867
  imageStyle: ImageCreativeStyleEnum.optional().describe("Opcional para imagen."),
8633
8868
  creativeArchetype: ImageCreativeArchetypeEnum.optional().describe("Opcional para imagen."),
8634
8869
  visualTreatment: ImageCreativeVisualTreatmentEnum.optional().describe("Opcional para imagen."),
8635
8870
  aspectRatio: ImageCreativeAspectRatioEnum.optional().describe("Opcional para imagen."),
8636
- styleMix: z41.boolean().optional().describe("Opcional para imagen."),
8871
+ styleMix: z42.boolean().optional().describe("Opcional para imagen."),
8637
8872
  sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si se omite, se clasifica con IA a partir de instructions."),
8638
- 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.")
8873
+ 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.")
8639
8874
  }).strict();
8640
8875
  var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((input, ctx) => {
8641
8876
  if ((input.kind === "video" || input.kind === "image") && !input.angle) {
8642
8877
  ctx.addIssue({
8643
- code: z41.ZodIssueCode.custom,
8878
+ code: z42.ZodIssueCode.custom,
8644
8879
  path: [
8645
8880
  "angle"
8646
8881
  ],
@@ -8649,7 +8884,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
8649
8884
  }
8650
8885
  if (input.kind === "landing" && !input.sectionKey && !input.instructions) {
8651
8886
  ctx.addIssue({
8652
- code: z41.ZodIssueCode.custom,
8887
+ code: z42.ZodIssueCode.custom,
8653
8888
  path: [
8654
8889
  "instructions"
8655
8890
  ],
@@ -8695,7 +8930,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
8695
8930
  for (const field of fieldsByKind[input.kind]) {
8696
8931
  if (input[field] !== void 0) {
8697
8932
  ctx.addIssue({
8698
- code: z41.ZodIssueCode.custom,
8933
+ code: z42.ZodIssueCode.custom,
8699
8934
  path: [
8700
8935
  field
8701
8936
  ],
@@ -8812,27 +9047,27 @@ function registerCreativeVariantTools(server, api) {
8812
9047
  __name(registerCreativeVariantTools, "registerCreativeVariantTools");
8813
9048
 
8814
9049
  // src/tools/script-review.ts
8815
- import { z as z42 } from "zod";
8816
- var GetVideoScriptInput = z42.object({
8817
- taskId: z42.string().min(1).describe("ID de la tarea (la misma que devolvi\xF3 create_product_task / generate_video_creative)."),
8818
- 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.')
9050
+ import { z as z43 } from "zod";
9051
+ var GetVideoScriptInput = z43.object({
9052
+ taskId: z43.string().min(1).describe("ID de la tarea (la misma que devolvi\xF3 create_product_task / generate_video_creative)."),
9053
+ 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.')
8819
9054
  }).strict();
8820
9055
  var VIDEO_SCRIPT_APPROVAL_MAX_SCENES2 = 50;
8821
9056
  var VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2 = 2e3;
8822
- var ApproveSceneEdit = z42.object({
8823
- index: z42.number().int().nonnegative().describe("\xCDndice de la escena (0-based, mismo que devuelve get_video_script)."),
8824
- dialogue: z42.string().min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2).refine((v) => v.trim().length > 0, {
9057
+ var ApproveSceneEdit = z43.object({
9058
+ index: z43.number().int().nonnegative().describe("\xCDndice de la escena (0-based, mismo que devuelve get_video_script)."),
9059
+ dialogue: z43.string().min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2).refine((v) => v.trim().length > 0, {
8825
9060
  message: "dialogue no puede ser s\xF3lo espacios"
8826
9061
  }).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.")
8827
9062
  }).strict();
8828
- var ApproveVideoScriptInput = z42.object({
8829
- taskId: z42.string().min(1).describe("ID de la tarea."),
8830
- 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.'),
8831
- 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.")
9063
+ var ApproveVideoScriptInput = z43.object({
9064
+ taskId: z43.string().min(1).describe("ID de la tarea."),
9065
+ 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.'),
9066
+ 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.")
8832
9067
  }).strict();
8833
- var RegenerateVideoScriptInput = z42.object({
8834
- taskId: z42.string().min(1).describe("ID de la tarea."),
8835
- 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.'),
9068
+ var RegenerateVideoScriptInput = z43.object({
9069
+ taskId: z43.string().min(1).describe("ID de la tarea."),
9070
+ 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.'),
8836
9071
  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).')
8837
9072
  }).strict();
8838
9073
  function registerScriptReviewTools(server, api) {
@@ -8872,9 +9107,9 @@ function registerScriptReviewTools(server, api) {
8872
9107
  __name(registerScriptReviewTools, "registerScriptReviewTools");
8873
9108
 
8874
9109
  // src/tools/connections.ts
8875
- import { z as z43 } from "zod";
9110
+ import { z as z44 } from "zod";
8876
9111
  function registerConnectionTools(server, api) {
8877
- 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 () => {
9112
+ 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 () => {
8878
9113
  try {
8879
9114
  const r = await api.getJson("/v1/shopify/shops");
8880
9115
  return asText(r);
@@ -8882,10 +9117,10 @@ function registerConnectionTools(server, api) {
8882
9117
  throw translateError(e);
8883
9118
  }
8884
9119
  });
8885
- 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({
8886
- shopDomain: z43.string().min(3).max(120),
8887
- query: z43.string().trim().max(120).optional(),
8888
- taskId: z43.string().uuid().optional()
9120
+ 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({
9121
+ shopDomain: z44.string().min(3).max(120),
9122
+ query: z44.string().trim().max(120).optional(),
9123
+ taskId: z44.string().uuid().optional()
8889
9124
  }).shape, async ({ shopDomain, query, taskId }) => {
8890
9125
  try {
8891
9126
  const params = new URLSearchParams({
@@ -8899,7 +9134,7 @@ function registerConnectionTools(server, api) {
8899
9134
  throw translateError(e);
8900
9135
  }
8901
9136
  });
8902
- 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 () => {
9137
+ 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 () => {
8903
9138
  try {
8904
9139
  const r = await api.getJson("/v1/facebook/ad-accounts");
8905
9140
  return asText(r);
@@ -8907,7 +9142,7 @@ function registerConnectionTools(server, api) {
8907
9142
  throw translateError(e);
8908
9143
  }
8909
9144
  });
8910
- 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 () => {
9145
+ 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 () => {
8911
9146
  try {
8912
9147
  const r = await api.getJson("/v1/tiktok/advertisers");
8913
9148
  return asText(r);
@@ -8919,105 +9154,105 @@ function registerConnectionTools(server, api) {
8919
9154
  __name(registerConnectionTools, "registerConnectionTools");
8920
9155
 
8921
9156
  // src/tools/publish.ts
8922
- import { z as z44 } from "zod";
8923
- var ShopifyInput = z44.object({
8924
- taskId: z44.string().min(1),
8925
- shopDomain: z44.string().min(1),
9157
+ import { z as z45 } from "zod";
9158
+ var ShopifyInput = z45.object({
9159
+ taskId: z45.string().min(1),
9160
+ shopDomain: z45.string().min(1),
8926
9161
  /** Override: number of landing images to include. Default = ALL images the
8927
9162
  * task generated (the MCP fetches /v1/results first to count). Bounded
8928
9163
  * generously here; the backend validates each index against
8929
9164
  * landing.images.length and rejects out-of-range. */
8930
- numLandingImages: z44.number().int().min(1).max(20).optional(),
8931
- variantIndex: z44.number().int().min(0).optional(),
9165
+ numLandingImages: z45.number().int().min(1).max(20).optional(),
9166
+ variantIndex: z45.number().int().min(0).optional(),
8932
9167
  /** ASK THE USER before setting: "¿Tu producto tiene variantes (talla, color, etc.)?".
8933
9168
  * When true, the landing renders variant-picker pills; when false, it ships
8934
9169
  * a single-option product page. Defaults to false. */
8935
- showVariantPicker: z44.boolean().optional(),
9170
+ showVariantPicker: z45.boolean().optional(),
8936
9171
  /** Default `auto` updates the product previously published for this task and
8937
9172
  * shop. Use `existing` with targetProductId to choose another product, or
8938
9173
  * `new` only when the user explicitly wants a duplicate/new listing. */
8939
- publicationMode: z44.enum([
9174
+ publicationMode: z45.enum([
8940
9175
  "auto",
8941
9176
  "existing",
8942
9177
  "new"
8943
9178
  ]).optional(),
8944
9179
  /** Shopify product GID, numeric id, or admin product URL. Required when
8945
9180
  * publicationMode is `existing`. */
8946
- targetProductId: z44.string().trim().min(1).max(300).optional()
9181
+ targetProductId: z45.string().trim().min(1).max(300).optional()
8947
9182
  });
8948
- var McpCampaignConfig = z44.object({
9183
+ var McpCampaignConfig = z45.object({
8949
9184
  /** ALWAYS in US dollars, whatever the ad account's currency is. ProductMaker
8950
9185
  * converts to the account currency server-side. Do NOT pre-convert: on a COP
8951
9186
  * account the user's "60.000 al día" is `dailyBudgetUsd: 15`, not 60000. */
8952
- dailyBudgetUsd: z44.number().positive().optional(),
9187
+ dailyBudgetUsd: z45.number().positive().optional(),
8953
9188
  /** ISO-3166-1 alpha-2 country codes. Meta accepts max 1 (single-country
8954
9189
  * ad set). TikTok takes only the first entry. */
8955
- targetCountries: z44.array(z44.string().length(2)).max(1).optional(),
8956
- campaignName: z44.string().trim().min(1).max(100).optional()
9190
+ targetCountries: z45.array(z45.string().length(2)).max(1).optional(),
9191
+ campaignName: z45.string().trim().min(1).max(100).optional()
8957
9192
  });
8958
9193
  var McpAdAccountId = MetaAdAccountId.describe('Meta ad account id. Accepts "act_1234567890" or the bare numeric id "1234567890".');
8959
- var MetaInput = z44.object({
8960
- taskId: z44.string().min(1),
9194
+ var MetaInput = z45.object({
9195
+ taskId: z45.string().min(1),
8961
9196
  adAccountId: McpAdAccountId,
8962
- pixelId: z44.string().min(1).optional(),
8963
- landingUrl: z44.string().url().optional(),
9197
+ pixelId: z45.string().min(1).optional(),
9198
+ landingUrl: z45.string().url().optional(),
8964
9199
  /** Select which angles to publish by 0-based index. Omit to publish all
8965
9200
  * successful angles. Example: a task with 3 angles → `[0,2]` publishes #0 + #2. */
8966
- angleIndexes: z44.array(z44.number().int().nonnegative()).optional(),
9201
+ angleIndexes: z45.array(z45.number().int().nonnegative()).optional(),
8967
9202
  campaignConfig: McpCampaignConfig.optional(),
8968
9203
  /** Override the ad's primary text (body copy). If omitted, ProductMaker
8969
9204
  * uses the auto-generated copy attached to the task. Capped at 500 chars
8970
9205
  * (Meta's primary-text limit). */
8971
- primaryText: z44.string().trim().min(1).max(500).optional(),
9206
+ primaryText: z45.string().trim().min(1).max(500).optional(),
8972
9207
  /** Optional WhatsApp number. When set, the ad button opens a WhatsApp chat
8973
9208
  * (wa.me) instead of a landing page. */
8974
- phoneNumber: z44.string().min(8).max(32).optional()
9209
+ phoneNumber: z45.string().min(8).max(32).optional()
8975
9210
  });
8976
- var GroupedTaskSource = z44.object({
8977
- type: z44.literal("task"),
8978
- taskId: z44.string().min(1),
8979
- angleIndex: z44.number().int().min(0).optional(),
8980
- videoVariant: z44.enum([
9211
+ var GroupedTaskSource = z45.object({
9212
+ type: z45.literal("task"),
9213
+ taskId: z45.string().min(1),
9214
+ angleIndex: z45.number().int().min(0).optional(),
9215
+ videoVariant: z45.enum([
8981
9216
  "with_subtitles",
8982
9217
  "without_subtitles"
8983
9218
  ]).optional(),
8984
- imageVariantIndex: z44.number().int().min(0).optional()
8985
- });
8986
- var GroupedMediaSource = z44.object({
8987
- type: z44.literal("media"),
8988
- url: z44.string().url().optional(),
8989
- path: z44.string().min(1).optional(),
8990
- thumbnailUrl: z44.string().url().optional(),
8991
- thumbnailPath: z44.string().min(1).optional()
9219
+ imageVariantIndex: z45.number().int().min(0).optional()
9220
+ });
9221
+ var GroupedMediaSource = z45.object({
9222
+ type: z45.literal("media"),
9223
+ url: z45.string().url().optional(),
9224
+ path: z45.string().min(1).optional(),
9225
+ thumbnailUrl: z45.string().url().optional(),
9226
+ thumbnailPath: z45.string().min(1).optional()
8992
9227
  }).refine((value) => Boolean(value.url) !== Boolean(value.path), {
8993
9228
  message: "media source needs exactly one of `url` or `path`"
8994
9229
  }).refine((value) => !(value.thumbnailUrl && value.thumbnailPath), {
8995
9230
  message: "media source needs at most one of `thumbnailUrl` or `thumbnailPath`"
8996
9231
  });
8997
- var GroupedCreative = z44.object({
8998
- type: z44.enum([
9232
+ var GroupedCreative = z45.object({
9233
+ type: z45.enum([
8999
9234
  "image",
9000
9235
  "video"
9001
9236
  ]),
9002
- source: z44.union([
9237
+ source: z45.union([
9003
9238
  GroupedTaskSource,
9004
9239
  GroupedMediaSource
9005
9240
  ]),
9006
- title: z44.string().max(120).optional(),
9007
- description: z44.string().max(500).optional(),
9241
+ title: z45.string().max(120).optional(),
9242
+ description: z45.string().max(500).optional(),
9008
9243
  cta: MetaCtaType.optional(),
9009
- creativeId: z44.string().min(1).optional()
9244
+ creativeId: z45.string().min(1).optional()
9010
9245
  });
9011
- var GroupedCampaignConfig = z44.object({
9246
+ var GroupedCampaignConfig = z45.object({
9012
9247
  campaignType: MetaCampaignType.default("sales"),
9013
9248
  optimizationMode: MetaOptimizationMode.default("advantage_plus"),
9014
9249
  budgetMode: MetaBudgetMode.default("campaign"),
9015
- campaignDailyBudget: z44.number().int().positive().optional(),
9016
- adSets: z44.array(z44.object({
9017
- tempId: z44.string().min(1).max(80),
9018
- name: z44.string().min(1).max(100),
9019
- dailyBudget: z44.number().int().positive().optional(),
9020
- creativeIds: z44.array(z44.string().min(1)).min(1)
9250
+ campaignDailyBudget: z45.number().int().positive().optional(),
9251
+ adSets: z45.array(z45.object({
9252
+ tempId: z45.string().min(1).max(80),
9253
+ name: z45.string().min(1).max(100),
9254
+ dailyBudget: z45.number().int().positive().optional(),
9255
+ creativeIds: z45.array(z45.string().min(1)).min(1)
9021
9256
  })).min(1).max(CAMPAIGN_LIMITS.MAX_AD_SETS),
9022
9257
  /**
9023
9258
  * Valid values are 'landing' and 'whatsapp' — there is no 'website'. This
@@ -9025,22 +9260,22 @@ var GroupedCampaignConfig = z44.object({
9025
9260
  * opaque `invalid_literal` and made the WhatsApp destination — supported by
9026
9261
  * the API since #849 — unreachable from this tool.
9027
9262
  */
9028
- destination: z44.discriminatedUnion("type", [
9029
- z44.object({
9030
- type: z44.literal("landing"),
9031
- landingUrl: z44.string().url()
9263
+ destination: z45.discriminatedUnion("type", [
9264
+ z45.object({
9265
+ type: z45.literal("landing"),
9266
+ landingUrl: z45.string().url()
9032
9267
  }),
9033
- z44.object({
9034
- type: z44.literal("whatsapp"),
9035
- phoneNumber: z44.string().min(8).max(32)
9268
+ z45.object({
9269
+ type: z45.literal("whatsapp"),
9270
+ phoneNumber: z45.string().min(8).max(32)
9036
9271
  })
9037
9272
  ]),
9038
- saveAsDefault: z44.boolean().default(false),
9273
+ saveAsDefault: z45.boolean().default(false),
9039
9274
  budgetPolicy: MetaBudgetPolicy.optional()
9040
9275
  }).superRefine((value, ctx) => {
9041
9276
  if (value.budgetMode === "campaign" && value.campaignDailyBudget === void 0) {
9042
9277
  ctx.addIssue({
9043
- code: z44.ZodIssueCode.custom,
9278
+ code: z45.ZodIssueCode.custom,
9044
9279
  path: [
9045
9280
  "campaignDailyBudget"
9046
9281
  ],
@@ -9051,7 +9286,7 @@ var GroupedCampaignConfig = z44.object({
9051
9286
  value.adSets.forEach((adSet, index) => {
9052
9287
  if (adSet.dailyBudget === void 0) {
9053
9288
  ctx.addIssue({
9054
- code: z44.ZodIssueCode.custom,
9289
+ code: z45.ZodIssueCode.custom,
9055
9290
  path: [
9056
9291
  "adSets",
9057
9292
  index,
@@ -9063,22 +9298,22 @@ var GroupedCampaignConfig = z44.object({
9063
9298
  });
9064
9299
  }
9065
9300
  });
9066
- var PublishCreativesToMetaInputBase = z44.object({
9067
- creatives: z44.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
9301
+ var PublishCreativesToMetaInputBase = z45.object({
9302
+ creatives: z45.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
9068
9303
  adAccountId: McpAdAccountId,
9069
- pixelId: z44.string().min(1).optional(),
9304
+ pixelId: z45.string().min(1).optional(),
9070
9305
  /** Required unless `campaignConfig.destination` is whatsapp — see the
9071
9306
  * superRefine below. Mirrors the API's `UploadToMetaRequest`. */
9072
- landingUrl: z44.string().url().optional(),
9073
- targetCountry: z44.string().length(2),
9074
- campaignName: z44.string().trim().min(1).max(100),
9307
+ landingUrl: z45.string().url().optional(),
9308
+ targetCountry: z45.string().length(2),
9309
+ campaignName: z45.string().trim().min(1).max(100),
9075
9310
  campaignConfig: GroupedCampaignConfig.optional()
9076
9311
  });
9077
9312
  var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((value, ctx) => {
9078
9313
  const destination = value.campaignConfig?.destination;
9079
9314
  if (destination?.type !== "whatsapp" && !value.landingUrl) {
9080
9315
  ctx.addIssue({
9081
- code: z44.ZodIssueCode.custom,
9316
+ code: z45.ZodIssueCode.custom,
9082
9317
  path: [
9083
9318
  "landingUrl"
9084
9319
  ],
@@ -9087,7 +9322,7 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
9087
9322
  }
9088
9323
  if (destination?.type === "landing" && destination.landingUrl !== value.landingUrl) {
9089
9324
  ctx.addIssue({
9090
- code: z44.ZodIssueCode.custom,
9325
+ code: z45.ZodIssueCode.custom,
9091
9326
  path: [
9092
9327
  "campaignConfig",
9093
9328
  "destination",
@@ -9097,12 +9332,12 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
9097
9332
  });
9098
9333
  }
9099
9334
  });
9100
- var PresignUploadResponseSchema = z44.object({
9101
- uploadUrl: z44.string().url().refine((value) => {
9335
+ var PresignUploadResponseSchema = z45.object({
9336
+ uploadUrl: z45.string().url().refine((value) => {
9102
9337
  const url = new URL(value);
9103
9338
  return !url.username && !url.password && url.protocol === "https:";
9104
9339
  }, "presigned upload URL must be HTTPS without embedded credentials"),
9105
- publicUrl: z44.string().url().refine((value) => {
9340
+ publicUrl: z45.string().url().refine((value) => {
9106
9341
  const url = new URL(value);
9107
9342
  return !url.username && !url.password;
9108
9343
  }, "public media URL must not contain embedded credentials")
@@ -9127,16 +9362,16 @@ async function resolveAndUploadMedia(api, ref, expectedKind, transport) {
9127
9362
  return uploadResolvedMedia(api, media);
9128
9363
  }
9129
9364
  __name(resolveAndUploadMedia, "resolveAndUploadMedia");
9130
- var TikTokInput = z44.object({
9131
- taskId: z44.string().min(1),
9132
- advertiserId: z44.string().min(1),
9133
- pixelId: z44.string().min(1).optional(),
9134
- landingUrl: z44.string().url().optional(),
9135
- angleIndexes: z44.array(z44.number().int().nonnegative()).optional(),
9365
+ var TikTokInput = z45.object({
9366
+ taskId: z45.string().min(1),
9367
+ advertiserId: z45.string().min(1),
9368
+ pixelId: z45.string().min(1).optional(),
9369
+ landingUrl: z45.string().url().optional(),
9370
+ angleIndexes: z45.array(z45.number().int().nonnegative()).optional(),
9136
9371
  campaignConfig: McpCampaignConfig.optional(),
9137
9372
  /** Override the ad's description (TikTok's body copy). If omitted,
9138
9373
  * ProductMaker uses the auto-generated copy attached to the task. */
9139
- primaryText: z44.string().trim().min(1).max(500).optional()
9374
+ primaryText: z45.string().trim().min(1).max(500).optional()
9140
9375
  });
9141
9376
  function registerPublishTools(server, api, opts = {}) {
9142
9377
  const transport = opts.transport ?? "http";
@@ -9347,13 +9582,13 @@ function toTikTokCampaignConfig(cfg) {
9347
9582
  __name(toTikTokCampaignConfig, "toTikTokCampaignConfig");
9348
9583
 
9349
9584
  // src/tools/campaigns.ts
9350
- import { z as z45 } from "zod";
9351
- var MetaStatusInput = z45.object({
9352
- campaignId: z45.string().min(1).describe("Meta campaign ID returned by publish_to_meta.")
9585
+ import { z as z46 } from "zod";
9586
+ var MetaStatusInput = z46.object({
9587
+ campaignId: z46.string().min(1).describe("Meta campaign ID returned by publish_to_meta.")
9353
9588
  });
9354
- var TikTokStatusInput = z45.object({
9355
- campaignId: z45.string().min(1).describe("TikTok campaign ID returned by publish_to_tiktok."),
9356
- advertiserId: z45.string().min(1).describe("The same advertiserId used to publish the campaign.")
9589
+ var TikTokStatusInput = z46.object({
9590
+ campaignId: z46.string().min(1).describe("TikTok campaign ID returned by publish_to_tiktok."),
9591
+ advertiserId: z46.string().min(1).describe("The same advertiserId used to publish the campaign.")
9357
9592
  });
9358
9593
  function registerCampaignTools(server, api) {
9359
9594
  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) => {
@@ -9450,10 +9685,10 @@ function renderTikTokStatusMarkdown(r) {
9450
9685
  __name(renderTikTokStatusMarkdown, "renderTikTokStatusMarkdown");
9451
9686
 
9452
9687
  // ../../packages/product-economics/src/product-costing.ts
9453
- import { z as z47 } from "zod";
9688
+ import { z as z48 } from "zod";
9454
9689
 
9455
9690
  // ../../packages/product-economics/src/product-costing-countries.ts
9456
- import { z as z46 } from "zod";
9691
+ import { z as z47 } from "zod";
9457
9692
 
9458
9693
  // ../../packages/product-economics/src/product-costing-shipping-references.json
9459
9694
  var product_costing_shipping_references_default = {
@@ -9517,21 +9752,21 @@ var product_costing_shipping_references_default = {
9517
9752
  };
9518
9753
 
9519
9754
  // ../../packages/product-economics/src/product-costing-countries.ts
9520
- var ShippingReferenceSchema = z46.object({
9521
- reference: z46.number().positive(),
9522
- min: z46.number().nonnegative(),
9523
- max: z46.number().positive(),
9524
- updatedAt: z46.string().date(),
9525
- confidence: z46.enum([
9755
+ var ShippingReferenceSchema = z47.object({
9756
+ reference: z47.number().positive(),
9757
+ min: z47.number().nonnegative(),
9758
+ max: z47.number().positive(),
9759
+ updatedAt: z47.string().date(),
9760
+ confidence: z47.enum([
9526
9761
  "high",
9527
9762
  "medium",
9528
9763
  "medium-low"
9529
9764
  ]),
9530
- sources: z46.array(z46.string().url().startsWith("https://")).min(1).max(3)
9765
+ sources: z47.array(z47.string().url().startsWith("https://")).min(1).max(3)
9531
9766
  }).superRefine(({ reference, min, max }, context) => {
9532
9767
  if (min > reference || reference > max) {
9533
9768
  context.addIssue({
9534
- code: z46.ZodIssueCode.custom,
9769
+ code: z47.ZodIssueCode.custom,
9535
9770
  message: "shipping_reference_outside_range",
9536
9771
  path: [
9537
9772
  "reference"
@@ -9539,7 +9774,7 @@ var ShippingReferenceSchema = z46.object({
9539
9774
  });
9540
9775
  }
9541
9776
  });
9542
- var SHIPPING_REFERENCES = z46.record(ShippingReferenceSchema).parse(product_costing_shipping_references_default);
9777
+ var SHIPPING_REFERENCES = z47.record(ShippingReferenceSchema).parse(product_costing_shipping_references_default);
9543
9778
  var PRODUCT_COSTING_COUNTRY_CODES = Object.freeze(Object.keys(SHIPPING_REFERENCES));
9544
9779
  var SPANISH_LOCALE_COUNTRIES = /* @__PURE__ */ new Set([
9545
9780
  "MX",
@@ -9721,18 +9956,18 @@ function getProductCostingCountryName(country) {
9721
9956
  __name(getProductCostingCountryName, "getProductCostingCountryName");
9722
9957
 
9723
9958
  // ../../packages/product-economics/src/product-costing.ts
9724
- var ProductCostingInputSchema = z47.object({
9725
- country: z47.string().refine(isProductCostingCountryCode, "unsupported_country"),
9726
- supplierCost: z47.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").positive("supplier_cost_positive"),
9727
- shippingCost: z47.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("shipping_cost_nonnegative"),
9728
- returnRate: z47.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
9729
- cancellationRate: z47.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
9730
- adsManagerCpa: z47.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("cpa_nonnegative"),
9731
- desiredMargin: z47.number().finite().min(0, "margin_nonnegative").lt(1, "margin_below_one")
9959
+ var ProductCostingInputSchema = z48.object({
9960
+ country: z48.string().refine(isProductCostingCountryCode, "unsupported_country"),
9961
+ supplierCost: z48.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").positive("supplier_cost_positive"),
9962
+ shippingCost: z48.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("shipping_cost_nonnegative"),
9963
+ returnRate: z48.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
9964
+ cancellationRate: z48.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
9965
+ adsManagerCpa: z48.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("cpa_nonnegative"),
9966
+ desiredMargin: z48.number().finite().min(0, "margin_nonnegative").lt(1, "margin_below_one")
9732
9967
  }).superRefine(({ returnRate, cancellationRate }, context) => {
9733
9968
  if (returnRate + cancellationRate >= 1) {
9734
9969
  context.addIssue({
9735
- code: z47.ZodIssueCode.custom,
9970
+ code: z48.ZodIssueCode.custom,
9736
9971
  message: "delivery_rate_positive",
9737
9972
  path: [
9738
9973
  "cancellationRate"
@@ -9799,14 +10034,14 @@ function calculateProductCosting(rawInput) {
9799
10034
  __name(calculateProductCosting, "calculateProductCosting");
9800
10035
 
9801
10036
  // src/tools/product-costing.ts
9802
- import { z as z48 } from "zod";
9803
- var optionalAmount = /* @__PURE__ */ __name((description) => z48.number().finite().max(Number.MAX_SAFE_INTEGER).nonnegative().optional().describe(description), "optionalAmount");
9804
- var optionalPercent = /* @__PURE__ */ __name((description) => z48.number().finite().min(0).lt(100).optional().describe(description), "optionalPercent");
9805
- var ProductCostingToolInput = z48.object({
9806
- country: z48.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
10037
+ import { z as z49 } from "zod";
10038
+ var optionalAmount = /* @__PURE__ */ __name((description) => z49.number().finite().max(Number.MAX_SAFE_INTEGER).nonnegative().optional().describe(description), "optionalAmount");
10039
+ var optionalPercent = /* @__PURE__ */ __name((description) => z49.number().finite().min(0).lt(100).optional().describe(description), "optionalPercent");
10040
+ var ProductCostingToolInput = z49.object({
10041
+ country: z49.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
9807
10042
  message: "Pa\xEDs no soportado por ProductMaker."
9808
10043
  }).optional().describe("C\xF3digo ISO-2 de cualquiera de los pa\xEDses disponibles en ProductMaker. Omite para Colombia."),
9809
- 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."),
10044
+ 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."),
9810
10045
  supplierCostCop: optionalAmount("Alias anterior para costo del proveedor en Colombia. Prefiere supplierCost."),
9811
10046
  shippingCost: optionalAmount("Costo de un env\xEDo en la moneda del pa\xEDs. Omite para usar el valor gu\xEDa vigente."),
9812
10047
  shippingCostCop: optionalAmount("Alias anterior para flete en Colombia. Prefiere shippingCost."),
@@ -9938,7 +10173,7 @@ function formatPercent(value) {
9938
10173
  __name(formatPercent, "formatPercent");
9939
10174
 
9940
10175
  // src/tools/product-research.ts
9941
- import { z as z49 } from "zod";
10176
+ import { z as z50 } from "zod";
9942
10177
 
9943
10178
  // src/tools/product-research-idempotency.ts
9944
10179
  import { createHash as createHash2 } from "crypto";
@@ -10054,17 +10289,17 @@ var TERMINAL2 = /* @__PURE__ */ new Set([
10054
10289
  ]);
10055
10290
  var POLL_INTERVAL_MS = 5e3;
10056
10291
  var MAX_WAIT_SECONDS = 120;
10057
- var FindInput = z49.object({
10292
+ var FindInput = z50.object({
10058
10293
  targetCountry: ResearchTargetCountryEnum.describe("Pa\xEDs objetivo. S\xF3lo CO, MX, PE, EC y CL tienen cobertura de investigaci\xF3n."),
10059
10294
  categoryKey: ResearchCategoryEnum.default("auto").describe('Categor\xEDa a investigar. Usa "auto" para que la IA elija seg\xFAn el mercado.'),
10060
- 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.`),
10061
- 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.")
10295
+ 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.`),
10296
+ 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.")
10062
10297
  });
10063
- var GetInput = z49.object({
10064
- runId: z49.string().uuid().describe("ID del run devuelto por find_winning_products.")
10298
+ var GetInput = z50.object({
10299
+ runId: z50.string().uuid().describe("ID del run devuelto por find_winning_products.")
10065
10300
  });
10066
- var ListInput = z49.object({
10067
- limit: z49.number().int().min(1).max(20).default(10)
10301
+ var ListInput = z50.object({
10302
+ limit: z50.number().int().min(1).max(20).default(10)
10068
10303
  });
10069
10304
  function textResult(text) {
10070
10305
  return {
@@ -10155,6 +10390,230 @@ _Esta llamada no cobr\xF3 cr\xE9ditos: se reutiliz\xF3 una investigaci\xF3n reci
10155
10390
  }
10156
10391
  __name(registerProductResearchTools, "registerProductResearchTools");
10157
10392
 
10393
+ // src/tools/dropshipping-projects.ts
10394
+ import { z as z51 } from "zod";
10395
+ var ProjectId = z51.string().uuid();
10396
+ var DropshippingProjectStatus = z51.enum([
10397
+ "active",
10398
+ "completed",
10399
+ "archived"
10400
+ ]);
10401
+ var DropshippingProjectPhase = z51.enum([
10402
+ "research",
10403
+ "costing",
10404
+ "media",
10405
+ "generating",
10406
+ "storefront",
10407
+ "advertising",
10408
+ "complete"
10409
+ ]);
10410
+ var TargetCountry = z51.enum([
10411
+ "CO",
10412
+ "MX",
10413
+ "PE",
10414
+ "EC",
10415
+ "CL"
10416
+ ]);
10417
+ var ResearchCategory = z51.enum([
10418
+ "auto",
10419
+ "automotive",
10420
+ "kitchen",
10421
+ "pets",
10422
+ "home_organization",
10423
+ "beauty",
10424
+ "wellness",
10425
+ "fitness",
10426
+ "baby",
10427
+ "tech",
10428
+ "tools",
10429
+ "garden",
10430
+ "cleaning"
10431
+ ]);
10432
+ var HttpUrl3 = z51.string().url().max(2e3).refine((value) => {
10433
+ try {
10434
+ return [
10435
+ "http:",
10436
+ "https:"
10437
+ ].includes(new URL(value).protocol);
10438
+ } catch {
10439
+ return false;
10440
+ }
10441
+ }, "URL must use HTTP(S)");
10442
+ var ResearchCandidateOrigin = z51.object({
10443
+ type: z51.literal("research_candidate"),
10444
+ runId: ProjectId,
10445
+ candidateSourceId: z51.string().min(1).max(160)
10446
+ });
10447
+ var TaskOrigin = z51.object({
10448
+ type: z51.literal("task"),
10449
+ taskId: ProjectId
10450
+ });
10451
+ var ManualOrigin = z51.object({
10452
+ type: z51.literal("manual"),
10453
+ clientRequestId: ProjectId,
10454
+ country: z51.string().min(2).max(3).optional()
10455
+ });
10456
+ var StartProjectInput = z51.object({
10457
+ origin: z51.discriminatedUnion("type", [
10458
+ ResearchCandidateOrigin,
10459
+ TaskOrigin,
10460
+ ManualOrigin
10461
+ ]),
10462
+ productName: z51.string().trim().min(1).max(240).optional(),
10463
+ productUrl: HttpUrl3.optional(),
10464
+ country: z51.string().min(2).max(3).optional(),
10465
+ languageCode: z51.string().min(2).max(16).optional()
10466
+ }).strict();
10467
+ var CostProjectInput = z51.object({
10468
+ projectId: ProjectId,
10469
+ version: z51.number().int().positive(),
10470
+ country: z51.string().trim().min(2).max(3),
10471
+ supplierCost: z51.number().finite().positive(),
10472
+ shippingCost: z51.number().finite().nonnegative(),
10473
+ returnRate: z51.number().finite().min(0).lt(1),
10474
+ cancellationRate: z51.number().finite().min(0).lt(1),
10475
+ adsManagerCpa: z51.number().finite().nonnegative(),
10476
+ desiredMargin: z51.number().finite().min(0).lt(1)
10477
+ });
10478
+ var GuideInput = z51.object({
10479
+ projectId: ProjectId.optional()
10480
+ }).strict();
10481
+ var DropshippingProjectNextAction = z51.discriminatedUnion("kind", [
10482
+ "cost_product",
10483
+ "provide_product_media",
10484
+ "generate_creatives",
10485
+ "wait_for_creatives",
10486
+ "repair_creatives",
10487
+ "complete_creative_set",
10488
+ "connect_shopify",
10489
+ "publish_storefront",
10490
+ "wait_for_storefront",
10491
+ "connect_meta",
10492
+ "create_meta_campaign",
10493
+ "repair_meta_campaign",
10494
+ "review_paused_campaign"
10495
+ ].map((kind) => z51.object({
10496
+ kind: z51.literal(kind),
10497
+ href: z51.string().min(1)
10498
+ })));
10499
+ var CostingInputSchema = z51.object({
10500
+ country: z51.string().min(2).max(3),
10501
+ supplierCost: z51.number().finite().positive(),
10502
+ shippingCost: z51.number().finite().nonnegative(),
10503
+ returnRate: z51.number().finite().min(0).lt(1),
10504
+ cancellationRate: z51.number().finite().min(0).lt(1),
10505
+ adsManagerCpa: z51.number().finite().nonnegative(),
10506
+ desiredMargin: z51.number().finite().min(0).lt(1)
10507
+ }).superRefine(({ returnRate, cancellationRate }, context) => {
10508
+ if (returnRate + cancellationRate >= 1) context.addIssue({
10509
+ code: z51.ZodIssueCode.custom,
10510
+ path: [
10511
+ "cancellationRate"
10512
+ ]
10513
+ });
10514
+ });
10515
+ var CostingResultSchema = z51.object({
10516
+ country: z51.string().min(2).max(3),
10517
+ deliveryRate: z51.number().finite(),
10518
+ adjustedShippingCost: z51.number().finite(),
10519
+ adjustedCpa: z51.number().finite(),
10520
+ totalCost: z51.number().finite(),
10521
+ suggestedPrice: z51.number().finite(),
10522
+ baseCommercialPrice: z51.number().finite(),
10523
+ minimumSafePrice: z51.number().finite(),
10524
+ commercialPrice: z51.number().finite(),
10525
+ comparisonPrice: z51.number().finite(),
10526
+ estimatedProfit: z51.number().finite(),
10527
+ actualMargin: z51.number().finite(),
10528
+ breakEvenAdsManagerCpa: z51.number().finite(),
10529
+ wasPriceAutoAdjusted: z51.boolean(),
10530
+ profitabilityStatus: z51.literal("healthy")
10531
+ });
10532
+ var ProjectResponseSchema = z51.object({
10533
+ id: ProjectId,
10534
+ taskId: ProjectId.nullable(),
10535
+ status: DropshippingProjectStatus,
10536
+ originKey: z51.string().min(1),
10537
+ researchRunId: ProjectId.nullable(),
10538
+ candidateSourceId: z51.string().nullable(),
10539
+ productName: z51.string().nullable(),
10540
+ productUrl: HttpUrl3.nullable(),
10541
+ referenceImageUrl: HttpUrl3.nullable(),
10542
+ targetCountry: z51.string().nullable(),
10543
+ languageCode: z51.string().nullable(),
10544
+ hasReferenceImage: z51.boolean(),
10545
+ costingInput: CostingInputSchema.nullable(),
10546
+ costingResult: CostingResultSchema.nullable(),
10547
+ costingVersion: z51.number().int().positive().nullable(),
10548
+ version: z51.number().int().positive(),
10549
+ phase: DropshippingProjectPhase,
10550
+ nextAction: DropshippingProjectNextAction.nullable(),
10551
+ createdAt: z51.string().datetime(),
10552
+ updatedAt: z51.string().datetime()
10553
+ }).passthrough();
10554
+ function projectText(raw) {
10555
+ const project = ProjectResponseSchema.parse(raw);
10556
+ const next = project.nextAction ? `${project.nextAction.kind} (${project.nextAction.href})` : "ninguna";
10557
+ return {
10558
+ content: [
10559
+ {
10560
+ type: "text",
10561
+ text: JSON.stringify({
10562
+ projectId: project.id,
10563
+ productName: project.productName,
10564
+ phase: project.phase,
10565
+ nextAction: project.nextAction,
10566
+ hasReferenceImage: project.hasReferenceImage,
10567
+ costingSaved: Boolean(project.costingResult),
10568
+ version: project.version,
10569
+ message: `Siguiente paso: ${next}. Relee este proyecto despu\xE9s de cada mutaci\xF3n.`
10570
+ })
10571
+ }
10572
+ ]
10573
+ };
10574
+ }
10575
+ __name(projectText, "projectText");
10576
+ function registerDropshippingProjectTools(server, api) {
10577
+ 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) => {
10578
+ try {
10579
+ return projectText(await api.postJson("/v1/dropshipping-projects", input));
10580
+ } catch (error) {
10581
+ throw translateError(error);
10582
+ }
10583
+ });
10584
+ 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) => {
10585
+ try {
10586
+ if (input.returnRate + input.cancellationRate >= 1) {
10587
+ throw new Error("returnRate + cancellationRate debe ser menor que 1.");
10588
+ }
10589
+ const { projectId, version, ...costingInput } = input;
10590
+ return projectText(await api.putJson(`/v1/dropshipping-projects/${encodeURIComponent(projectId)}/costing`, {
10591
+ version,
10592
+ input: costingInput
10593
+ }));
10594
+ } catch (error) {
10595
+ throw translateError(error);
10596
+ }
10597
+ });
10598
+ 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) => {
10599
+ try {
10600
+ if (!input.projectId) {
10601
+ return asText({
10602
+ mode: "choose",
10603
+ guided: "Usa start_dropshipping_project cuando el usuario quiera encontrar un producto y dejarlo listo para publicar.",
10604
+ 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.",
10605
+ confirmation: "Antes de acciones pagadas o externas, muestra el estimado y pide confirmaci\xF3n.",
10606
+ safety: "Las campa\xF1as Meta quedan PAUSED; el usuario las activa manualmente en Ads Manager."
10607
+ });
10608
+ }
10609
+ return projectText(await api.getJson(`/v1/dropshipping-projects/${encodeURIComponent(input.projectId)}`));
10610
+ } catch (error) {
10611
+ throw translateError(error);
10612
+ }
10613
+ });
10614
+ }
10615
+ __name(registerDropshippingProjectTools, "registerDropshippingProjectTools");
10616
+
10158
10617
  // src/server.ts
10159
10618
  function createServer(opts) {
10160
10619
  const { apiKey, apiBaseUrl, proxyBaseUrl = null, transport = "http" } = opts;
@@ -10172,6 +10631,7 @@ function createServer(opts) {
10172
10631
  const api = new ApiClient(apiBaseUrl, apiKey);
10173
10632
  registerProductCostingTools(server);
10174
10633
  registerProductResearchTools(server, api);
10634
+ registerDropshippingProjectTools(server, api);
10175
10635
  registerTaskTools(server, api, {
10176
10636
  proxyBaseUrl,
10177
10637
  transport