@productmaker/mcp 8.1.0 → 8.3.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 (4) hide show
  1. package/README.md +14 -1
  2. package/dist/http.js +1940 -861
  3. package/dist/stdio.js +1885 -836
  4. package/package.json +1 -1
package/dist/stdio.js CHANGED
@@ -90,6 +90,23 @@ var VideoPreset = z.enum([
90
90
  ]);
91
91
  var VoiceId = z.string();
92
92
  var _VIDEO_MODEL_SET = new Set(VideoModel.options);
93
+ var BROLL_VIDEO_MODES = new Set(VideoModel.options);
94
+ var PersistedVideoMode = z.enum([
95
+ ...VideoModel.options,
96
+ "avatar_talking",
97
+ "hybrid",
98
+ "sora"
99
+ ]);
100
+ var PersistedVideoModel = z.enum([
101
+ ...VideoModel.options,
102
+ "sora",
103
+ "infinitalk"
104
+ ]);
105
+ var PLAN_VIDEO_MODES = [
106
+ ...VideoModel.options,
107
+ "avatar_talking",
108
+ "hybrid"
109
+ ];
93
110
 
94
111
  // ../../packages/types/src/video-creation.ts
95
112
  import { z as z3 } from "zod";
@@ -1060,40 +1077,206 @@ var VideoTier = z8.preprocess((v) => v === "grok" ? "single_call" : v, z8.enum([
1060
1077
  "single_call"
1061
1078
  ]));
1062
1079
  var DEFAULT_VIDEO_TIER = "single_call";
1080
+ var WritableVideoTier = VideoTier.refine((tier) => tier !== "legacy", {
1081
+ message: "VIDEO_TIER_RETIRED"
1082
+ });
1063
1083
 
1064
- // ../../packages/types/src/image-provider.ts
1065
- var GPT_IMAGE_2_RATIOS_BLOCKED_ABOVE_1K = [
1066
- "4:5",
1067
- "5:4"
1084
+ // ../../packages/types/src/image-models.ts
1085
+ var IMAGE_RESOLUTIONS = [
1086
+ "1k",
1087
+ "2k",
1088
+ "4k"
1068
1089
  ];
1069
- var GPT_IMAGE_2_5_RATIOS_BLOCKED_ABOVE_1K = [
1070
- "4:5",
1071
- "5:4",
1072
- "27:16",
1073
- "16:27",
1074
- "9:8",
1075
- "8:9"
1090
+ var GPT_IMAGE_2_5_TOKENS = {
1091
+ "1k": "gpt-image-2-5:1k",
1092
+ "2k": "gpt-image-2-5:2k",
1093
+ "4k": "gpt-image-2-5:4k"
1094
+ };
1095
+ var GPT_IMAGE_2_TOKENS = {
1096
+ "1k": "gpt-image-2:1k",
1097
+ "2k": "gpt-image-2:2k",
1098
+ "4k": "gpt-image-2:4k"
1099
+ };
1100
+ var GPT_IMAGE_2_5 = {
1101
+ id: "gpt-image-2.5",
1102
+ status: "active",
1103
+ renderedBy: "gpt-image-2.5",
1104
+ tokens: GPT_IMAGE_2_5_TOKENS,
1105
+ kie: {
1106
+ imageToImage: [
1107
+ "gpt-image-2-5-sunburst-image-to-image",
1108
+ "gpt-image-2-5-flare-image-to-image"
1109
+ ],
1110
+ textToImage: [
1111
+ "gpt-image-2-5-sunburst-text-to-image",
1112
+ "gpt-image-2-5-flare-text-to-image"
1113
+ ],
1114
+ shorthands: [
1115
+ "gpt-image-2-5"
1116
+ ]
1117
+ },
1118
+ fal: {
1119
+ edit: "openai/gpt-image-2.5/sunburst/edit",
1120
+ textToImage: "openai/gpt-image-2.5/sunburst/text-to-image",
1121
+ quality: {
1122
+ "1k": "low",
1123
+ "2k": "medium",
1124
+ "4k": "high"
1125
+ },
1126
+ ourCostCents: {
1127
+ "1k": 1.5,
1128
+ "2k": 6.1,
1129
+ "4k": 17.8
1130
+ }
1131
+ },
1132
+ // Docs KIE de gpt-image-2-5 (sunburst/flare): 27:16, 16:27, 9:8, 8:9 solo 1K;
1133
+ // 4:5 y 5:4 también bloqueados por encima de 1K.
1134
+ ratiosBlockedAbove1k: [
1135
+ "4:5",
1136
+ "5:4",
1137
+ "27:16",
1138
+ "16:27",
1139
+ "9:8",
1140
+ "8:9"
1141
+ ],
1142
+ ledgerSuffix: {
1143
+ "1k": "gpt-image-2",
1144
+ "2k": "gpt-image-2:medium",
1145
+ "4k": "gpt-image-2:high"
1146
+ },
1147
+ falLedgerSuffix: {
1148
+ "1k": "gpt-image-2:1k",
1149
+ "2k": "gpt-image-2:2k",
1150
+ "4k": "gpt-image-2:4k"
1151
+ }
1152
+ };
1153
+ var GPT_IMAGE_2 = {
1154
+ ...GPT_IMAGE_2_5,
1155
+ id: "gpt-image-2",
1156
+ status: "retired",
1157
+ renderedBy: GPT_IMAGE_2_5.id,
1158
+ tokens: GPT_IMAGE_2_TOKENS,
1159
+ kie: {
1160
+ imageToImage: [
1161
+ "gpt-image-2-image-to-image"
1162
+ ],
1163
+ textToImage: [
1164
+ "gpt-image-2-text-to-image"
1165
+ ],
1166
+ shorthands: [
1167
+ "gpt-image-2"
1168
+ ]
1169
+ },
1170
+ ratiosBlockedAbove1k: [
1171
+ "4:5",
1172
+ "5:4"
1173
+ ]
1174
+ };
1175
+ var IMAGE_MODELS = [
1176
+ GPT_IMAGE_2_5,
1177
+ GPT_IMAGE_2
1076
1178
  ];
1077
- function gptImage2SupportsTier(aspectRatio, tier) {
1078
- if (tier === "gpt-image-2:1k" || tier === "gpt-image-2-5:1k") return true;
1079
- if (tier.startsWith("gpt-image-2-5:")) {
1080
- return !GPT_IMAGE_2_5_RATIOS_BLOCKED_ABOVE_1K.includes(aspectRatio);
1179
+ var ACTIVE_IMAGE_MODEL = GPT_IMAGE_2_5;
1180
+ var IMAGE_WRITE_TOKENS = GPT_IMAGE_2_5_TOKENS;
1181
+ var LEGACY_IMAGE_TOKENS = GPT_IMAGE_2_TOKENS;
1182
+ var PUBLIC_IMAGE_QUALITY_INPUTS = [
1183
+ IMAGE_WRITE_TOKENS["1k"],
1184
+ IMAGE_WRITE_TOKENS["2k"],
1185
+ LEGACY_IMAGE_TOKENS["1k"],
1186
+ LEGACY_IMAGE_TOKENS["2k"],
1187
+ "image:1k",
1188
+ "image:2k"
1189
+ ];
1190
+ function entryById(id) {
1191
+ const found = IMAGE_MODELS.find((entry) => entry.id === id);
1192
+ if (!found) throw new Error(`IMAGE_MODEL_UNKNOWN:${id}`);
1193
+ return found;
1194
+ }
1195
+ __name(entryById, "entryById");
1196
+ function resolveImageToken(raw) {
1197
+ if (typeof raw !== "string") return null;
1198
+ let normalized = raw.trim().toLowerCase();
1199
+ let viaFal = false;
1200
+ if (normalized.startsWith("fal:")) {
1201
+ viaFal = true;
1202
+ normalized = normalized.slice("fal:".length);
1203
+ }
1204
+ for (const resolution of IMAGE_RESOLUTIONS) {
1205
+ if (normalized === `image:${resolution}`) {
1206
+ return {
1207
+ resolution,
1208
+ model: ACTIVE_IMAGE_MODEL,
1209
+ declared: ACTIVE_IMAGE_MODEL,
1210
+ spelling: "intent",
1211
+ viaFal
1212
+ };
1213
+ }
1214
+ }
1215
+ for (const entry of IMAGE_MODELS) {
1216
+ for (const resolution of IMAGE_RESOLUTIONS) {
1217
+ if (entry.tokens[resolution] === normalized) {
1218
+ return {
1219
+ resolution,
1220
+ model: entryById(entry.renderedBy),
1221
+ declared: entry,
1222
+ spelling: entry.status === "active" ? "current" : "retired",
1223
+ viaFal
1224
+ };
1225
+ }
1226
+ }
1081
1227
  }
1082
- return !GPT_IMAGE_2_RATIOS_BLOCKED_ABOVE_1K.includes(aspectRatio);
1228
+ return null;
1229
+ }
1230
+ __name(resolveImageToken, "resolveImageToken");
1231
+ function imageResolutionOf(raw) {
1232
+ const token = resolveImageToken(raw);
1233
+ return token && !token.viaFal ? token.resolution : null;
1234
+ }
1235
+ __name(imageResolutionOf, "imageResolutionOf");
1236
+ function requireKieImageId(ids, index, label) {
1237
+ const id = ids[index];
1238
+ if (!id) throw new Error(`IMAGE_MODEL_NO_KIE_ID:${label}`);
1239
+ return id;
1240
+ }
1241
+ __name(requireKieImageId, "requireKieImageId");
1242
+ var KIE_FLARE_IMAGE_TO_IMAGE_MODEL = requireKieImageId(GPT_IMAGE_2_5.kie.imageToImage, 1, "flare-imageToImage");
1243
+ function canonicalImageModelInput(raw) {
1244
+ if (raw === void 0) return void 0;
1245
+ const token = resolveImageToken(raw);
1246
+ if (!token || token.viaFal || token.spelling !== "intent") return raw;
1247
+ return IMAGE_WRITE_TOKENS[token.resolution];
1248
+ }
1249
+ __name(canonicalImageModelInput, "canonicalImageModelInput");
1250
+ function imageTokenSupportsRatio(aspectRatio, rawToken) {
1251
+ const token = resolveImageToken(rawToken);
1252
+ if (!token) return true;
1253
+ if (token.resolution === "1k") return true;
1254
+ return !token.declared.ratiosBlockedAbove1k.includes(aspectRatio);
1255
+ }
1256
+ __name(imageTokenSupportsRatio, "imageTokenSupportsRatio");
1257
+
1258
+ // ../../packages/types/src/image-provider.ts
1259
+ var GPT_IMAGE_2_RATIOS_BLOCKED_ABOVE_1K = GPT_IMAGE_2.ratiosBlockedAbove1k;
1260
+ var GPT_IMAGE_2_5_RATIOS_BLOCKED_ABOVE_1K = GPT_IMAGE_2_5.ratiosBlockedAbove1k;
1261
+ function gptImage2SupportsTier(aspectRatio, tier) {
1262
+ return imageTokenSupportsRatio(aspectRatio, tier);
1083
1263
  }
1084
1264
  __name(gptImage2SupportsTier, "gptImage2SupportsTier");
1085
1265
  var IMAGE_TIER_UNSUPPORTED_FOR_RATIO_RULE = "image_tier_unsupported_for_ratio";
1086
1266
  var IMAGE_TIER_LIST = [
1087
- "gpt-image-2-5:1k",
1088
- "gpt-image-2-5:2k",
1089
- "gpt-image-2-5:4k",
1090
- "gpt-image-2:1k",
1091
- "gpt-image-2:2k",
1092
- "gpt-image-2:4k"
1267
+ ...Object.values(IMAGE_WRITE_TOKENS),
1268
+ ...Object.values(LEGACY_IMAGE_TOKENS)
1093
1269
  ];
1270
+ function isImageTier(value) {
1271
+ return IMAGE_TIER_LIST.includes(value);
1272
+ }
1273
+ __name(isImageTier, "isImageTier");
1094
1274
  function parseImageTier(imageModel) {
1095
- const normalized = imageModel.trim().toLowerCase();
1096
- return IMAGE_TIER_LIST.includes(normalized) ? normalized : null;
1275
+ const token = resolveImageToken(imageModel);
1276
+ if (!token || token.viaFal) return null;
1277
+ if (token.spelling === "intent") return IMAGE_WRITE_TOKENS[token.resolution];
1278
+ const declared = token.declared.tokens[token.resolution];
1279
+ return isImageTier(declared) ? declared : null;
1097
1280
  }
1098
1281
  __name(parseImageTier, "parseImageTier");
1099
1282
  function validateImageTierForRatio(aspectRatio, imageModel) {
@@ -1669,6 +1852,9 @@ var VideoCreativeJobStatus = z14.object({
1669
1852
  var VideoCreativeJobPayload = z14.object({
1670
1853
  jobId: z14.string().optional(),
1671
1854
  status: VideoCreativeJobStatusEnum.optional(),
1855
+ /** True when the submission replayed an existing task instead of billing
1856
+ * new work — callers surface the twin verbatim. Omitted on fresh creates. */
1857
+ replayed: z14.boolean().optional(),
1672
1858
  videoStructure: VideoStructure.optional(),
1673
1859
  videoQuality: VideoQuality.optional(),
1674
1860
  videoRouteId: VideoRouteId.optional(),
@@ -1762,7 +1948,8 @@ var ImageCreativeJobStatus = z14.object({
1762
1948
  });
1763
1949
  var ImageCreativeJobPayload = z14.object({
1764
1950
  jobId: z14.string().uuid(),
1765
- status: ImageCreativeJobStatusEnum
1951
+ status: ImageCreativeJobStatusEnum,
1952
+ replayed: z14.boolean().optional()
1766
1953
  });
1767
1954
  var ImageCreativeHistoryEntry = z14.object({
1768
1955
  id: z14.string().uuid(),
@@ -1789,7 +1976,8 @@ var LandingCreativeJobStatus = z14.object({
1789
1976
  });
1790
1977
  var LandingCreativeJobPayload = z14.object({
1791
1978
  jobId: z14.string().uuid(),
1792
- status: LandingCreativeJobStatusEnum
1979
+ status: LandingCreativeJobStatusEnum,
1980
+ replayed: z14.boolean().optional()
1793
1981
  });
1794
1982
  var LandingCreativeHistoryEntry = z14.object({
1795
1983
  id: z14.string().uuid(),
@@ -3830,7 +4018,7 @@ var IngestOptions = z22.object({
3830
4018
  durationSeconds: FreeVideoOptionsSchema.shape.durationSeconds.optional(),
3831
4019
  addSubtitles: FreeVideoOptionsSchema.shape.addSubtitles.optional(),
3832
4020
  subtitleText: z22.string().transform((value) => value.trim()).pipe(z22.string().max(FREE_VIDEO_SUBTITLE_MAX_CHARS)).optional(),
3833
- videoTier: VideoTier.default(DEFAULT_VIDEO_TIER),
4021
+ videoTier: WritableVideoTier.default(DEFAULT_VIDEO_TIER),
3834
4022
  narrativeStyle: NarrativeStyle.default("review"),
3835
4023
  videoPreset: VideoPreset.optional(),
3836
4024
  multiAngleVideos: z22.boolean().default(false),
@@ -3904,7 +4092,9 @@ var IngestOptions = z22.object({
3904
4092
  aspectRatio: z22.string().optional(),
3905
4093
  brandColors: z22.string().optional(),
3906
4094
  nVariants: z22.number().int().min(1).max(6).optional(),
3907
- imageModel: z22.string().optional(),
4095
+ // O7: `image:Nk` is accepted and stored as the spelling written today
4096
+ // (IMAGE_WRITE_TOKENS); see packages/types/src/image-models.ts.
4097
+ imageModel: z22.string().optional().transform((value) => canonicalImageModelInput(value)),
3908
4098
  styleMix: z22.string().optional(),
3909
4099
  aiDecidesAll: z22.boolean().optional(),
3910
4100
  /** Opt out of the competitor-spy analysis. When false the worker skips
@@ -4023,7 +4213,7 @@ var VideoOptionsOverride = z22.object({
4023
4213
  videoStructure: VideoStructure.optional(),
4024
4214
  videoQuality: VideoQuality.optional(),
4025
4215
  videoModel: VideoModel.optional(),
4026
- videoTier: VideoTier.optional(),
4216
+ videoTier: WritableVideoTier.optional(),
4027
4217
  ugcFormat: VideoFormat.optional(),
4028
4218
  videoStyle: VideoStyle.optional(),
4029
4219
  narrativeStyle: NarrativeStyle.optional(),
@@ -4106,6 +4296,11 @@ var IngestResumePayload = z22.object({
4106
4296
  // "keep what the task has". See angle-management.service.ts resumeAfterAngles.
4107
4297
  videoStyle: VideoStyle.optional(),
4108
4298
  videoModel: VideoModel.default("veo"),
4299
+ // READ schema on purpose, unlike IngestOptions/VideoOptionsOverride: resume
4300
+ // does not write the tier — `resumeAfterAngles` projects the route from the
4301
+ // task's OWN persisted tier and ignores this field. MCP and WhatsApp echo
4302
+ // `status.videoTier`, which is still `legacy` for historical rows; refusing
4303
+ // it made those tasks impossible to resume from both channels.
4109
4304
  videoTier: VideoTier.optional(),
4110
4305
  videoStructure: VideoStructure.optional(),
4111
4306
  videoQuality: VideoQuality.optional(),
@@ -4916,17 +5111,7 @@ var VideoGenerator = z26.object({
4916
5111
  format: VideoFormat.optional(),
4917
5112
  videoStyle: VideoStyle.optional(),
4918
5113
  narrativeStyle: NarrativeStyle.optional(),
4919
- videoMode: z26.enum([
4920
- "veo",
4921
- "wan",
4922
- "bytedance",
4923
- "seedance",
4924
- "grok",
4925
- "kling",
4926
- "avatar_talking",
4927
- "hybrid",
4928
- "sora"
4929
- ]).optional(),
5114
+ videoMode: PersistedVideoMode.optional(),
4930
5115
  videoResolution: VideoResolution.optional(),
4931
5116
  videoStructure: VideoStructure.optional(),
4932
5117
  videoQuality: VideoQuality.optional(),
@@ -5463,6 +5648,10 @@ var CreateShopifySubscriptionRequest = z28.object({
5463
5648
  var CreateShopifyBillingChargeRequest = z28.object({
5464
5649
  packId: z28.string().min(1)
5465
5650
  });
5651
+ var ShopifyBillingCallbackQuery = z28.object({
5652
+ charge_id: z28.string().regex(/^\d+$/),
5653
+ transactionId: z28.string().uuid()
5654
+ });
5466
5655
  var CancelShopifySubscriptionRequest = z28.object({
5467
5656
  subscriptionId: z28.string().min(1)
5468
5657
  });
@@ -7291,7 +7480,8 @@ var IMAGE_QA_GATES = [
7291
7480
  "limb",
7292
7481
  "device_panel",
7293
7482
  "critical_feature",
7294
- "cross_section"
7483
+ "cross_section",
7484
+ "usage_fidelity"
7295
7485
  ];
7296
7486
  var ImageQaGate = z36.enum(IMAGE_QA_GATES);
7297
7487
  var ImageQaGateOutcome = z36.enum([
@@ -8337,6 +8527,10 @@ var GEMINI_OMNI_FLAT_USER_PRICE_CENTS = 168;
8337
8527
  // ../../packages/types/src/pricing.ts
8338
8528
  var CREDITS_PER_USD_CENT = 10;
8339
8529
  var CREDITS_PER_DOLLAR = 100 * CREDITS_PER_USD_CENT;
8530
+ var AD_CREATIVE_MODELS = {
8531
+ STANDARD: IMAGE_WRITE_TOKENS["1k"],
8532
+ PREMIUM: IMAGE_WRITE_TOKENS["2k"]
8533
+ };
8340
8534
  var KALODATA_USD_CENTS_PER_PROVIDER_CREDIT = 9;
8341
8535
  var KALODATA_PROVIDER_CREDITS_PER_SOURCE_CEILING = 7.5;
8342
8536
  var DEFAULT_PRICING = {
@@ -9295,6 +9489,27 @@ var DEFAULT_PRICING = {
9295
9489
  ourCostCents: 1.88,
9296
9490
  userPriceCents: 3.76
9297
9491
  },
9492
+ // Usage decider (role `usage-decider`: Gemini 3.8 Flash effort:'high' since
9493
+ // 05f543eae, ≤6 images + seller text, temperature 0 json_object, once per
9494
+ // classic landing, always on). Was 3¢ measured on Claude
9495
+ // Sonnet 5 (spike 2026-09-24, 17 prod tasks). NOT re-measured on Flash — no
9496
+ // local BillingEntry row carries the Flash decider yet — so it is priced as
9497
+ // the product truth (`openrouter:product-truth`, 2.2¢ measured from
9498
+ // OpenRouter usage.cost): same model, same effort, same ≤6-photo input and a
9499
+ // JSON answer of similar size. Refine from usage.cost after the e2e. Fallback
9500
+ // only (OpenRouter's usage.cost is billed when present).
9501
+ "openrouter:usage-decision": {
9502
+ ourCostCents: 2.2,
9503
+ userPriceCents: 4.4
9504
+ },
9505
+ // Usage-fidelity QA judge (records misuse; regen gated separately behind
9506
+ // always on, bounded by USAGE_FIDELITY_MAX_REGENS). Sized off landing-image-qa beside it: same
9507
+ // shape of call (1-2 images, a verdict), so quoted at the same rate until
9508
+ // it has its own BillingEntry volume to re-derive from.
9509
+ "openrouter:usage-fidelity": {
9510
+ ourCostCents: 1.88,
9511
+ userPriceCents: 3.76
9512
+ },
9298
9513
  // Landing commercial-facts extraction (gemini-3.8-flash, ONE text-only call over
9299
9514
  // the seller's offer/context/features). Sized off the image-QA key beside it,
9300
9515
  // which measured 1.88¢ at ~11.1K prompt tokens: this prompt is capped at ~3K in
@@ -10033,6 +10248,24 @@ var DEFAULT_PRICING = {
10033
10248
  ourCostCents: 0.2,
10034
10249
  userPriceCents: 0.4
10035
10250
  },
10251
+ // Product truth (Gemini 3.8 Flash effort:'high', ≤6 photos + all seller text, once per
10252
+ // extract, always on). Plan C spike 2026-09-24: 6 calls, US$0.132 total ⇒
10253
+ // 2.2¢/call measured from OpenRouter usage.cost. Replaces usage spec + functional demo +
10254
+ // product-use sheet (+ landing visual-context-fusion) on the same task. Fallback only.
10255
+ "openrouter:product-truth": {
10256
+ ourCostCents: 2.2,
10257
+ userPriceCents: 4.4
10258
+ },
10259
+ // Landing creative director, «categorías» arm (Gemini 3.8 Flash effort:'high', ≤6 photos +
10260
+ // all seller text + the section plan, once per classic landing behind
10261
+ // LANDING_DIRECTOR_ENABLED). ESTIMATE, not measured: same call shape as the product truth
10262
+ // (2.2¢) with a ~2-3x longer JSON answer (one content spec per section) ⇒ ~4¢. Replaces
10263
+ // the visual brief (4¢) + visual-context fusion (0.4¢) + usage decision (2.2¢) on that
10264
+ // landing. Refine from OpenRouter usage.cost after the e2e. Fallback only.
10265
+ "openrouter:landing-director": {
10266
+ ourCostCents: 4,
10267
+ userPriceCents: 8
10268
+ },
10036
10269
  // Unit = one reference-grouping pass over the user's product images (0.1241¢).
10037
10270
  "openrouter:product-reference-grouping": {
10038
10271
  ourCostCents: 0.15,
@@ -10646,6 +10879,7 @@ var MixSceneResult = z48.object({
10646
10879
  index: z48.number().int().positive(),
10647
10880
  status: z48.enum([
10648
10881
  "queued",
10882
+ "submitting",
10649
10883
  "submitted",
10650
10884
  "done",
10651
10885
  "failed"
@@ -13244,7 +13478,7 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
13244
13478
  ], [
13245
13479
  "product_costing"
13246
13480
  ]),
13247
- defineCapability("create_creative_variant", "6a777fbe7ad17966d16502e8872e15e4db2dcfacdfd2992d24853340d4f22a00", "credit_consuming", "creative", [
13481
+ defineCapability("create_creative_variant", "3e02399a7785df328f1c4d63cfbb6d7481abaac9f91d4a5cf51d410e657d63aa", "credit_consuming", "creative", [
13248
13482
  "credit_consuming",
13249
13483
  "state_change"
13250
13484
  ], [
@@ -13494,7 +13728,9 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
13494
13728
  // MCP 7.0.0 (Task 11, .agent/plans/2026-09-15-model-agnostic-video-routes.md):
13495
13729
  // `freeProModel` retired in favor of `modelId` (registry ids). Recomputed
13496
13730
  // from `pnpm --filter @productmaker/mcp test` printing the real digest.
13497
- "ead1f563a1915452157628d023ba203ecfa8f502a48259a5b5b15be4c84bf768",
13731
+ // Re-baselined 2026-09-26: `idempotencyKey` field description corrected —
13732
+ // paid tools ignore caller-provided keys and derive their own semantic key.
13733
+ "35616a32a42a0bc062218fdfbb4cc7f4ecfa814571fb86b4f11ef9e4184855e1",
13498
13734
  "credit_consuming",
13499
13735
  "creative",
13500
13736
  [
@@ -13550,7 +13786,7 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
13550
13786
  "video"
13551
13787
  ]
13552
13788
  ),
13553
- defineCapability("generate_image_creatives", "eb2c3f6a0d8974ccf33c42f2b4cc9e09918dabf29182acce1418ea2842a01ec3", "credit_consuming", "creative", [
13789
+ defineCapability("generate_image_creatives", "2f1698b623d98645b7c618a17988f3aac30f403a9c3f3dc26eb5bdbd9c6ef23e", "credit_consuming", "creative", [
13554
13790
  "credit_consuming",
13555
13791
  "state_change"
13556
13792
  ], [
@@ -14947,79 +15183,696 @@ var WhatsAppPresentationManifestSchema = z67.object({
14947
15183
  }
14948
15184
  });
14949
15185
 
14950
- // ../../packages/types/src/flight-recorder.ts
15186
+ // ../../packages/types/src/usage-decision.ts
14951
15187
  import { z as z68 } from "zod";
14952
- var FlightRecorderGenerationTypeSchema = z68.enum([
15188
+
15189
+ // ../../packages/types/src/text-clamp.ts
15190
+ function clampAtWordBoundary(value, max) {
15191
+ if (typeof value !== "string") return value;
15192
+ if (value.length <= max) return value;
15193
+ const cut = value.slice(0, max);
15194
+ const lastSpace = cut.lastIndexOf(" ");
15195
+ const atWordBoundary = lastSpace > 0 ? cut.slice(0, lastSpace) : cut;
15196
+ return atWordBoundary.replace(/[,;:\s]+$/, "");
15197
+ }
15198
+ __name(clampAtWordBoundary, "clampAtWordBoundary");
15199
+ function clampStringArray(max) {
15200
+ return (value) => {
15201
+ if (!Array.isArray(value)) return value;
15202
+ const nonEmpty = value.filter((item) => typeof item === "string" && item.trim().length > 0);
15203
+ return nonEmpty.slice(0, max);
15204
+ };
15205
+ }
15206
+ __name(clampStringArray, "clampStringArray");
15207
+
15208
+ // ../../packages/types/src/usage-decision.ts
15209
+ var USAGE_SCENE_RULE_MAX_CHARS = 240;
15210
+ var USAGE_DECISION_MAX_REQUIRED_PARTS = 6;
15211
+ var USAGE_DECISION_MAX_ALTERNATE_USES = 4;
15212
+ var USAGE_DECISION_MAX_MISUSE = 5;
15213
+ function clampRule(value) {
15214
+ return clampAtWordBoundary(value, USAGE_SCENE_RULE_MAX_CHARS);
15215
+ }
15216
+ __name(clampRule, "clampRule");
15217
+ var UsageDecisionSchema = z68.object({
15218
+ productIdentity: z68.string().min(1),
15219
+ endUser: z68.object({
15220
+ who: z68.string(),
15221
+ appliedTo: z68.string()
15222
+ }),
15223
+ promise: z68.string().min(1),
15224
+ primaryUse: z68.object({
15225
+ scene: z68.string().min(1),
15226
+ postureAndGrip: z68.string(),
15227
+ contactPoint: z68.string(),
15228
+ requiredParts: z68.preprocess(clampStringArray(USAGE_DECISION_MAX_REQUIRED_PARTS), z68.array(z68.string()).max(USAGE_DECISION_MAX_REQUIRED_PARTS))
15229
+ }),
15230
+ validAlternateUses: z68.preprocess(clampStringArray(USAGE_DECISION_MAX_ALTERNATE_USES), z68.array(z68.string()).max(USAGE_DECISION_MAX_ALTERNATE_USES)),
15231
+ misuse: z68.preprocess(clampStringArray(USAGE_DECISION_MAX_MISUSE), z68.array(z68.string().min(1)).max(USAGE_DECISION_MAX_MISUSE)),
15232
+ useSceneRule: z68.preprocess(clampRule, z68.string().min(1).max(USAGE_SCENE_RULE_MAX_CHARS)),
15233
+ /**
15234
+ * True when the promise can ONLY be shown with the primary use (the pain is bending over and
15235
+ * the product has an extension pole: without the pole there is no promise); false when the
15236
+ * valid alternates sell it equally. Drives whether an alternate use in a use scene is a
15237
+ * failure (`requirePrimary` in the usage judge) and whether the prompts' primary use is
15238
+ * mandatory. Optional and fail-safe: missing (decisions stored before the field) or not a
15239
+ * boolean ⇒ `undefined`, read as false by every caller — never a rejected decision.
15240
+ */
15241
+ primaryRequired: z68.boolean().optional().catch(void 0),
15242
+ confidence: z68.number().min(0).max(1),
15243
+ modelId: z68.string(),
15244
+ decidedAt: z68.string()
15245
+ });
15246
+
15247
+ // ../../packages/types/src/product-truth.ts
15248
+ import { z as z70 } from "zod";
15249
+
15250
+ // ../../packages/types/src/lenient-zod.ts
15251
+ import { z as z69 } from "zod";
15252
+ function trimAndClamp(value, max) {
15253
+ if (typeof value !== "string") return value;
15254
+ return clampAtWordBoundary(value.replace(/\s+/g, " ").trim(), max);
15255
+ }
15256
+ __name(trimAndClamp, "trimAndClamp");
15257
+ function lenientText(max) {
15258
+ return z69.preprocess((v) => trimAndClamp(v, max), z69.string().min(1).max(max));
15259
+ }
15260
+ __name(lenientText, "lenientText");
15261
+ function lenientOptionalText(max) {
15262
+ return z69.preprocess((v) => {
15263
+ if (typeof v !== "string") return void 0;
15264
+ const clamped = trimAndClamp(v, max);
15265
+ return typeof clamped === "string" && clamped.length > 0 ? clamped : void 0;
15266
+ }, z69.string().max(max).optional());
15267
+ }
15268
+ __name(lenientOptionalText, "lenientOptionalText");
15269
+ function lenientTextList(maxItems, maxChars) {
15270
+ return z69.preprocess((v) => {
15271
+ if (!Array.isArray(v)) return [];
15272
+ return v.map((item) => trimAndClamp(item, maxChars)).filter((item) => typeof item === "string" && item.length > 0).slice(0, maxItems);
15273
+ }, z69.array(z69.string().min(1).max(maxChars)).max(maxItems));
15274
+ }
15275
+ __name(lenientTextList, "lenientTextList");
15276
+ function lenientList(item, maxItems) {
15277
+ return z69.preprocess((v) => {
15278
+ if (!Array.isArray(v)) return [];
15279
+ return v.filter((entry) => item.safeParse(entry).success).slice(0, maxItems);
15280
+ }, z69.array(item).max(maxItems));
15281
+ }
15282
+ __name(lenientList, "lenientList");
15283
+
15284
+ // ../../packages/types/src/product-truth.ts
15285
+ var PRODUCT_TRUTH_SCHEMA_VERSION = 1;
15286
+ var PRODUCT_TRUTH_CAPS = {
15287
+ whatItIs: 160,
15288
+ deliveryForm: 120,
15289
+ distinctiveMarks: 6,
15290
+ distinctiveMarkChars: 160,
15291
+ parts: 12,
15292
+ partNameChars: 80,
15293
+ criticalFeatures: 6,
15294
+ connections: 6,
15295
+ connectionChars: 80,
15296
+ states: 4,
15297
+ stateKeyChars: 60,
15298
+ stateLabelChars: 80,
15299
+ partsPresent: 10,
15300
+ orientationInUseChars: 200,
15301
+ appliedToChars: 120,
15302
+ environmentChars: 120,
15303
+ summaryChars: 200,
15304
+ sceneChars: 300,
15305
+ postureAndGripChars: 160,
15306
+ contactPointChars: 120,
15307
+ steps: 8,
15308
+ stepNameChars: 120,
15309
+ stepInstructionChars: 300,
15310
+ visibleEffectChars: 200,
15311
+ beforeStateChars: 200,
15312
+ requiredProps: 8,
15313
+ requiredPropChars: 120,
15314
+ alternates: 3,
15315
+ misuse: 5,
15316
+ misuseChars: 200,
15317
+ mustShowFeatures: 5,
15318
+ mustShowFeatureChars: 100,
15319
+ safety: 5,
15320
+ safetyChars: 200,
15321
+ forbiddenClaims: 8,
15322
+ unsupportedClaims: 5,
15323
+ claimChars: 200,
15324
+ visualSummaryEnChars: 400
15325
+ };
15326
+ var PRODUCT_TRUTH_END_USER_AGE_BANDS = [
15327
+ "baby",
15328
+ "child",
15329
+ "teen",
15330
+ "adult",
15331
+ "any"
15332
+ ];
15333
+ var PRODUCT_TRUTH_END_USER_GENDERS = [
15334
+ "female",
15335
+ "male",
15336
+ "any"
15337
+ ];
15338
+ var PRODUCT_TRUTH_UNKNOWN_REASONS = [
15339
+ "model_unsure",
15340
+ "no_signal",
15341
+ "invalid_response",
15342
+ "provider_error"
15343
+ ];
15344
+ var text = lenientText;
15345
+ var optionalText = lenientOptionalText;
15346
+ var textList = lenientTextList;
15347
+ function toProductTruthStateKey(value) {
15348
+ if (typeof value !== "string") return void 0;
15349
+ const slug = value.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, PRODUCT_TRUTH_CAPS.stateKeyChars);
15350
+ return slug.length > 0 ? slug : void 0;
15351
+ }
15352
+ __name(toProductTruthStateKey, "toProductTruthStateKey");
15353
+ var stateKey = z70.preprocess(toProductTruthStateKey, z70.string().min(1).max(PRODUCT_TRUTH_CAPS.stateKeyChars));
15354
+ var optionalStateKey = z70.preprocess(toProductTruthStateKey, z70.string().min(1).max(PRODUCT_TRUTH_CAPS.stateKeyChars).optional());
15355
+ var AGE_BAND_ALIASES = {
15356
+ baby: "baby",
15357
+ babies: "baby",
15358
+ infant: "baby",
15359
+ infants: "baby",
15360
+ toddler: "child",
15361
+ child: "child",
15362
+ children: "child",
15363
+ kid: "child",
15364
+ kids: "child",
15365
+ teen: "teen",
15366
+ teens: "teen",
15367
+ teenager: "teen",
15368
+ teenagers: "teen",
15369
+ adult: "adult",
15370
+ adults: "adult",
15371
+ any: "any",
15372
+ all: "any",
15373
+ "all ages": "any"
15374
+ };
15375
+ var GENDER_ALIASES = {
15376
+ female: "female",
15377
+ woman: "female",
15378
+ women: "female",
15379
+ girl: "female",
15380
+ male: "male",
15381
+ man: "male",
15382
+ men: "male",
15383
+ boy: "male",
15384
+ any: "any",
15385
+ unisex: "any",
15386
+ both: "any",
15387
+ all: "any"
15388
+ };
15389
+ function matchAlias(raw, aliases) {
15390
+ if (typeof raw !== "string") return void 0;
15391
+ const text2 = raw.trim().toLowerCase();
15392
+ const exact = aliases[text2];
15393
+ if (exact) return exact;
15394
+ const words = new Set(text2.split(/[^a-z]+/).filter(Boolean));
15395
+ for (const [alias, value] of Object.entries(aliases)) {
15396
+ if (words.has(alias)) return value;
15397
+ }
15398
+ return void 0;
15399
+ }
15400
+ __name(matchAlias, "matchAlias");
15401
+ function normalizeEndUser(value) {
15402
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
15403
+ const v = value;
15404
+ const age = matchAlias(v.ageBand, AGE_BAND_ALIASES);
15405
+ const gender = matchAlias(v.gender, GENDER_ALIASES);
15406
+ if (!age || !gender || typeof v.usedByBuyer !== "boolean") return void 0;
15407
+ return {
15408
+ ageBand: age,
15409
+ gender,
15410
+ usedByBuyer: v.usedByBuyer
15411
+ };
15412
+ }
15413
+ __name(normalizeEndUser, "normalizeEndUser");
15414
+ var ProductTruthEndUserSchema = z70.object({
15415
+ ageBand: z70.enum(PRODUCT_TRUTH_END_USER_AGE_BANDS),
15416
+ gender: z70.enum(PRODUCT_TRUTH_END_USER_GENDERS),
15417
+ usedByBuyer: z70.boolean()
15418
+ });
15419
+ var MATERIAL_HINTS = ProductMorphology.shape.materialHints.unwrap().element.options;
15420
+ var DEMO_TYPES = FunctionalDemonstration.shape.demoType;
15421
+ var PartSchema = z70.object({
15422
+ name: text(PRODUCT_TRUTH_CAPS.partNameChars),
15423
+ kind: z70.enum([
15424
+ "rigid",
15425
+ "flexible",
15426
+ "joint"
15427
+ ]).catch("rigid"),
15428
+ count: z70.number().int().min(1).max(20).catch(1)
15429
+ });
15430
+ var CriticalFeatureSchema = z70.object({
15431
+ name: text(60),
15432
+ importance: text(120),
15433
+ antiPattern: optionalText(80),
15434
+ expectedCount: z70.number().int().min(1).max(20).optional().catch(void 0)
15435
+ });
15436
+ var ConnectionSchema = z70.object({
15437
+ part: text(PRODUCT_TRUTH_CAPS.connectionChars),
15438
+ attachesTo: text(PRODUCT_TRUTH_CAPS.connectionChars),
15439
+ where: optionalText(PRODUCT_TRUTH_CAPS.connectionChars)
15440
+ });
15441
+ var StateSchema = z70.object({
15442
+ key: stateKey,
15443
+ label: optionalText(PRODUCT_TRUTH_CAPS.stateLabelChars),
15444
+ partsPresent: textList(PRODUCT_TRUTH_CAPS.partsPresent, PRODUCT_TRUTH_CAPS.partNameChars),
15445
+ orientationInUse: text(PRODUCT_TRUTH_CAPS.orientationInUseChars),
15446
+ naturalOrientation: ProductNaturalOrientation.optional().catch(void 0)
15447
+ });
15448
+ var StepSchema = z70.object({
15449
+ name: text(PRODUCT_TRUTH_CAPS.stepNameChars),
15450
+ instruction: text(PRODUCT_TRUTH_CAPS.stepInstructionChars),
15451
+ kind: z70.enum([
15452
+ "setup",
15453
+ "use",
15454
+ "finish"
15455
+ ]).catch("use")
15456
+ });
15457
+ var AlternateSchema = z70.object({
15458
+ summary: text(PRODUCT_TRUTH_CAPS.summaryChars),
15459
+ stateKey: optionalStateKey,
15460
+ advertisedBySeller: z70.boolean().catch(false)
15461
+ });
15462
+ var DemoSchema = z70.object({
15463
+ demoType: DEMO_TYPES.nullable().catch(null),
15464
+ mustShowFeatures: textList(PRODUCT_TRUTH_CAPS.mustShowFeatures, PRODUCT_TRUTH_CAPS.mustShowFeatureChars),
15465
+ importance: z70.enum([
15466
+ "critical",
15467
+ "important",
15468
+ "nice_to_have"
15469
+ ]).catch("important")
15470
+ });
15471
+ var objectOrEmpty = /* @__PURE__ */ __name((v) => v && typeof v === "object" && !Array.isArray(v) ? v : {}, "objectOrEmpty");
15472
+ var ProductTruthBody = z70.object({
15473
+ identity: z70.object({
15474
+ whatItIs: text(PRODUCT_TRUTH_CAPS.whatItIs),
15475
+ deliveryForm: optionalText(PRODUCT_TRUTH_CAPS.deliveryForm),
15476
+ distinctiveMarks: textList(PRODUCT_TRUTH_CAPS.distinctiveMarks, PRODUCT_TRUTH_CAPS.distinctiveMarkChars)
15477
+ }),
15478
+ morphology: z70.preprocess(objectOrEmpty, z70.object({
15479
+ shape: ProductGeometryShape.optional().catch(void 0),
15480
+ crossSection: ProductCrossSection.optional().catch(void 0),
15481
+ cornerProfile: ProductCornerProfile.optional().catch(void 0),
15482
+ silhouetteWidthToHeightRatio: z70.number().min(0.05).max(20).optional().catch(void 0),
15483
+ parts: lenientList(PartSchema, PRODUCT_TRUTH_CAPS.parts),
15484
+ criticalFeatures: lenientList(CriticalFeatureSchema, PRODUCT_TRUTH_CAPS.criticalFeatures),
15485
+ shapeLockLevel: z70.enum([
15486
+ "strict",
15487
+ "moderate",
15488
+ "relaxed"
15489
+ ]).optional().catch(void 0),
15490
+ materialHints: z70.preprocess((v) => Array.isArray(v) ? [
15491
+ ...new Set(v.filter((m) => MATERIAL_HINTS.includes(m)))
15492
+ ].slice(0, 3) : [], z70.array(z70.enum(MATERIAL_HINTS)).max(3))
15493
+ })),
15494
+ assembly: z70.object({
15495
+ connections: lenientList(ConnectionSchema, PRODUCT_TRUTH_CAPS.connections),
15496
+ states: z70.preprocess((v) => {
15497
+ if (!Array.isArray(v)) return v;
15498
+ const seen = /* @__PURE__ */ new Set();
15499
+ return v.filter((entry) => {
15500
+ const parsed = StateSchema.safeParse(entry);
15501
+ if (!parsed.success || seen.has(parsed.data.key)) return false;
15502
+ seen.add(parsed.data.key);
15503
+ return true;
15504
+ }).slice(0, PRODUCT_TRUTH_CAPS.states);
15505
+ }, z70.array(StateSchema).min(1).max(PRODUCT_TRUTH_CAPS.states)),
15506
+ defaultStateKey: optionalStateKey
15507
+ }),
15508
+ use: z70.object({
15509
+ endUser: z70.preprocess(normalizeEndUser, ProductTruthEndUserSchema.optional()).catch(void 0),
15510
+ appliedTo: optionalText(PRODUCT_TRUTH_CAPS.appliedToChars),
15511
+ environment: optionalText(PRODUCT_TRUTH_CAPS.environmentChars),
15512
+ primary: z70.object({
15513
+ summary: text(PRODUCT_TRUTH_CAPS.summaryChars),
15514
+ scene: text(PRODUCT_TRUTH_CAPS.sceneChars),
15515
+ postureAndGrip: optionalText(PRODUCT_TRUTH_CAPS.postureAndGripChars),
15516
+ contactPoint: optionalText(PRODUCT_TRUTH_CAPS.contactPointChars),
15517
+ stateKey: optionalStateKey,
15518
+ steps: z70.preprocess((v) => Array.isArray(v) ? v.filter((s) => StepSchema.safeParse(s).success).slice(0, PRODUCT_TRUTH_CAPS.steps) : v, z70.array(StepSchema).min(1).max(PRODUCT_TRUTH_CAPS.steps)),
15519
+ visibleEffect: optionalText(PRODUCT_TRUTH_CAPS.visibleEffectChars),
15520
+ beforeState: optionalText(PRODUCT_TRUTH_CAPS.beforeStateChars),
15521
+ requiredProps: textList(PRODUCT_TRUTH_CAPS.requiredProps, PRODUCT_TRUTH_CAPS.requiredPropChars)
15522
+ }),
15523
+ alternates: lenientList(AlternateSchema, PRODUCT_TRUTH_CAPS.alternates),
15524
+ misuse: textList(PRODUCT_TRUTH_CAPS.misuse, PRODUCT_TRUTH_CAPS.misuseChars),
15525
+ demo: z70.preprocess(objectOrEmpty, DemoSchema),
15526
+ safety: textList(PRODUCT_TRUTH_CAPS.safety, PRODUCT_TRUTH_CAPS.safetyChars)
15527
+ }),
15528
+ claims: z70.preprocess(objectOrEmpty, z70.object({
15529
+ forbidden: textList(PRODUCT_TRUTH_CAPS.forbiddenClaims, PRODUCT_TRUTH_CAPS.claimChars),
15530
+ unsupported: textList(PRODUCT_TRUTH_CAPS.unsupportedClaims, PRODUCT_TRUTH_CAPS.claimChars)
15531
+ })),
15532
+ visualSummaryEn: optionalText(PRODUCT_TRUTH_CAPS.visualSummaryEnChars),
15533
+ evidence: z70.preprocess(objectOrEmpty, z70.object({
15534
+ photoIndexForState: z70.preprocess((v) => {
15535
+ const out = {};
15536
+ if (!v || typeof v !== "object" || Array.isArray(v)) return out;
15537
+ for (const [rawKey, rawIndexes] of Object.entries(v)) {
15538
+ const key = toProductTruthStateKey(rawKey);
15539
+ if (!key || !Array.isArray(rawIndexes)) continue;
15540
+ out[key] = rawIndexes.filter((i) => typeof i === "number" && Number.isInteger(i) && i >= 0 && i < 20);
15541
+ }
15542
+ return out;
15543
+ }, z70.record(z70.array(z70.number().int().min(0).max(19))))
15544
+ })),
15545
+ confidence: z70.preprocess((v) => typeof v === "number" && Number.isFinite(v) ? Math.min(1, Math.max(0, v)) : 0, z70.number().min(0).max(1))
15546
+ });
15547
+ function resolveStateRefs(body) {
15548
+ const keys = new Set(body.assembly.states.map((s) => s.key));
15549
+ const firstKey = body.assembly.states[0]?.key ?? "";
15550
+ const defaultStateKey = body.assembly.defaultStateKey && keys.has(body.assembly.defaultStateKey) ? body.assembly.defaultStateKey : firstKey;
15551
+ const primaryKey = body.use.primary.stateKey && keys.has(body.use.primary.stateKey) ? body.use.primary.stateKey : defaultStateKey;
15552
+ const photoIndexForState = Object.fromEntries(Object.entries(body.evidence.photoIndexForState).filter(([key]) => keys.has(key)));
15553
+ return {
15554
+ ...body,
15555
+ assembly: {
15556
+ ...body.assembly,
15557
+ defaultStateKey
15558
+ },
15559
+ use: {
15560
+ ...body.use,
15561
+ primary: {
15562
+ ...body.use.primary,
15563
+ stateKey: primaryKey
15564
+ },
15565
+ alternates: body.use.alternates.map((alt) => ({
15566
+ ...alt,
15567
+ stateKey: alt.stateKey && keys.has(alt.stateKey) ? alt.stateKey : defaultStateKey
15568
+ }))
15569
+ },
15570
+ evidence: {
15571
+ ...body.evidence,
15572
+ photoIndexForState
15573
+ }
15574
+ };
15575
+ }
15576
+ __name(resolveStateRefs, "resolveStateRefs");
15577
+ var ProductTruthModelResponseSchema = ProductTruthBody.transform(resolveStateRefs);
15578
+ var provenance = {
15579
+ schemaVersion: z70.literal(PRODUCT_TRUTH_SCHEMA_VERSION),
15580
+ modelId: z70.string().min(1),
15581
+ derivedAt: z70.string().min(1),
15582
+ /** Hash of (image keys + seller text + features): a reader that sees a different current
15583
+ * fingerprint treats the truth as absent. See `computeProductTruthFingerprint` in packages/ai. */
15584
+ sourceFingerprint: z70.string().min(1),
15585
+ /** The seller upload KEYS (deduped, sorted) the fingerprint was computed over. Stored so a
15586
+ * reader can recompute the CURRENT fingerprint from today's seller text + features without
15587
+ * knowing the extract job's inputs (plan Global Constraint "Invalidez"). */
15588
+ sourceImageKeys: z70.array(z70.string().min(1)).max(20).optional()
15589
+ };
15590
+ var ProductTruthKnownSchema = ProductTruthBody.extend({
15591
+ ...provenance,
15592
+ known: z70.literal(true)
15593
+ }).transform(resolveStateRefs);
15594
+ var ProductTruthUnknownSchema = z70.object({
15595
+ schemaVersion: z70.literal(PRODUCT_TRUTH_SCHEMA_VERSION),
15596
+ known: z70.literal(false),
15597
+ unknownReason: z70.enum(PRODUCT_TRUTH_UNKNOWN_REASONS),
15598
+ modelId: z70.string().optional(),
15599
+ derivedAt: z70.string().min(1),
15600
+ sourceFingerprint: z70.string().min(1),
15601
+ sourceImageKeys: z70.array(z70.string().min(1)).max(20).optional()
15602
+ });
15603
+ var ProductTruthSchema = z70.union([
15604
+ ProductTruthKnownSchema,
15605
+ ProductTruthUnknownSchema
15606
+ ]);
15607
+
15608
+ // ../../packages/types/src/landing-director.ts
15609
+ import { z as z71 } from "zod";
15610
+ var LANDING_DIRECTOR_SCHEMA_VERSION = 1;
15611
+ var LANDING_DIRECTOR_ARM = "categories";
15612
+ var LANDING_DIRECTOR_CAPS = {
15613
+ productIdentity: 200,
15614
+ parts: 8,
15615
+ partName: 40,
15616
+ promise: 200,
15617
+ endUserWho: 80,
15618
+ endUserAppliedTo: 80,
15619
+ forbiddenClaims: 10,
15620
+ claimChars: 80,
15621
+ misuse: 5,
15622
+ misuseChars: 120,
15623
+ assembledStates: 3,
15624
+ statePartsMax: 6,
15625
+ orientation: 160,
15626
+ handling: 140,
15627
+ primaryScene: 200,
15628
+ postureAndGrip: 140,
15629
+ contactPoint: 100,
15630
+ alternates: 3,
15631
+ alternateChars: 120,
15632
+ ambiguities: 4,
15633
+ ambiguityChars: 160,
15634
+ sectionKeyChars: 40,
15635
+ personWho: 80,
15636
+ personPosture: 120,
15637
+ personGrip: 120,
15638
+ scene: 450,
15639
+ panels: 4,
15640
+ panelScene: 200,
15641
+ panelCaption: 60,
15642
+ /** Rendered text: an item over its cap is DROPPED, never cut. The headline cap is 80, not
15643
+ * the design's 70: a dropped headline costs the whole director headline, and 7-10 Spanish
15644
+ * words routinely run 70-80 chars. */
15645
+ headline: 80,
15646
+ subheadline: 90,
15647
+ labels: 3,
15648
+ labelChars: 40,
15649
+ captions: 3,
15650
+ captionChars: 60,
15651
+ props: 4,
15652
+ propChars: 40,
15653
+ mustNotShow: 5,
15654
+ mustNotShowChars: 80
15655
+ };
15656
+ var STATE_KEY_PATTERN = /^[a-z][a-z_]{1,23}$/;
15657
+ function toLandingDirectorStateKey(value) {
15658
+ if (typeof value !== "string") return void 0;
15659
+ const slug = value.toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/[^a-z]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 24).replace(/_+$/g, "");
15660
+ return STATE_KEY_PATTERN.test(slug) ? slug : void 0;
15661
+ }
15662
+ __name(toLandingDirectorStateKey, "toLandingDirectorStateKey");
15663
+ var stateKey2 = z71.preprocess(toLandingDirectorStateKey, z71.string().regex(STATE_KEY_PATTERN));
15664
+ function renderedOptionalText(max) {
15665
+ return z71.preprocess((v) => {
15666
+ if (typeof v !== "string") return void 0;
15667
+ const trimmed = v.replace(/\s+/g, " ").trim();
15668
+ return trimmed.length > 0 && trimmed.length <= max ? trimmed : void 0;
15669
+ }, z71.string().min(1).max(max).optional());
15670
+ }
15671
+ __name(renderedOptionalText, "renderedOptionalText");
15672
+ function renderedTextList(maxItems, maxChars) {
15673
+ return z71.preprocess((v) => {
15674
+ if (!Array.isArray(v)) return [];
15675
+ return v.map((item) => typeof item === "string" ? item.replace(/\s+/g, " ").trim() : "").filter((item) => item.length > 0 && item.length <= maxChars).slice(0, maxItems);
15676
+ }, z71.array(z71.string().min(1).max(maxChars)).max(maxItems));
15677
+ }
15678
+ __name(renderedTextList, "renderedTextList");
15679
+ var C = LANDING_DIRECTOR_CAPS;
15680
+ var PartSchema2 = z71.object({
15681
+ name: lenientText(C.partName),
15682
+ critical: z71.boolean().catch(false)
15683
+ });
15684
+ var AssembledStateSchema = z71.object({
15685
+ key: stateKey2,
15686
+ parts: lenientTextList(C.statePartsMax, C.partName),
15687
+ orientation: lenientText(C.orientation),
15688
+ handling: lenientOptionalText(C.handling)
15689
+ });
15690
+ var END_USER_GENDERS = [
15691
+ "female",
15692
+ "male",
15693
+ "any"
15694
+ ];
15695
+ var END_USER_AGE_BANDS = [
15696
+ "child",
15697
+ "teen",
15698
+ "adult",
15699
+ "senior",
15700
+ "any"
15701
+ ];
15702
+ var EndUserSchema = z71.object({
15703
+ who: lenientText(C.endUserWho),
15704
+ appliedTo: lenientOptionalText(C.endUserAppliedTo),
15705
+ gender: z71.enum(END_USER_GENDERS).catch("any"),
15706
+ ageBand: z71.enum(END_USER_AGE_BANDS).catch("any")
15707
+ });
15708
+ var LandingDirectorGlobalSchema = z71.object({
15709
+ productIdentity: lenientText(C.productIdentity),
15710
+ parts: lenientList(PartSchema2, C.parts),
15711
+ promise: lenientText(C.promise),
15712
+ endUser: EndUserSchema,
15713
+ forbiddenClaims: lenientTextList(C.forbiddenClaims, C.claimChars),
15714
+ misuse: lenientTextList(C.misuse, C.misuseChars),
15715
+ assembledStates: z71.preprocess((v) => {
15716
+ if (!Array.isArray(v)) return v;
15717
+ const seen = /* @__PURE__ */ new Set();
15718
+ return v.filter((entry) => {
15719
+ const parsed = AssembledStateSchema.safeParse(entry);
15720
+ if (!parsed.success || seen.has(parsed.data.key)) return false;
15721
+ seen.add(parsed.data.key);
15722
+ return true;
15723
+ }).slice(0, C.assembledStates);
15724
+ }, z71.array(AssembledStateSchema).min(1).max(C.assembledStates)),
15725
+ primaryUse: z71.object({
15726
+ assembledState: stateKey2,
15727
+ scene: lenientText(C.primaryScene),
15728
+ postureAndGrip: lenientText(C.postureAndGrip),
15729
+ contactPoint: lenientOptionalText(C.contactPoint)
15730
+ }),
15731
+ primaryRequired: z71.boolean().catch(false),
15732
+ validAlternateUses: lenientTextList(C.alternates, C.alternateChars),
15733
+ ambiguities: lenientTextList(C.ambiguities, C.ambiguityChars)
15734
+ });
15735
+ var LANDING_DIRECTOR_BODY_PARTS = [
15736
+ "hands",
15737
+ "face",
15738
+ "torso",
15739
+ "legs",
15740
+ "feet",
15741
+ "full_body"
15742
+ ];
15743
+ var PersonSchema = z71.object({
15744
+ who: lenientText(C.personWho),
15745
+ posture: lenientText(C.personPosture),
15746
+ grip: lenientOptionalText(C.personGrip),
15747
+ bodyPartsVisible: z71.preprocess((v) => Array.isArray(v) ? [
15748
+ ...new Set(v.filter((part) => LANDING_DIRECTOR_BODY_PARTS.includes(part)))
15749
+ ] : [], z71.array(z71.enum(LANDING_DIRECTOR_BODY_PARTS)))
15750
+ });
15751
+ var LandingDirectorTextSchema = z71.object({
15752
+ headline: renderedOptionalText(C.headline),
15753
+ subheadline: renderedOptionalText(C.subheadline),
15754
+ labels: renderedTextList(C.labels, C.labelChars),
15755
+ captions: renderedTextList(C.captions, C.captionChars)
15756
+ });
15757
+ var PanelSchema = z71.object({
15758
+ scene: lenientText(C.panelScene),
15759
+ caption: renderedOptionalText(C.panelCaption)
15760
+ });
15761
+ var PRODUCT_IN_FRAME_VALUES = [
15762
+ "none",
15763
+ "packshot",
15764
+ "in_use"
15765
+ ];
15766
+ var LandingDirectorSectionSchema = z71.object({
15767
+ sectionKey: z71.string().trim().min(1).max(C.sectionKeyChars),
15768
+ /** Present only in the «director» arm. Kept as a flag so the «categorías» validation can
15769
+ * invalidate the section (design §2.3) instead of silently dropping the model's choice. */
15770
+ hasComposition: z71.boolean().default(false),
15771
+ productInFrame: z71.enum(PRODUCT_IN_FRAME_VALUES),
15772
+ assembledState: z71.preprocess((v) => v === null || v === void 0 ? null : toLandingDirectorStateKey(v), z71.string().regex(STATE_KEY_PATTERN).nullable()),
15773
+ usesPrimary: z71.boolean().catch(false),
15774
+ person: z71.preprocess((v) => v === void 0 ? null : v, PersonSchema.nullable()),
15775
+ scene: lenientText(C.scene),
15776
+ panels: lenientList(PanelSchema, C.panels),
15777
+ textToRender: z71.preprocess((v) => v && typeof v === "object" && !Array.isArray(v) ? v : {}, LandingDirectorTextSchema),
15778
+ props: lenientTextList(C.props, C.propChars),
15779
+ mustNotShow: lenientTextList(C.mustNotShow, C.mustNotShowChars)
15780
+ });
15781
+ var LandingDirectorOutputSchema = z71.object({
15782
+ version: z71.literal(LANDING_DIRECTOR_SCHEMA_VERSION),
15783
+ confidence: z71.number().min(0).max(1),
15784
+ global: LandingDirectorGlobalSchema,
15785
+ sections: z71.array(LandingDirectorSectionSchema)
15786
+ });
15787
+ var StoredLandingDirectorSchema = z71.object({
15788
+ version: z71.literal(LANDING_DIRECTOR_SCHEMA_VERSION),
15789
+ arm: z71.literal(LANDING_DIRECTOR_ARM),
15790
+ modelId: z71.string().min(1),
15791
+ generatedAt: z71.string().min(1),
15792
+ confidence: z71.number().min(0).max(1),
15793
+ /** The plan the answer was validated against (sectionKey, or the role on classic). */
15794
+ planSectionKeys: z71.array(z71.string().min(1)),
15795
+ output: LandingDirectorOutputSchema,
15796
+ /** Sections that fell back to the category composition, and why. */
15797
+ fallbackSections: z71.array(z71.object({
15798
+ sectionKey: z71.string().min(1),
15799
+ reason: z71.string().min(1)
15800
+ }))
15801
+ });
15802
+
15803
+ // ../../packages/types/src/flight-recorder.ts
15804
+ import { z as z72 } from "zod";
15805
+ var FlightRecorderGenerationTypeSchema = z72.enum([
14953
15806
  "initial",
14954
15807
  "regeneration",
14955
15808
  "edit",
14956
15809
  "retry"
14957
15810
  ]);
14958
- var FlightRecorderProviderSchema = z68.enum([
15811
+ var FlightRecorderProviderSchema = z72.enum([
14959
15812
  "kie",
14960
15813
  "fal"
14961
15814
  ]);
14962
- var FlightRecorderElementSchema = z68.object({
14963
- name: z68.string().optional(),
14964
- description: z68.string().optional(),
14965
- frontalImageUrl: z68.string().optional(),
14966
- referenceImageUrls: z68.array(z68.string()).optional(),
14967
- videoUrl: z68.string().optional()
14968
- });
14969
- var FlightRecorderDistillationSchema = z68.object({
14970
- actorGender: z68.enum([
15815
+ var FlightRecorderElementSchema = z72.object({
15816
+ name: z72.string().optional(),
15817
+ description: z72.string().optional(),
15818
+ frontalImageUrl: z72.string().optional(),
15819
+ referenceImageUrls: z72.array(z72.string()).optional(),
15820
+ videoUrl: z72.string().optional()
15821
+ });
15822
+ var FlightRecorderDistillationSchema = z72.object({
15823
+ actorGender: z72.enum([
14971
15824
  "female",
14972
15825
  "male",
14973
15826
  "mixed"
14974
- ]).or(z68.string()).optional(),
14975
- voiceId: z68.string().optional(),
14976
- cameraMotion: z68.string().optional()
14977
- }).catchall(z68.unknown());
14978
- var FlightRecorderEntrySchema = z68.object({
14979
- schemaVersion: z68.literal(1).optional(),
14980
- id: z68.string().min(1),
14981
- taskId: z68.string().min(1),
14982
- runId: z68.string().min(1),
14983
- sequence: z68.number().int().nonnegative(),
14984
- timestamp: z68.string().min(1),
14985
- provider: FlightRecorderProviderSchema.or(z68.string()),
14986
- model: z68.string().min(1),
14987
- prompt: z68.string(),
14988
- durationSeconds: z68.number().nonnegative(),
14989
- aspectRatio: z68.string().optional(),
14990
- resolution: z68.string().optional(),
14991
- startImageUrl: z68.string().optional(),
14992
- endImageUrl: z68.string().optional(),
14993
- generateAudio: z68.boolean().optional(),
15827
+ ]).or(z72.string()).optional(),
15828
+ voiceId: z72.string().optional(),
15829
+ cameraMotion: z72.string().optional()
15830
+ }).catchall(z72.unknown());
15831
+ var FlightRecorderEntrySchema = z72.object({
15832
+ schemaVersion: z72.literal(1).optional(),
15833
+ id: z72.string().min(1),
15834
+ taskId: z72.string().min(1),
15835
+ runId: z72.string().min(1),
15836
+ sequence: z72.number().int().nonnegative(),
15837
+ timestamp: z72.string().min(1),
15838
+ provider: FlightRecorderProviderSchema.or(z72.string()),
15839
+ model: z72.string().min(1),
15840
+ prompt: z72.string(),
15841
+ durationSeconds: z72.number().nonnegative(),
15842
+ aspectRatio: z72.string().optional(),
15843
+ resolution: z72.string().optional(),
15844
+ startImageUrl: z72.string().optional(),
15845
+ endImageUrl: z72.string().optional(),
15846
+ generateAudio: z72.boolean().optional(),
14994
15847
  // Correlation & pipeline metadata
14995
- angleKey: z68.string().optional(),
14996
- sceneIndex: z68.number().int().nonnegative().optional(),
15848
+ angleKey: z72.string().optional(),
15849
+ sceneIndex: z72.number().int().nonnegative().optional(),
14997
15850
  generationType: FlightRecorderGenerationTypeSchema,
14998
- operationId: z68.string().min(1).optional(),
14999
- revisionId: z68.string().min(1).optional(),
15000
- attemptNumber: z68.number().int().nonnegative().default(1),
15851
+ operationId: z72.string().min(1).optional(),
15852
+ revisionId: z72.string().min(1).optional(),
15853
+ attemptNumber: z72.number().int().nonnegative().default(1),
15001
15854
  // Elements & Distillation
15002
- elements: z68.array(FlightRecorderElementSchema).optional(),
15855
+ elements: z72.array(FlightRecorderElementSchema).optional(),
15003
15856
  distillation: FlightRecorderDistillationSchema.optional(),
15004
15857
  // Raw payload (sanitized of secrets)
15005
- rawPayload: z68.record(z68.unknown())
15858
+ rawPayload: z72.record(z72.unknown())
15006
15859
  });
15007
- var FlightRecorderQuerySchema = z68.object({
15008
- taskId: z68.string().min(1),
15009
- runId: z68.string().optional(),
15010
- sceneIndex: z68.number().int().nonnegative().optional(),
15011
- angleKey: z68.string().optional(),
15860
+ var FlightRecorderQuerySchema = z72.object({
15861
+ taskId: z72.string().min(1),
15862
+ runId: z72.string().optional(),
15863
+ sceneIndex: z72.number().int().nonnegative().optional(),
15864
+ angleKey: z72.string().optional(),
15012
15865
  generationType: FlightRecorderGenerationTypeSchema.optional(),
15013
- cursor: z68.number().int().nonnegative().optional(),
15014
- limit: z68.number().int().positive().optional()
15866
+ cursor: z72.number().int().nonnegative().optional(),
15867
+ limit: z72.number().int().positive().optional()
15015
15868
  });
15016
- var FlightRecorderResponseSchema = z68.object({
15017
- taskId: z68.string().min(1),
15018
- runId: z68.string().min(1),
15019
- entries: z68.array(FlightRecorderEntrySchema),
15869
+ var FlightRecorderResponseSchema = z72.object({
15870
+ taskId: z72.string().min(1),
15871
+ runId: z72.string().min(1),
15872
+ entries: z72.array(FlightRecorderEntrySchema),
15020
15873
  lastCall: FlightRecorderEntrySchema.nullable(),
15021
- count: z68.number().int().nonnegative(),
15022
- nextCursor: z68.number().int().nonnegative().nullable()
15874
+ count: z72.number().int().nonnegative(),
15875
+ nextCursor: z72.number().int().nonnegative().nullable()
15023
15876
  });
15024
15877
 
15025
15878
  // src/response-body.ts
@@ -15139,7 +15992,10 @@ var ApiClient = class {
15139
15992
  method: "POST",
15140
15993
  headers: {
15141
15994
  ...authHeaders,
15142
- "Content-Type": "application/json"
15995
+ "Content-Type": "application/json",
15996
+ ...options?.idempotencyKey ? {
15997
+ "Idempotency-Key": options.idempotencyKey
15998
+ } : {}
15143
15999
  },
15144
16000
  body: JSON.stringify(body)
15145
16001
  }, options);
@@ -15261,7 +16117,7 @@ var ApiClient = class {
15261
16117
  };
15262
16118
 
15263
16119
  // src/index.ts
15264
- var MCP_VERSION = "8.1.0";
16120
+ var MCP_VERSION = "8.3.0";
15265
16121
 
15266
16122
  // src/meta-upload-app.ts
15267
16123
  import { readFileSync } from "fs";
@@ -15333,7 +16189,7 @@ function registerMediaUploadAppResource(server, resolveEndpoint) {
15333
16189
  __name(registerMediaUploadAppResource, "registerMediaUploadAppResource");
15334
16190
 
15335
16191
  // src/tools/tasks.ts
15336
- import { z as z72 } from "zod";
16192
+ import { z as z76 } from "zod";
15337
16193
 
15338
16194
  // src/resolve-image.ts
15339
16195
  import { lookup as dnsLookup } from "dns/promises";
@@ -15894,10 +16750,10 @@ async function resolveMediaRef(ref, opts) {
15894
16750
  __name(resolveMediaRef, "resolveMediaRef");
15895
16751
 
15896
16752
  // src/tools/shared.ts
15897
- import { z as z69 } from "zod";
15898
- var Lang = z69.string().regex(/^[a-z]{2}(?:[-_][a-zA-Z]{2})?$/, "Usa un c\xF3digo de idioma BCP-47, por ejemplo es, en-US o pt-BR").max(10);
15899
- var Country = z69.string().min(2).max(3);
15900
- var LandingStrategyEnum = z69.enum([
16753
+ import { z as z73 } from "zod";
16754
+ var Lang = z73.string().regex(/^[a-z]{2}(?:[-_][a-zA-Z]{2})?$/, "Usa un c\xF3digo de idioma BCP-47, por ejemplo es, en-US o pt-BR").max(10);
16755
+ var Country = z73.string().min(2).max(3);
16756
+ var LandingStrategyEnum = z73.enum([
15901
16757
  "auto",
15902
16758
  "classic",
15903
16759
  "pain_solution",
@@ -15906,12 +16762,12 @@ var LandingStrategyEnum = z69.enum([
15906
16762
  "offer_value",
15907
16763
  "lifestyle_identity"
15908
16764
  ]);
15909
- var LandingImageFormatEnum = z69.enum([
16765
+ var LandingImageFormatEnum = z73.enum([
15910
16766
  "9:16",
15911
16767
  "4:5",
15912
16768
  "1:1"
15913
16769
  ]);
15914
- var VideoPresetEnum = z69.enum([
16770
+ var VideoPresetEnum = z73.enum([
15915
16771
  "auto",
15916
16772
  "ugc_dynamic",
15917
16773
  "ugc_how_to",
@@ -15924,21 +16780,21 @@ var VideoPresetEnum = z69.enum([
15924
16780
  "pixar_problem",
15925
16781
  "pixar_sassy"
15926
16782
  ]);
15927
- var VideoStructureEnum = z69.enum([
16783
+ var VideoStructureEnum = z73.enum([
15928
16784
  "single_take",
15929
16785
  "multi_scene"
15930
16786
  ]);
15931
- var VideoQualityEnum = z69.enum([
16787
+ var VideoQualityEnum = z73.enum([
15932
16788
  "economy",
15933
16789
  "medium",
15934
16790
  "pro"
15935
16791
  ]);
15936
- var VideoRouteIdEnum = z69.enum([
16792
+ var VideoRouteIdEnum = z73.enum([
15937
16793
  "single_take",
15938
16794
  "multi_scene",
15939
16795
  "free"
15940
16796
  ]);
15941
- var TaskCreativeVariantVideoPresetEnum = z69.enum([
16797
+ var TaskCreativeVariantVideoPresetEnum = z73.enum([
15942
16798
  "auto",
15943
16799
  "ugc_dynamic",
15944
16800
  "ugc_how_to",
@@ -15955,53 +16811,43 @@ var VIDEO_PRESET_RESOLUTION = {
15955
16811
  auto: null,
15956
16812
  ugc_dynamic: {
15957
16813
  videoStyle: "ai_dynamic",
15958
- narrativeStyle: "review",
15959
- videoModel: "veo"
16814
+ narrativeStyle: "review"
15960
16815
  },
15961
16816
  ugc_how_to: {
15962
16817
  videoStyle: "ai_dynamic",
15963
- narrativeStyle: "ugc_how_to",
15964
- videoModel: "veo"
16818
+ narrativeStyle: "ugc_how_to"
15965
16819
  },
15966
16820
  ugc_unboxing_chaos: {
15967
16821
  videoStyle: "ai_dynamic",
15968
- narrativeStyle: "ugc_unboxing_chaos",
15969
- videoModel: "veo"
16822
+ narrativeStyle: "ugc_unboxing_chaos"
15970
16823
  },
15971
16824
  problem_solution: {
15972
16825
  videoStyle: "ai_dynamic",
15973
- narrativeStyle: "problem_product_cta",
15974
- videoModel: "kling"
16826
+ narrativeStyle: "problem_product_cta"
15975
16827
  },
15976
16828
  pixar_problem: {
15977
16829
  videoStyle: "ai_dynamic",
15978
- narrativeStyle: "animated_problem_pixar",
15979
- videoModel: "kling"
16830
+ narrativeStyle: "animated_problem_pixar"
15980
16831
  },
15981
16832
  pixar_sassy: {
15982
16833
  videoStyle: "ai_dynamic",
15983
- narrativeStyle: "sassy_product_pixar",
15984
- videoModel: "seedance"
16834
+ narrativeStyle: "sassy_product_pixar"
15985
16835
  },
15986
16836
  product_showcase: {
15987
16837
  videoStyle: "product_only",
15988
- narrativeStyle: "product_showcase",
15989
- videoModel: "veo"
16838
+ narrativeStyle: "product_showcase"
15990
16839
  },
15991
16840
  wearable_showcase: {
15992
16841
  videoStyle: "ugc",
15993
- narrativeStyle: "wearable_showcase",
15994
- videoModel: "veo"
16842
+ narrativeStyle: "wearable_showcase"
15995
16843
  },
15996
16844
  cinematic: {
15997
16845
  videoStyle: "product_only",
15998
- narrativeStyle: "cinematic",
15999
- videoModel: "grok"
16846
+ narrativeStyle: "cinematic"
16000
16847
  },
16001
16848
  wild_card: {
16002
16849
  videoStyle: "product_only",
16003
- narrativeStyle: "wild_card",
16004
- videoModel: "grok"
16850
+ narrativeStyle: "wild_card"
16005
16851
  }
16006
16852
  };
16007
16853
  function resolveVideoPreset(preset) {
@@ -16009,7 +16855,7 @@ function resolveVideoPreset(preset) {
16009
16855
  return VIDEO_PRESET_RESOLUTION[preset] ?? null;
16010
16856
  }
16011
16857
  __name(resolveVideoPreset, "resolveVideoPreset");
16012
- var VideoTierEnum = z69.enum([
16858
+ var VideoTierEnum = z73.enum([
16013
16859
  "basic"
16014
16860
  ]);
16015
16861
  function resolveVideoTier(_tier) {
@@ -16049,22 +16895,22 @@ function assertSemanticVideoDurationCompatibility(structure, quality, durationSe
16049
16895
  }
16050
16896
  }
16051
16897
  __name(assertSemanticVideoDurationCompatibility, "assertSemanticVideoDurationCompatibility");
16052
- var VideoResolutionEnum = z69.enum([
16898
+ var VideoResolutionEnum = z73.enum([
16053
16899
  "480p",
16054
16900
  "720p"
16055
16901
  ]);
16056
- var CreativeGenderEnum = z69.enum([
16902
+ var CreativeGenderEnum = z73.enum([
16057
16903
  "female",
16058
16904
  "male",
16059
16905
  "mixed"
16060
16906
  ]);
16061
- var VideoStyleEnum = z69.enum([
16907
+ var VideoStyleEnum = z73.enum([
16062
16908
  "ugc",
16063
16909
  "cartoon_3d",
16064
16910
  "product_only",
16065
16911
  "ai_dynamic"
16066
16912
  ]);
16067
- var NarrativeStyleEnum = z69.enum([
16913
+ var NarrativeStyleEnum = z73.enum([
16068
16914
  "review",
16069
16915
  "ugc_how_to",
16070
16916
  "problem_product_cta",
@@ -16082,7 +16928,7 @@ var NarrativeStyleEnum = z69.enum([
16082
16928
  "ugc_demo_review",
16083
16929
  "market"
16084
16930
  ]);
16085
- var PublicNarrativeStyleEnum = z69.enum([
16931
+ var PublicNarrativeStyleEnum = z73.enum([
16086
16932
  "review",
16087
16933
  "ugc_how_to",
16088
16934
  "ugc_unboxing_chaos",
@@ -16095,7 +16941,7 @@ var PublicNarrativeStyleEnum = z69.enum([
16095
16941
  "sassy_product_pixar",
16096
16942
  "market"
16097
16943
  ]);
16098
- var VideoModelEnum = z69.enum([
16944
+ var TaskVideoModelEnum = z73.enum([
16099
16945
  "veo",
16100
16946
  "wan",
16101
16947
  "bytedance",
@@ -16103,29 +16949,29 @@ var VideoModelEnum = z69.enum([
16103
16949
  "grok",
16104
16950
  "kling"
16105
16951
  ]);
16106
- var ProductReferenceUserChoiceEnum = z69.enum([
16952
+ var ProductReferenceUserChoiceEnum = z73.enum([
16107
16953
  "same_product",
16108
16954
  "variants",
16109
16955
  "different_products",
16110
16956
  "combo_distinct"
16111
16957
  ]);
16112
- var ProductReferenceCandidateGroupingEnum = z69.enum([
16958
+ var ProductReferenceCandidateGroupingEnum = z73.enum([
16113
16959
  "same_product_same_variant",
16114
16960
  "same_product_different_variants",
16115
16961
  "different_products",
16116
16962
  "unclear"
16117
16963
  ]);
16118
- var ProductReferenceScopeEnum = z69.enum([
16964
+ var ProductReferenceScopeEnum = z73.enum([
16119
16965
  "base",
16120
16966
  "video_view_pack"
16121
16967
  ]);
16122
- var ProductReferenceResolutionDecisionEnum = z69.enum([
16968
+ var ProductReferenceResolutionDecisionEnum = z73.enum([
16123
16969
  "approve",
16124
16970
  "reject",
16125
16971
  "observed_only",
16126
16972
  "correct_view"
16127
16973
  ]);
16128
- var ProductReferenceRejectionReasonEnum = z69.enum([
16974
+ var ProductReferenceRejectionReasonEnum = z73.enum([
16129
16975
  "replacement_requested",
16130
16976
  "wrong_product_identity",
16131
16977
  "wrong_variant",
@@ -16136,13 +16982,13 @@ var ProductReferenceRejectionReasonEnum = z69.enum([
16136
16982
  "not_a_product_photo",
16137
16983
  "other"
16138
16984
  ]);
16139
- var GapDurationChoiceMirror = z69.object({
16140
- angleIndex: z69.number().int().nonnegative(),
16141
- recommendedSec: z69.union([
16142
- z69.literal(15),
16143
- z69.literal(30)
16985
+ var GapDurationChoiceMirror = z73.object({
16986
+ angleIndex: z73.number().int().nonnegative(),
16987
+ recommendedSec: z73.union([
16988
+ z73.literal(15),
16989
+ z73.literal(30)
16144
16990
  ]),
16145
- estimatedDeltaCredits: z69.number().int().nonnegative().optional()
16991
+ estimatedDeltaCredits: z73.number().int().nonnegative().optional()
16146
16992
  });
16147
16993
  function buildSelectedImageUrlsHash(urls) {
16148
16994
  const input = [
@@ -16152,13 +16998,13 @@ function buildSelectedImageUrlsHash(urls) {
16152
16998
  return `fnv1a:${hash.toString(16).padStart(8, "0")}:${urls.length}`;
16153
16999
  }
16154
17000
  __name(buildSelectedImageUrlsHash, "buildSelectedImageUrlsHash");
16155
- var ImageCreativeStyleEnum = z69.enum([
17001
+ var ImageCreativeStyleEnum = z73.enum([
16156
17002
  "studio",
16157
17003
  "floating",
16158
17004
  "ingredients",
16159
17005
  "in_use"
16160
17006
  ]);
16161
- var ImageCreativeArchetypeEnum = z69.enum([
17007
+ var ImageCreativeArchetypeEnum = z73.enum([
16162
17008
  "direct_response_product_ad",
16163
17009
  "hero_outcome",
16164
17010
  "offer_value_prop",
@@ -16167,20 +17013,20 @@ var ImageCreativeArchetypeEnum = z69.enum([
16167
17013
  "problem_solution",
16168
17014
  "lifestyle_identity"
16169
17015
  ]);
16170
- var ImageCreativeVisualTreatmentEnum = z69.enum([
17016
+ var ImageCreativeVisualTreatmentEnum = z73.enum([
16171
17017
  "performance_product_ad",
16172
17018
  "premium_dtc_carousel"
16173
17019
  ]);
16174
- var ImageCreativeAspectRatioEnum = z69.enum([
17020
+ var ImageCreativeAspectRatioEnum = z73.enum([
16175
17021
  "1:1",
16176
17022
  "4:5",
16177
17023
  "9:16",
16178
17024
  "16:9"
16179
17025
  ]);
16180
- var ImageRefSchema = z69.object({
16181
- url: z69.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.'),
16182
- path: z69.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`.'),
16183
- assetId: z69.string().uuid().optional().describe("ID opaco devuelto por check_media_upload. \xDAsalo para una imagen subida desde el MCP App; ProductMaker adopta el archivo directamente y el MCP nunca descarga sus bytes.")
17026
+ var ImageRefSchema = z73.object({
17027
+ url: z73.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.'),
17028
+ path: z73.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`.'),
17029
+ assetId: z73.string().uuid().optional().describe("ID opaco devuelto por check_media_upload. \xDAsalo para una imagen subida desde el MCP App; ProductMaker adopta el archivo directamente y el MCP nunca descarga sus bytes.")
16184
17030
  }).refine((v) => [
16185
17031
  v.url,
16186
17032
  v.path,
@@ -16188,11 +17034,11 @@ var ImageRefSchema = z69.object({
16188
17034
  ].filter(Boolean).length === 1, {
16189
17035
  message: "each image entry must have exactly one of `url`, `path`, or `assetId`"
16190
17036
  });
16191
- var ImagesInput = z69.array(ImageRefSchema).min(1).max(5).superRefine((images, ctx) => {
17037
+ var ImagesInput = z73.array(ImageRefSchema).min(1).max(5).superRefine((images, ctx) => {
16192
17038
  const assetCount = images.filter((image) => Boolean(image.assetId)).length;
16193
17039
  if (assetCount > 0 && assetCount !== images.length) {
16194
17040
  ctx.addIssue({
16195
- code: z69.ZodIssueCode.custom,
17041
+ code: z73.ZodIssueCode.custom,
16196
17042
  message: "all image entries must use assetId, or all must use URL/path sources"
16197
17043
  });
16198
17044
  }
@@ -16255,19 +17101,24 @@ async function submitCreative(api, opts) {
16255
17101
  const taskId = r.taskId ?? r.jobId;
16256
17102
  return asText({
16257
17103
  taskId,
16258
- status: "queued",
16259
- estimatedSeconds: opts.estimatedSeconds
17104
+ // Replay-aware: a keyed retry may land on a task that is already
17105
+ // processing/done — report the real status, not a hopeful "queued".
17106
+ status: r.status ?? "queued",
17107
+ estimatedSeconds: opts.estimatedSeconds,
17108
+ ...r.replayed === true ? {
17109
+ replayed: true
17110
+ } : {}
16260
17111
  });
16261
17112
  }
16262
17113
  __name(submitCreative, "submitCreative");
16263
- var ResearchTargetCountryEnum = z69.enum([
17114
+ var ResearchTargetCountryEnum = z73.enum([
16264
17115
  "CO",
16265
17116
  "MX",
16266
17117
  "PE",
16267
17118
  "EC",
16268
17119
  "CL"
16269
17120
  ]);
16270
- var ResearchCategoryEnum = z69.enum([
17121
+ var ResearchCategoryEnum = z73.enum([
16271
17122
  "auto",
16272
17123
  "automotive",
16273
17124
  "kitchen",
@@ -16336,7 +17187,7 @@ var OVERWRITES_EXTERNAL = {
16336
17187
  idempotentHint: false,
16337
17188
  openWorldHint: false
16338
17189
  };
16339
- var DailyControlStatusEnum = z69.enum([
17190
+ var DailyControlStatusEnum = z73.enum([
16340
17191
  "ok",
16341
17192
  "sales_unavailable",
16342
17193
  "timezone_unavailable",
@@ -16349,19 +17200,19 @@ var DailyControlStatusEnum = z69.enum([
16349
17200
  "product_missing",
16350
17201
  "store_unreachable"
16351
17202
  ]);
16352
- var DailyControlLightEnum = z69.enum([
17203
+ var DailyControlLightEnum = z73.enum([
16353
17204
  "green",
16354
17205
  "amber",
16355
17206
  "red",
16356
17207
  "none"
16357
17208
  ]);
16358
- var DailyControlVerdictEnum = z69.enum([
17209
+ var DailyControlVerdictEnum = z73.enum([
16359
17210
  "scale",
16360
17211
  "hold",
16361
17212
  "kill",
16362
17213
  "insufficient_sample"
16363
17214
  ]);
16364
- var DailyControlSourceEnum = z69.enum([
17215
+ var DailyControlSourceEnum = z73.enum([
16365
17216
  "shopify",
16366
17217
  "shopify_settling",
16367
17218
  "user",
@@ -17502,29 +18353,29 @@ ${parsed.data.error ? `${escapeMarkdownText(parsed.data.error)}
17502
18353
  __name(renderVideoScriptMarkdown, "renderVideoScriptMarkdown");
17503
18354
 
17504
18355
  // src/tools/media-presentation.ts
17505
- import { z as z70 } from "zod";
17506
- var HttpsPresentationUrlSchema = z70.string().url().max(2048).refine((value) => new URL(value).protocol === "https:");
18356
+ import { z as z74 } from "zod";
18357
+ var HttpsPresentationUrlSchema = z74.string().url().max(2048).refine((value) => new URL(value).protocol === "https:");
17507
18358
  var ProductMakerPreviewUrlSchema = HttpsPresentationUrlSchema.refine((value) => {
17508
18359
  const hostname = new URL(value).hostname.toLowerCase();
17509
18360
  return hostname === "productmaker.app" || hostname.endsWith(".productmaker.app");
17510
18361
  }, "preview URL must be owned by ProductMaker");
17511
- var positiveIntAtMost = /* @__PURE__ */ __name((maximum) => z70.number().int().positive().max(maximum), "positiveIntAtMost");
17512
- var PresentationImageSchema = z70.object({
17513
- kind: z70.literal("image"),
18362
+ var positiveIntAtMost = /* @__PURE__ */ __name((maximum) => z74.number().int().positive().max(maximum), "positiveIntAtMost");
18363
+ var PresentationImageSchema = z74.object({
18364
+ kind: z74.literal("image"),
17514
18365
  url: HttpsPresentationUrlSchema,
17515
- mimeType: z70.enum([
18366
+ mimeType: z74.enum([
17516
18367
  "image/jpeg",
17517
18368
  "image/png"
17518
18369
  ]),
17519
18370
  byteSize: positiveIntAtMost(5 * 1024 * 1024),
17520
18371
  width: positiveIntAtMost(16384),
17521
18372
  height: positiveIntAtMost(16384),
17522
- alt: z70.string().trim().min(1).max(240)
18373
+ alt: z74.string().trim().min(1).max(240)
17523
18374
  }).strict();
17524
18375
  var McpMediaPresentationSchema = PresentationImageSchema;
17525
- var GalleryImageSchema = z70.object({
18376
+ var GalleryImageSchema = z74.object({
17526
18377
  url: ProductMakerPreviewUrlSchema,
17527
- alt: z70.string().trim().min(1).max(240)
18378
+ alt: z74.string().trim().min(1).max(240)
17528
18379
  });
17529
18380
  var MCP_GALLERY_IMAGE_MAX_BYTES = 10 * 1024 * 1024;
17530
18381
  async function imageBlocks(image, transport) {
@@ -17673,11 +18524,175 @@ ${summary}`,
17673
18524
  }
17674
18525
  __name(requirePaidCreativeConfirmation, "requirePaidCreativeConfirmation");
17675
18526
 
18527
+ // src/tools/stable-uuid.ts
18528
+ import { createHash as createHash2 } from "crypto";
18529
+ function canonicalize(value) {
18530
+ if (Array.isArray(value)) return value.map(canonicalize);
18531
+ if (value === null || typeof value !== "object") return value;
18532
+ const record = value;
18533
+ return Object.fromEntries(Object.keys(record).sort().filter((key) => record[key] !== void 0).map((key) => [
18534
+ key,
18535
+ canonicalize(record[key])
18536
+ ]));
18537
+ }
18538
+ __name(canonicalize, "canonicalize");
18539
+ function canonicalJson(value) {
18540
+ return JSON.stringify(canonicalize(value));
18541
+ }
18542
+ __name(canonicalJson, "canonicalJson");
18543
+ function deriveUuidV5(name, namespace) {
18544
+ const namespaceBytes = Buffer.from(namespace.replaceAll("-", ""), "hex");
18545
+ const digest = createHash2("sha1").update(namespaceBytes).update(name, "utf8").digest();
18546
+ const bytes = Buffer.from(digest.subarray(0, 16));
18547
+ bytes[6] = (bytes[6] ?? 0) & 15 | 80;
18548
+ bytes[8] = (bytes[8] ?? 0) & 63 | 128;
18549
+ const hex = bytes.toString("hex");
18550
+ return [
18551
+ hex.slice(0, 8),
18552
+ hex.slice(8, 12),
18553
+ hex.slice(12, 16),
18554
+ hex.slice(16, 20),
18555
+ hex.slice(20)
18556
+ ].join("-");
18557
+ }
18558
+ __name(deriveUuidV5, "deriveUuidV5");
18559
+
18560
+ // src/tools/request-key.ts
18561
+ var PAID_REQUEST_KEY_WINDOW_MS = 15 * 60 * 1e3;
18562
+ var PAID_REQUEST_KEY_NAMESPACE = "8f3d6b1c-2a47-4e5b-9c0d-6e7f8a9b0c1d";
18563
+ var PARAPHRASE_PRONE_FIELDS = /* @__PURE__ */ new Set([
18564
+ "additionalContext",
18565
+ "productContext",
18566
+ "scriptNotes",
18567
+ "creativeDirection",
18568
+ "buyerPersona",
18569
+ "actorDescription",
18570
+ "description",
18571
+ "idempotencyKey",
18572
+ "clientRequestId"
18573
+ ]);
18574
+ var URL_FIELDS = /* @__PURE__ */ new Set([
18575
+ "landingUrl",
18576
+ "productUrl",
18577
+ "url",
18578
+ "serviceLandingUrl"
18579
+ ]);
18580
+ var TRACKING_PARAM_PATTERN = /^(utm_\w+|fbclid|gclid|msclkid|mc_cid|mc_eid|igshid)$/;
18581
+ var LANGUAGE_FIELDS = /* @__PURE__ */ new Set([
18582
+ "language",
18583
+ "lang"
18584
+ ]);
18585
+ var CASE_FOLD_FIELDS = /* @__PURE__ */ new Set([
18586
+ "country",
18587
+ "language",
18588
+ "lang"
18589
+ ]);
18590
+ var ORDER_INSIGNIFICANT_ARRAY_FIELDS = /* @__PURE__ */ new Set([
18591
+ "outputs",
18592
+ "selectedOutputs",
18593
+ "features"
18594
+ ]);
18595
+ function sortCanonical(items) {
18596
+ return [
18597
+ ...items
18598
+ ].sort((a, b) => {
18599
+ const ka = canonicalJson(a);
18600
+ const kb = canonicalJson(b);
18601
+ return ka < kb ? -1 : ka > kb ? 1 : 0;
18602
+ });
18603
+ }
18604
+ __name(sortCanonical, "sortCanonical");
18605
+ function isRecord3(value) {
18606
+ return typeof value === "object" && value !== null && !Array.isArray(value);
18607
+ }
18608
+ __name(isRecord3, "isRecord");
18609
+ function collectLanguageCodes(value, out) {
18610
+ if (Array.isArray(value)) {
18611
+ const items = value;
18612
+ for (const item of items) collectLanguageCodes(item, out);
18613
+ return out;
18614
+ }
18615
+ if (!isRecord3(value)) return out;
18616
+ for (const [key, entry] of Object.entries(value)) {
18617
+ if (LANGUAGE_FIELDS.has(key) && typeof entry === "string") {
18618
+ const base = entry.trim().split(/[-_]/)[0]?.toLowerCase();
18619
+ if (base) out.add(base);
18620
+ }
18621
+ collectLanguageCodes(entry, out);
18622
+ }
18623
+ return out;
18624
+ }
18625
+ __name(collectLanguageCodes, "collectLanguageCodes");
18626
+ function normalizeSearchForKey(search) {
18627
+ if (!search) return "";
18628
+ const params = [
18629
+ ...new URLSearchParams(search).entries()
18630
+ ].filter(([key]) => !TRACKING_PARAM_PATTERN.test(key.toLowerCase())).sort(([ka, va], [kb, vb]) => ka === kb ? va < vb ? -1 : va > vb ? 1 : 0 : ka < kb ? -1 : 1);
18631
+ if (!params.length) return "";
18632
+ const out = new URLSearchParams();
18633
+ for (const [key, value] of params) out.append(key, value);
18634
+ return `?${out.toString()}`;
18635
+ }
18636
+ __name(normalizeSearchForKey, "normalizeSearchForKey");
18637
+ function normalizeUrlForKey(value, languages) {
18638
+ const trimmed = value.trim();
18639
+ let url;
18640
+ try {
18641
+ url = new URL(trimmed);
18642
+ } catch {
18643
+ return trimmed.toLowerCase();
18644
+ }
18645
+ const host = url.hostname.toLowerCase().replace(/^www\./, "");
18646
+ const port = url.port ? `:${url.port}` : "";
18647
+ const segments = url.pathname.split("/").filter(Boolean);
18648
+ const soleSegment = segments.length === 1 ? segments[0] : void 0;
18649
+ if (soleSegment !== void 0 && languages.has(soleSegment.toLowerCase())) {
18650
+ segments.pop();
18651
+ }
18652
+ const path3 = segments.length ? `/${segments.join("/")}` : "";
18653
+ return host + port + path3 + normalizeSearchForKey(url.search);
18654
+ }
18655
+ __name(normalizeUrlForKey, "normalizeUrlForKey");
18656
+ function normalizeForKey(value, languages) {
18657
+ if (Array.isArray(value)) {
18658
+ const items = value;
18659
+ return items.map((item) => normalizeForKey(item, languages));
18660
+ }
18661
+ if (!isRecord3(value)) return value;
18662
+ const out = {};
18663
+ for (const [key, entry] of Object.entries(value)) {
18664
+ if (PARAPHRASE_PRONE_FIELDS.has(key)) {
18665
+ if (key === "description" && value.mode === "described") {
18666
+ out[key] = normalizeForKey(entry, languages);
18667
+ }
18668
+ continue;
18669
+ }
18670
+ out[key] = URL_FIELDS.has(key) && typeof entry === "string" ? normalizeUrlForKey(entry, languages) : CASE_FOLD_FIELDS.has(key) && typeof entry === "string" ? (
18671
+ // separator too or a host alternating between them splits the key.
18672
+ entry.trim().toLowerCase().replace(/_/g, "-")
18673
+ ) : ORDER_INSIGNIFICANT_ARRAY_FIELDS.has(key) && Array.isArray(entry) ? sortCanonical(entry.map((item) => normalizeForKey(item, languages))) : normalizeForKey(entry, languages);
18674
+ }
18675
+ return out;
18676
+ }
18677
+ __name(normalizeForKey, "normalizeForKey");
18678
+ function derivePaidRequestKey(args) {
18679
+ const languages = collectLanguageCodes(args.input, /* @__PURE__ */ new Set());
18680
+ const semanticInput = normalizeForKey(args.input, languages);
18681
+ const bucket = Math.floor(args.nowMs / PAID_REQUEST_KEY_WINDOW_MS);
18682
+ return deriveUuidV5(canonicalJson({
18683
+ user: args.fingerprint,
18684
+ tool: args.tool,
18685
+ input: semanticInput,
18686
+ bucket
18687
+ }), PAID_REQUEST_KEY_NAMESPACE);
18688
+ }
18689
+ __name(derivePaidRequestKey, "derivePaidRequestKey");
18690
+
17676
18691
  // src/tools/winning-product-image.ts
17677
- import { z as z71 } from "zod";
17678
- var WinnerReferenceInput = z71.object({
17679
- runId: z71.string().uuid(),
17680
- candidateSourceId: z71.string().trim().min(1).max(160)
18692
+ import { z as z75 } from "zod";
18693
+ var WinnerReferenceInput = z75.object({
18694
+ runId: z75.string().uuid(),
18695
+ candidateSourceId: z75.string().trim().min(1).max(160)
17681
18696
  }).strict().describe("Referencia opaca de un producto devuelto por find_winning_products/get_product_research_run.");
17682
18697
  async function resolveWinnerImages(api, winnerReference, images) {
17683
18698
  if (!winnerReference) return images;
@@ -17714,59 +18729,59 @@ var createProductTaskSingleOutputDescription = renderCreateProductTaskAvoidWhenF
17714
18729
  var createServiceVideoGuidance = getProductMakerCapabilityModelGuidance("create_service_video");
17715
18730
  var listTasksGuidance = getProductMakerCapabilityModelGuidance("list_tasks");
17716
18731
  var editTaskDraftGuidance = getProductMakerCapabilityModelGuidance("edit_task_draft");
17717
- var CreateProductTaskInput = z72.object({
18732
+ var CreateProductTaskInput = z76.object({
17718
18733
  images: ImagesInput.optional().describe("1\u20135 fotos del mismo producto. Omitir \xFAnicamente cuando projectId apunta a un proyecto propio con imagen guardada."),
17719
18734
  winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
17720
- projectId: z72.string().uuid().optional().describe("Opcional. Proyecto guiado propio. Permite continuar sin volver a subir la imagen guardada; nunca evita cr\xE9ditos, cuotas ni confirmaciones."),
17721
- productUrl: z72.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."),
18735
+ projectId: z76.string().uuid().optional().describe("Opcional. Proyecto guiado propio. Permite continuar sin volver a subir la imagen guardada; nunca evita cr\xE9ditos, cuotas ni confirmaciones."),
18736
+ productUrl: z76.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."),
17722
18737
  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.'),
17723
18738
  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.'),
17724
- price: z72.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.'),
17725
- compareAtPrice: z72.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".'),
17726
- offer: z72.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.'),
17727
- paymentMethods: z72.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.'),
17728
- productContext: z72.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."),
17729
- productLabel: z72.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.'),
18739
+ price: z76.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.'),
18740
+ compareAtPrice: z76.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".'),
18741
+ offer: z76.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.'),
18742
+ paymentMethods: z76.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.'),
18743
+ productContext: z76.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."),
18744
+ productLabel: z76.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.'),
17730
18745
  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). Todos los presets funcionan a 15s (single_call) y 30s (Multiescena), excepto "problem_solution", que requiere 30s Multiescena. 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), "problem_solution" (problema concreto, producto como soluci\xF3n y CTA), "product_showcase" (producto ejecutando una funci\xF3n real), "wearable_showcase" (prenda o accesorio modelado), "cinematic" (TV spot con receta cinematogr\xE1fica), "wild_card" (idea imposible: un truco visual que en la vida real no podr\xEDa pasar, y al cierre la invitaci\xF3n a comprar), "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.'),
17731
- durationSeconds: z72.union([
17732
- z72.literal(15),
17733
- z72.literal(30)
18746
+ durationSeconds: z76.union([
18747
+ z76.literal(15),
18748
+ z76.literal(30)
17734
18749
  ]).default(15).describe("Opcional. Duraci\xF3n objetivo del video: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
17735
- pauseForAngleReview: z72.boolean().optional().describe("Compatibilidad heredada. MCP deriva esta pausa de aiDecidesAll: manual siempre pausa y autom\xE1tico siempre la omite. No uses este campo para saltar reviews."),
17736
- aiDecidesAll: z72.boolean().optional().describe("Mapea el toggle web \u201CDejar que la IA decida todo\u201D. Si true, ProductMaker omite reviews. Act\xEDvalo \xFAnicamente cuando el usuario lo pida expl\xEDcitamente. Omitido/false = reviews conversacionales."),
18750
+ pauseForAngleReview: z76.boolean().optional().describe("Compatibilidad heredada. MCP deriva esta pausa de aiDecidesAll: manual siempre pausa y autom\xE1tico siempre la omite. No uses este campo para saltar reviews."),
18751
+ aiDecidesAll: z76.boolean().optional().describe("Mapea el toggle web \u201CDejar que la IA decida todo\u201D. Si true, ProductMaker omite reviews. Act\xEDvalo \xFAnicamente cuando el usuario lo pida expl\xEDcitamente. Omitido/false = reviews conversacionales."),
17737
18752
  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.'),
17738
18753
  videoStructure: VideoStructureEnum.default("single_take").describe('Opcional. Formato: "single_take" = Un solo clip (generado completo; puede incluir cambios de plano) o "multi_scene" = Multiescena. Default: single_take.'),
17739
18754
  videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El proveedor se elige internamente. Default: economy.'),
17740
18755
  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, entre 3% y 15% m\xE1s caro seg\xFAn formato y tier. Si el usuario no menciona resoluci\xF3n, OMITE este campo (el backend usa 480p).'),
17741
- videoAngleCount: z72.number().int().min(1).max(3).optional().describe("Cantidad de videos a renderizar: 1, 2 o 3. REQUERIDO cuando outputs incluye video (o se omite outputs, cuyo default incluye video); om\xEDtelo s\xF3lo cuando no se generar\xE1 video. Preg\xFAntalo incluso con aiDecidesAll=true porque cambia el costo."),
18756
+ videoAngleCount: z76.number().int().min(1).max(3).optional().describe("Cantidad de videos a renderizar: 1, 2 o 3. REQUERIDO cuando outputs incluye video (o se omite outputs, cuyo default incluye video); om\xEDtelo s\xF3lo cuando no se generar\xE1 video. Preg\xFAntalo incluso con aiDecidesAll=true porque cambia el costo."),
17742
18757
  angleNarratives: PublicNarrativeStyleEnum.array().max(3).optional().describe('Opcional. Narrativa POR \xC1NGULO (posicional). Valores actuales: "review", "ugc_how_to", "ugc_unboxing_chaos", "problem_product_cta" (problema concreto, producto como soluci\xF3n y CTA), "product_showcase", "wearable_showcase", "cinematic", "wild_card", "animated_problem_pixar", "sassy_product_pixar", "market" (narrativa an\xE1lisis de mercado: adapta el video a los patrones ganadores estudiados de la competencia; requiere competitorScanEnabled: true). angleNarratives[i] fuerza la narrativa del \xE1ngulo i; las posiciones restantes las elige la IA. La lista no puede superar videoAngleCount.'),
17743
- outputs: z72.array(z72.enum([
18758
+ outputs: z76.array(z76.enum([
17744
18759
  "landing",
17745
18760
  "video",
17746
18761
  "image"
17747
18762
  ])).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). ${createProductTaskSingleOutputDescription}`),
17748
- competitorScanEnabled: z72.boolean().optional().describe("Opcional. Debe enviarse true solo despu\xE9s de que el usuario acepte estudiar la competencia (o si solicita la narrativa an\xE1lisis de mercado). Si la tarea incluye video, true analiza anuncios de la competencia y desbloquea la narrativa an\xE1lisis de mercado; si el estudio no encuentra datos suficientes, degrada al planner cl\xE1sico. Omitir no autoriza el estudio profundo."),
17749
- replicateWinnerWhenFound: z72.boolean().optional().describe("Conserva la preferencia de copiar un ganador si existe. false estudia el mercado sin reemplazar un \xE1ngulo por el anuncio ganador. No a\xF1ade una pregunta obligatoria."),
17750
- brandId: z72.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."),
17751
- actorId: z72.string().trim().min(1).optional().describe("Opcional. ID de un personaje guardado (usa list_actors para verlos). Si lo env\xEDas, su foto y su voz mandan sobre la descripci\xF3n de texto."),
18763
+ competitorScanEnabled: z76.boolean().optional().describe("Opcional. Debe enviarse true solo despu\xE9s de que el usuario acepte estudiar la competencia (o si solicita la narrativa an\xE1lisis de mercado). Si la tarea incluye video, true analiza anuncios de la competencia y desbloquea la narrativa an\xE1lisis de mercado; si el estudio no encuentra datos suficientes, degrada al planner cl\xE1sico. Omitir no autoriza el estudio profundo."),
18764
+ replicateWinnerWhenFound: z76.boolean().optional().describe("Conserva la preferencia de copiar un ganador si existe. false estudia el mercado sin reemplazar un \xE1ngulo por el anuncio ganador. No a\xF1ade una pregunta obligatoria."),
18765
+ brandId: z76.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."),
18766
+ actorId: z76.string().trim().min(1).optional().describe("Opcional. ID de un personaje guardado (usa list_actors para verlos). Si lo env\xEDas, su foto y su voz mandan sobre la descripci\xF3n de texto."),
17752
18767
  creativeGender: CreativeGenderEnum.optional().describe('Opcional. QUI\xC9N APARECE EN PANTALLA en todos los creativos de la task: el presentador del video, las personas fotografiadas en la landing y la persona del anuncio. NO es la voz. M\xE1ndalo en cuanto el usuario lo diga en cualquier momento de la conversaci\xF3n y con cualquier redacci\xF3n ("quiero una mujer", "que salga un hombre", "esto es para hombres"): si no lo mandas, el g\xE9nero lo inventa la IA y el usuario recibe lo contrario de lo que pidi\xF3 sin ning\xFAn aviso. "mixed" solo cuando pida expl\xEDcitamente ambos. Si adem\xE1s mandas actorId, el personaje guardado manda sobre este campo.'),
17753
- creativeDirection: z72.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.'),
17754
- headline: z72.string().max(120).optional().describe("Opcional. Titular EXACTO a renderizar. Se imprime tal cual: la IA no lo reescribe, no lo acorta y no lo traduce. Se aplica a las IM\xC1GENES y a la portada de la landing de esta task (el video no lo usa; para el video usa scriptNotes). \xDAsalo cuando el copy es de marca y no es negociable. M\xE1x 120 caracteres. Si lo omites, la IA escribe el titular."),
17755
- subheadline: z72.string().max(160).optional().describe("Opcional. Subt\xEDtulo EXACTO a renderizar, misma regla que headline. Independiente: puedes fijar solo el subt\xEDtulo. M\xE1x 160 caracteres."),
18768
+ creativeDirection: z76.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.'),
18769
+ headline: z76.string().max(120).optional().describe("Opcional. Titular EXACTO a renderizar. Se imprime tal cual: la IA no lo reescribe, no lo acorta y no lo traduce. Se aplica a las IM\xC1GENES y a la portada de la landing de esta task (el video no lo usa; para el video usa scriptNotes). \xDAsalo cuando el copy es de marca y no es negociable. M\xE1x 120 caracteres. Si lo omites, la IA escribe el titular."),
18770
+ subheadline: z76.string().max(160).optional().describe("Opcional. Subt\xEDtulo EXACTO a renderizar, misma regla que headline. Independiente: puedes fijar solo el subt\xEDtulo. M\xE1x 160 caracteres."),
17756
18771
  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".'),
17757
18772
  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".')
17758
18773
  }).strict();
17759
- var CreateServiceVideoInput = z72.object({
17760
- landingUrl: z72.string().url().describe("URL p\xFAblica de la landing del servicio. Es suficiente para generar el video; los medios son opcionales en la web."),
17761
- additionalContext: z72.string().max(3e3).optional().describe('Instrucci\xF3n natural prioritaria. Ej.: "adapta el ritmo de estos competidores", "enfatiza reservas de demos" o restricciones de claims.'),
18774
+ var CreateServiceVideoInput = z76.object({
18775
+ landingUrl: z76.string().url().describe("URL p\xFAblica de la landing del servicio. Es suficiente para generar el video; los medios son opcionales en la web."),
18776
+ additionalContext: z76.string().max(3e3).optional().describe('Instrucci\xF3n natural prioritaria. Ej.: "adapta el ritmo de estos competidores", "enfatiza reservas de demos" o restricciones de claims.'),
17762
18777
  language: Lang.describe("Idioma del video: es, en o pt."),
17763
- voiceId: z72.string().trim().min(1).optional().describe('Opcional. Voz de ElevenLabs para toda la narraci\xF3n. Usa "auto" para que la IA la alinee con el presentador o env\xEDa un ID concreto de la lista de voces de la web.'),
17764
- brandId: z72.string().uuid().optional().describe("Marca guardada del usuario. Aplica voz, mensajes, paleta y persona predeterminada."),
17765
- reviewMode: z72.enum([
18778
+ voiceId: z76.string().trim().min(1).optional().describe('Opcional. Voz de ElevenLabs para toda la narraci\xF3n. Usa "auto" para que la IA la alinee con el presentador o env\xEDa un ID concreto de la lista de voces de la web.'),
18779
+ brandId: z76.string().uuid().optional().describe("Marca guardada del usuario. Aplica voz, mensajes, paleta y persona predeterminada."),
18780
+ reviewMode: z76.enum([
17766
18781
  "automatic",
17767
18782
  "guided"
17768
18783
  ]).default("automatic").describe("automatic = la IA elige concepto y guion sin pausas; guided = el usuario aprueba concepto y storyboard."),
17769
- videoPreset: z72.enum([
18784
+ videoPreset: z76.enum([
17770
18785
  "auto",
17771
18786
  "ugc_dynamic",
17772
18787
  "ugc_how_to",
@@ -17776,128 +18791,128 @@ var CreateServiceVideoInput = z72.object({
17776
18791
  videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El proveedor se elige internamente. Default: economy.'),
17777
18792
  videoResolution: VideoResolutionEnum.default("480p").describe('Opcional. Resoluci\xF3n: "480p" o "720p". 720p cuesta m\xE1s por segundo y solo est\xE1 disponible en "economy" y "pro" (Medio = 768P). Default: 480p.')
17778
18793
  }).strict();
17779
- var GetTaskStatusInput = z72.object({
17780
- taskId: z72.string().min(1),
17781
- waitSeconds: z72.number().int().min(0).max(120).optional()
18794
+ var GetTaskStatusInput = z76.object({
18795
+ taskId: z76.string().min(1),
18796
+ waitSeconds: z76.number().int().min(0).max(120).optional()
17782
18797
  });
17783
- var ListTasksInput = z72.object({
17784
- limit: z72.number().int().min(1).max(50).optional(),
17785
- offset: z72.number().int().min(0).optional(),
17786
- search: z72.string().optional()
18798
+ var ListTasksInput = z76.object({
18799
+ limit: z76.number().int().min(1).max(50).optional(),
18800
+ offset: z76.number().int().min(0).optional(),
18801
+ search: z76.string().optional()
17787
18802
  });
17788
18803
  var MCP_REVIEW_LIST_FIELD_MAX = 1500;
17789
18804
  var MCP_REVIEW_FEATURE_ITEM_MAX = 240;
17790
- var EditTaskDraftInput = z72.object({
17791
- taskId: z72.string().min(1),
17792
- angleIndex: z72.number().int().min(0),
17793
- angle: z72.object({
17794
- angle: z72.string().min(1).max(480),
17795
- buyerPersona: z72.string().min(1).max(360),
17796
- actor: z72.string().min(1).max(360).optional(),
17797
- actorId: z72.string().trim().min(1).optional().describe("Opcional. ID de un personaje guardado (usa list_actors para verlos). Si lo env\xEDas, su foto y su voz mandan sobre la descripci\xF3n de texto."),
17798
- gender: z72.enum([
18805
+ var EditTaskDraftInput = z76.object({
18806
+ taskId: z76.string().min(1),
18807
+ angleIndex: z76.number().int().min(0),
18808
+ angle: z76.object({
18809
+ angle: z76.string().min(1).max(480),
18810
+ buyerPersona: z76.string().min(1).max(360),
18811
+ actor: z76.string().min(1).max(360).optional(),
18812
+ actorId: z76.string().trim().min(1).optional().describe("Opcional. ID de un personaje guardado (usa list_actors para verlos). Si lo env\xEDas, su foto y su voz mandan sobre la descripci\xF3n de texto."),
18813
+ gender: z76.enum([
17799
18814
  "female",
17800
18815
  "male"
17801
18816
  ]).optional(),
17802
- scriptNotes: z72.string().max(2e3).optional()
18817
+ scriptNotes: z76.string().max(2e3).optional()
17803
18818
  })
17804
18819
  });
17805
- var PendingActionPayload = z72.discriminatedUnion("kind", [
17806
- z72.object({
17807
- kind: z72.literal("product_reference_set_approval"),
18820
+ var PendingActionPayload = z76.discriminatedUnion("kind", [
18821
+ z76.object({
18822
+ kind: z76.literal("product_reference_set_approval"),
17808
18823
  scope: ProductReferenceScopeEnum.optional(),
17809
- scopeKey: z72.string().min(1).max(120).optional(),
17810
- scopeSnapshotId: z72.string().uuid().optional(),
17811
- expectedScopeHash: z72.string().min(1).max(128).optional(),
18824
+ scopeKey: z76.string().min(1).max(120).optional(),
18825
+ scopeSnapshotId: z76.string().uuid().optional(),
18826
+ expectedScopeHash: z76.string().min(1).max(128).optional(),
17812
18827
  decision: ProductReferenceResolutionDecisionEnum,
17813
18828
  reason: ProductReferenceRejectionReasonEnum.optional(),
17814
- affectedRefKeys: z72.array(z72.string().min(1).max(200)).min(1).optional(),
17815
- affectedPhotoNumbers: z72.array(z72.number().int().positive()).min(1).optional(),
17816
- recoveryAction: z72.enum([
18829
+ affectedRefKeys: z76.array(z76.string().min(1).max(200)).min(1).optional(),
18830
+ affectedPhotoNumbers: z76.array(z76.number().int().positive()).min(1).optional(),
18831
+ recoveryAction: z76.enum([
17817
18832
  "remove",
17818
18833
  "replace",
17819
18834
  "upload",
17820
18835
  "return_to_selection"
17821
18836
  ]).optional()
17822
18837
  }),
17823
- z72.object({
17824
- kind: z72.literal("product_reference_confirmation"),
17825
- selectedPhotoNumbers: z72.array(z72.number().int().positive().max(5)).min(1).max(5).optional().describe("Opcional. N\xFAmeros visibles de las fotos que el usuario quiere conservar; las dem\xE1s se deseleccionan. Debe quedar al menos una."),
18838
+ z76.object({
18839
+ kind: z76.literal("product_reference_confirmation"),
18840
+ selectedPhotoNumbers: z76.array(z76.number().int().positive().max(5)).min(1).max(5).optional().describe("Opcional. N\xFAmeros visibles de las fotos que el usuario quiere conservar; las dem\xE1s se deseleccionan. Debe quedar al menos una."),
17826
18841
  userChoice: ProductReferenceUserChoiceEnum.optional(),
17827
- confirmedProductNames: z72.array(z72.string().min(1).max(120)).min(2).max(8).optional(),
18842
+ confirmedProductNames: z76.array(z76.string().min(1).max(120)).min(2).max(8).optional(),
17828
18843
  videoStructure: VideoStructureEnum.optional().describe("Opcional. Formato para reanudar el video: single_take (Un solo clip, generado completo; puede incluir cambios de plano) o multi_scene (Multiescena). El backend vuelve a validar que sea compatible con el agrupamiento confirmado."),
17829
18844
  videoQuality: VideoQualityEnum.optional().describe("Opcional. Calidad para reanudar el video: economy (Econ\xF3mico), medium (Medio) o pro. El proveedor se elige internamente y el backend vuelve a cotizar la ruta."),
17830
- useMultiSceneShowcase: z72.boolean().optional().describe("Compatibilidad con confirmaciones antiguas: si es true junto con userChoice=variants, conserva el showcase multiescena.")
18845
+ useMultiSceneShowcase: z76.boolean().optional().describe("Compatibilidad con confirmaciones antiguas: si es true junto con userChoice=variants, conserva el showcase multiescena.")
17831
18846
  }),
17832
- z72.object({
17833
- kind: z72.literal("angle_review_approval"),
18847
+ z76.object({
18848
+ kind: z76.literal("angle_review_approval"),
17834
18849
  // All edits are optional — empty action body just approves the
17835
18850
  // backend-generated draft as-is. Field shapes mirror the angle-review
17836
18851
  // request contracts in @packages/types/wizard.ts; the handler routes to
17837
18852
  // the dedicated video/image/autopilot endpoint based on StatusResponse.kind.
17838
- customAngle: z72.string().min(1).max(480).optional(),
18853
+ customAngle: z76.string().min(1).max(480).optional(),
17839
18854
  // Keep the shared MCP action within the strictest downstream contract:
17840
18855
  // autopilot/image resume endpoints cap these fields at 1500 characters.
17841
- painPoints: z72.string().max(MCP_REVIEW_LIST_FIELD_MAX).optional(),
17842
- benefits: z72.string().max(MCP_REVIEW_LIST_FIELD_MAX).optional(),
18856
+ painPoints: z76.string().max(MCP_REVIEW_LIST_FIELD_MAX).optional(),
18857
+ benefits: z76.string().max(MCP_REVIEW_LIST_FIELD_MAX).optional(),
17843
18858
  // Same policy as the two fields above: the strictest downstream contract
17844
18859
  // wins. The image endpoint caps each feature at 240 while landing/video
17845
18860
  // accept 300, and this single action routes to whichever the task is — so
17846
18861
  // accepting 300 here turns a 250-character feature on an image task into
17847
18862
  // an opaque HTTP 400 after the model already believed it had been saved.
17848
- features: z72.array(z72.string().min(1).max(MCP_REVIEW_FEATURE_ITEM_MAX)).max(10).optional(),
17849
- buyerPersona: z72.string().min(1).max(360).optional(),
18863
+ features: z76.array(z76.string().min(1).max(MCP_REVIEW_FEATURE_ITEM_MAX)).max(10).optional(),
18864
+ buyerPersona: z76.string().min(1).max(360).optional(),
17850
18865
  narrativeStyle: PublicNarrativeStyleEnum.optional().describe('Narrativa del video. "problem_product_cta" (Problema y Soluci\xF3n) requiere Multiescena de 30s: en un video de 15s en formato Un solo clip el servidor la rechaza.'),
17851
- actor: z72.string().min(1).max(360).optional().describe("Nueva descripci\xF3n del presentador. Al enviarla sin actorId, se elimina autom\xE1ticamente cualquier actor guardado previo de esos \xE1ngulos."),
17852
- actorId: z72.string().max(120).nullable().optional(),
17853
- creativeDirection: z72.string().max(2e3).optional(),
17854
- selectedAngleNumbers: z72.array(z72.number().int().positive()).min(1).max(3).optional().describe("N\xFAmeros visibles (1-based) de los \xE1ngulos que el usuario quiere renderizar. Deben ser \xFAnicos, existir en la review y no superar el videoAngleCount autorizado."),
17855
- angleEdits: z72.array(z72.object({
17856
- angleNumber: z72.number().int().positive(),
17857
- narrativeSelection: z72.enum([
18866
+ actor: z76.string().min(1).max(360).optional().describe("Nueva descripci\xF3n del presentador. Al enviarla sin actorId, se elimina autom\xE1ticamente cualquier actor guardado previo de esos \xE1ngulos."),
18867
+ actorId: z76.string().max(120).nullable().optional(),
18868
+ creativeDirection: z76.string().max(2e3).optional(),
18869
+ selectedAngleNumbers: z76.array(z76.number().int().positive()).min(1).max(3).optional().describe("N\xFAmeros visibles (1-based) de los \xE1ngulos que el usuario quiere renderizar. Deben ser \xFAnicos, existir en la review y no superar el videoAngleCount autorizado."),
18870
+ angleEdits: z76.array(z76.object({
18871
+ angleNumber: z76.number().int().positive(),
18872
+ narrativeSelection: z76.enum([
17858
18873
  "market",
17859
18874
  "guided"
17860
18875
  ]).optional().describe("Elecci\xF3n expl\xEDcita: market usa el estudio disponible; guided usa la narrativa elegida para este \xE1ngulo."),
17861
- angle: z72.string().min(1).max(480).optional(),
17862
- buyerPersona: z72.string().min(1).max(360).optional(),
18876
+ angle: z76.string().min(1).max(480).optional(),
18877
+ buyerPersona: z76.string().min(1).max(360).optional(),
17863
18878
  narrativeStyle: PublicNarrativeStyleEnum.optional().describe('Narrativa del video. "problem_product_cta" (Problema y Soluci\xF3n) requiere Multiescena de 30s: en un video de 15s en formato Un solo clip el servidor la rechaza.'),
17864
- actor: z72.string().min(1).max(360).optional(),
17865
- actorId: z72.string().max(120).nullable().optional(),
17866
- creativeDirection: z72.string().max(2e3).optional()
18879
+ actor: z76.string().min(1).max(360).optional(),
18880
+ actorId: z76.string().max(120).nullable().optional(),
18881
+ creativeDirection: z76.string().max(2e3).optional()
17867
18882
  }).strict()).max(3).optional().describe("Ediciones independientes por n\xFAmero visible de \xE1ngulo. S\xF3lo se aceptan para \xE1ngulos incluidos en selectedAngleNumbers (o en la selecci\xF3n por defecto)."),
17868
- gapDurationSeconds: z72.union([
17869
- z72.literal(15),
17870
- z72.literal(30)
18883
+ gapDurationSeconds: z76.union([
18884
+ z76.literal(15),
18885
+ z76.literal(30)
17871
18886
  ]).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.")
17872
18887
  }),
17873
- z72.object({
17874
- kind: z72.literal("service_angle_approval"),
17875
- angleId: z72.string().uuid().optional(),
17876
- angleNumber: z72.number().int().positive().optional(),
17877
- content: z72.string().min(1).max(480).optional(),
17878
- creativeDirection: z72.string().min(1).max(1500).optional(),
17879
- actor: z72.string().min(1).max(360).optional()
18888
+ z76.object({
18889
+ kind: z76.literal("service_angle_approval"),
18890
+ angleId: z76.string().uuid().optional(),
18891
+ angleNumber: z76.number().int().positive().optional(),
18892
+ content: z76.string().min(1).max(480).optional(),
18893
+ creativeDirection: z76.string().min(1).max(1500).optional(),
18894
+ actor: z76.string().min(1).max(360).optional()
17880
18895
  }),
17881
- z72.object({
17882
- kind: z72.literal("service_script_approval"),
17883
- voiceovers: z72.record(z72.string(), z72.string().min(1).max(600)).optional(),
17884
- visualDirections: z72.record(z72.string(), z72.string().min(1).max(1200)).optional(),
17885
- voiceoversByScene: z72.record(z72.string(), z72.string().min(1).max(600)).optional(),
17886
- visualDirectionsByScene: z72.record(z72.string(), z72.string().min(1).max(1200)).optional()
18896
+ z76.object({
18897
+ kind: z76.literal("service_script_approval"),
18898
+ voiceovers: z76.record(z76.string(), z76.string().min(1).max(600)).optional(),
18899
+ visualDirections: z76.record(z76.string(), z76.string().min(1).max(1200)).optional(),
18900
+ voiceoversByScene: z76.record(z76.string(), z76.string().min(1).max(600)).optional(),
18901
+ visualDirectionsByScene: z76.record(z76.string(), z76.string().min(1).max(1200)).optional()
17887
18902
  })
17888
18903
  ]);
17889
- var ResolvePendingActionInput = z72.object({
17890
- taskId: z72.string().min(1),
18904
+ var ResolvePendingActionInput = z76.object({
18905
+ taskId: z76.string().min(1),
17891
18906
  action: PendingActionPayload
17892
18907
  }).strict();
17893
18908
  function resolveVariantConfirmVideoTier(action) {
17894
18909
  return action.useMultiSceneShowcase && action.userChoice === "variants" ? "standard" : void 0;
17895
18910
  }
17896
18911
  __name(resolveVariantConfirmVideoTier, "resolveVariantConfirmVideoTier");
17897
- var StatusForResolveSchema = z72.object({
18912
+ var StatusForResolveSchema = z76.object({
17898
18913
  // Metadata used to select the backend resume contract. Older API responses
17899
18914
  // may omit these fields, so the resolver keeps the generic fallback.
17900
- kind: z72.enum([
18915
+ kind: z76.enum([
17901
18916
  "autopilot",
17902
18917
  "video",
17903
18918
  "image",
@@ -17905,18 +18920,18 @@ var StatusForResolveSchema = z72.object({
17905
18920
  "mix",
17906
18921
  "service"
17907
18922
  ]).optional(),
17908
- selectedOutputs: z72.array(z72.enum([
18923
+ selectedOutputs: z76.array(z76.enum([
17909
18924
  "landing",
17910
18925
  "video",
17911
18926
  "image"
17912
18927
  ])).optional(),
17913
- videoAngleCount: z72.number().int().min(1).max(3).optional(),
17914
- ugcFormat: z72.enum([
18928
+ videoAngleCount: z76.number().int().min(1).max(3).optional(),
18929
+ ugcFormat: z76.enum([
17915
18930
  "single_8s",
17916
18931
  "single_15s",
17917
18932
  "double_30s"
17918
18933
  ]).optional(),
17919
- videoStyle: z72.enum([
18934
+ videoStyle: z76.enum([
17920
18935
  "ugc",
17921
18936
  "cartoon_3d",
17922
18937
  "product_only",
@@ -17925,79 +18940,72 @@ var StatusForResolveSchema = z72.object({
17925
18940
  videoStructure: VideoStructureEnum.optional(),
17926
18941
  videoQuality: VideoQualityEnum.optional(),
17927
18942
  videoRouteId: VideoRouteIdEnum.optional(),
17928
- videoModel: z72.enum([
17929
- "veo",
17930
- "wan",
17931
- "bytedance",
17932
- "seedance",
17933
- "grok",
17934
- "kling"
17935
- ]).optional(),
17936
- videoTier: z72.enum([
18943
+ videoModel: TaskVideoModelEnum.optional(),
18944
+ videoTier: z76.enum([
17937
18945
  "legacy",
17938
18946
  "standard",
17939
18947
  "pro",
17940
18948
  "single_call"
17941
18949
  ]).optional(),
17942
- videoResolution: z72.enum([
18950
+ videoResolution: z76.enum([
17943
18951
  "480p",
17944
18952
  "720p"
17945
18953
  ]).optional(),
17946
18954
  narrativeStyle: NarrativeStyleEnum.optional(),
17947
- pauseForVideoScriptReview: z72.boolean().optional(),
17948
- pendingUserAction: z72.object({
17949
- kind: z72.string(),
17950
- candidateGrouping: z72.string().optional(),
17951
- selectedImageUrls: z72.array(z72.string()).optional(),
17952
- proposedProductNames: z72.array(z72.string()).optional(),
18955
+ pauseForVideoScriptReview: z76.boolean().optional(),
18956
+ pendingUserAction: z76.object({
18957
+ kind: z76.string(),
18958
+ candidateGrouping: z76.string().optional(),
18959
+ selectedImageUrls: z76.array(z76.string()).optional(),
18960
+ proposedProductNames: z76.array(z76.string()).optional(),
17953
18961
  // Adaptive reference questions are server-owned. Echo only the evidence
17954
18962
  // identity and the choices shown by the current snapshot; never accept a
17955
18963
  // client-supplied reference set as authorization.
17956
- options: z72.array(ProductReferenceUserChoiceEnum).max(4).optional(),
17957
- mode: z72.enum([
18964
+ options: z76.array(ProductReferenceUserChoiceEnum).max(4).optional(),
18965
+ mode: z76.enum([
17958
18966
  "question",
17959
18967
  "visual_recovery"
17960
18968
  ]).optional(),
17961
18969
  /** Hand-mirrored from packages/types result.ts — see CLAUDE.md § MCP Server Sync. */
17962
- canKeepSelection: z72.boolean().optional(),
17963
- decisionKey: z72.string().optional(),
17964
- questionOrdinal: z72.number().int().min(1).max(5).optional(),
17965
- questionCeiling: z72.union([
17966
- z72.literal(3),
17967
- z72.literal(5)
18970
+ canKeepSelection: z76.boolean().optional(),
18971
+ decisionKey: z76.string().optional(),
18972
+ questionOrdinal: z76.number().int().min(1).max(5).optional(),
18973
+ questionCeiling: z76.union([
18974
+ z76.literal(3),
18975
+ z76.literal(5)
17968
18976
  ]).optional(),
17969
- referenceEvidenceVersion: z72.number().int().positive().optional(),
17970
- expectedEvidenceHash: z72.string().optional(),
17971
- question: z72.object({
17972
- prompt: z72.string().min(1).max(240),
17973
- helpText: z72.string().min(1).max(300).optional(),
17974
- options: z72.array(z72.object({
18977
+ referenceEvidenceVersion: z76.number().int().positive().optional(),
18978
+ expectedEvidenceHash: z76.string().optional(),
18979
+ question: z76.object({
18980
+ prompt: z76.string().min(1).max(240),
18981
+ helpText: z76.string().min(1).max(300).optional(),
18982
+ options: z76.array(z76.object({
17975
18983
  value: ProductReferenceUserChoiceEnum,
17976
- label: z72.string().min(1).max(120),
17977
- description: z72.string().min(1).max(240).optional()
18984
+ label: z76.string().min(1).max(120),
18985
+ description: z76.string().min(1).max(240).optional()
17978
18986
  })).min(2).max(4)
17979
18987
  }).optional(),
17980
18988
  scope: ProductReferenceScopeEnum.optional(),
17981
- scopeKey: z72.string().optional(),
17982
- scopeSnapshotId: z72.string().uuid().optional(),
17983
- scopeHash: z72.string().optional(),
17984
- baseReferenceSetHash: z72.string().optional(),
17985
- phase: z72.enum([
18989
+ scopeKey: z76.string().optional(),
18990
+ scopeSnapshotId: z76.string().uuid().optional(),
18991
+ scopeHash: z76.string().optional(),
18992
+ baseReferenceSetHash: z76.string().optional(),
18993
+ phase: z76.enum([
17986
18994
  "preparation",
17987
18995
  "final"
17988
18996
  ]).optional(),
17989
- selectedRefKeys: z72.array(z72.string().min(1).max(200)).optional(),
17990
- allowedDecisions: z72.array(ProductReferenceResolutionDecisionEnum).optional(),
17991
- previews: z72.array(z72.object({
17992
- refKey: z72.string().min(1).max(200)
18997
+ selectedRefKeys: z76.array(z76.string().min(1).max(200)).optional(),
18998
+ allowedDecisions: z76.array(ProductReferenceResolutionDecisionEnum).optional(),
18999
+ previews: z76.array(z76.object({
19000
+ refKey: z76.string().min(1).max(200)
17993
19001
  }).passthrough()).optional(),
17994
- customAngleDraft: z72.string().optional(),
17995
- painPointsDraft: z72.string().optional(),
17996
- benefitsDraft: z72.string().optional(),
17997
- featuresDraft: z72.array(z72.string()).optional(),
17998
- angleOptions: z72.array(z72.object({
17999
- index: z72.number().int().nonnegative(),
18000
- angle: z72.string().min(1)
19002
+ customAngleDraft: z76.string().optional(),
19003
+ painPointsDraft: z76.string().optional(),
19004
+ benefitsDraft: z76.string().optional(),
19005
+ featuresDraft: z76.array(z76.string()).optional(),
19006
+ angleOptions: z76.array(z76.object({
19007
+ index: z76.number().int().nonnegative(),
19008
+ angle: z76.string().min(1)
18001
19009
  }).passthrough()).min(1).max(5).optional(),
18002
19010
  // Recommendation consumed only when the caller explicitly sends
18003
19011
  // gapDurationSeconds: 30 in the angle approval action.
@@ -18007,17 +19015,17 @@ var StatusForResolveSchema = z72.object({
18007
19015
  // keys. `actorId` (the saved custom actor the picker stored) was silently
18008
19016
  // dropped here, so the approval handler below could not echo it back on the
18009
19017
  // PATCH — the selection looked accepted and reached no render.
18010
- angles: z72.array(z72.object({
18011
- id: z72.string().uuid(),
18012
- content: z72.string(),
18013
- creativeDirection: z72.string(),
18014
- actor: z72.string(),
18015
- actorId: z72.string().nullable().optional()
19018
+ angles: z76.array(z76.object({
19019
+ id: z76.string().uuid(),
19020
+ content: z76.string(),
19021
+ creativeDirection: z76.string(),
19022
+ actor: z76.string(),
19023
+ actorId: z76.string().nullable().optional()
18016
19024
  }).passthrough()).optional(),
18017
- panels: z72.array(z72.object({
18018
- beatId: z72.string(),
18019
- voiceover: z72.string(),
18020
- visualDirection: z72.string()
19025
+ panels: z76.array(z76.object({
19026
+ beatId: z76.string(),
19027
+ voiceover: z76.string(),
19028
+ visualDirection: z76.string()
18021
19029
  }).passthrough()).optional()
18022
19030
  }).optional()
18023
19031
  }).passthrough();
@@ -18216,10 +19224,25 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
18216
19224
  };
18217
19225
  const r = await api.postMultipart("/v1/ingest", {
18218
19226
  images: parts
18219
- }, fields);
19227
+ }, fields, {
19228
+ // LLM hosts retry paid tools with rephrased arguments — the API's
19229
+ // byte-fingerprint can't see the duplicate, this key can. Derived
19230
+ // from the semantic inputs, not the wire fields, for the same
19231
+ // reason (see request-key.ts, which also canonicalizes the
19232
+ // order-insignificant `outputs` set).
19233
+ idempotencyKey: derivePaidRequestKey({
19234
+ fingerprint: api.fingerprint(),
19235
+ tool: "create_product_task",
19236
+ input,
19237
+ nowMs: Date.now()
19238
+ })
19239
+ });
18220
19240
  return asText({
18221
19241
  taskId: r.taskId,
18222
- status: "queued",
19242
+ status: r.status ?? "queued",
19243
+ ...r.replayed ? {
19244
+ replayed: true
19245
+ } : {},
18223
19246
  estimatedSeconds: 300,
18224
19247
  outputs: selectedOutputs,
18225
19248
  outputMode: "single_task",
@@ -18256,26 +19279,56 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
18256
19279
  assets: []
18257
19280
  },
18258
19281
  appDemoVideos: []
19282
+ }, {
19283
+ // The 2026-09-25 incident: four retries of this exact tool created
19284
+ // four charged service tasks because the host rephrased
19285
+ // `additionalContext` and flipped the landing between `/` and
19286
+ // `/es`. The derived key is stable across both rewrites (see
19287
+ // request-key.ts).
19288
+ idempotencyKey: derivePaidRequestKey({
19289
+ fingerprint: api.fingerprint(),
19290
+ tool: "create_service_video",
19291
+ input,
19292
+ nowMs: Date.now()
19293
+ })
18259
19294
  });
18260
19295
  const angles = await api.postJson(`/v1/tasks/${encodeURIComponent(created.taskId)}/service-angles`, {});
18261
19296
  const first = angles[0];
19297
+ const replayed = created.replayed === true;
18262
19298
  if (input.reviewMode === "automatic") {
18263
19299
  if (!first) throw new Error("ProductMaker no pudo crear un concepto para este servicio.");
18264
- await api.postJson(`/v1/tasks/${encodeURIComponent(created.taskId)}/service-angles/select`, {
18265
- angleIds: [
18266
- first.id
18267
- ]
18268
- });
19300
+ if (!(replayed && created.status !== "queued")) {
19301
+ await api.postJson(`/v1/tasks/${encodeURIComponent(created.taskId)}/service-angles/select`, {
19302
+ angleIds: [
19303
+ first.id
19304
+ ]
19305
+ });
19306
+ }
18269
19307
  return asText({
18270
19308
  taskId: created.taskId,
18271
- status: "processing",
19309
+ status: replayed ? created.status ?? "processing" : "processing",
19310
+ ...replayed ? {
19311
+ replayed: true
19312
+ } : {},
18272
19313
  reviewMode: input.reviewMode,
18273
19314
  selectedConcept: first
18274
19315
  });
18275
19316
  }
19317
+ if (replayed && created.status !== "queued") {
19318
+ return asText({
19319
+ taskId: created.taskId,
19320
+ status: created.status,
19321
+ replayed: true,
19322
+ reviewMode: input.reviewMode,
19323
+ angles
19324
+ });
19325
+ }
18276
19326
  return asText({
18277
19327
  taskId: created.taskId,
18278
19328
  status: "waiting_for_user",
19329
+ ...replayed ? {
19330
+ replayed: true
19331
+ } : {},
18279
19332
  reviewMode: input.reviewMode,
18280
19333
  pendingUserAction: {
18281
19334
  kind: "service_angle_approval",
@@ -18851,8 +19904,7 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
18851
19904
  __name(registerTaskTools, "registerTaskTools");
18852
19905
 
18853
19906
  // src/tools/creatives.ts
18854
- import { randomUUID } from "crypto";
18855
- import { z as z73 } from "zod";
19907
+ import { z as z77 } from "zod";
18856
19908
  var generateFreeVideoGuidance = getProductMakerCapabilityModelGuidance("generate_free_video");
18857
19909
  var generateVideoCreativeGuidance = getProductMakerCapabilityModelGuidance("generate_video_creative");
18858
19910
  var generateImageCreativesGuidance = getProductMakerCapabilityModelGuidance("generate_image_creatives");
@@ -18862,70 +19914,70 @@ var CreativeRefs = {
18862
19914
  images: ImagesInput,
18863
19915
  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.'),
18864
19916
  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.'),
18865
- price: z73.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.'),
18866
- offer: z73.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.'),
18867
- compareAtPrice: z73.string().min(1).max(120).optional().describe('Opcional. Precio anterior o de referencia para mostrar descuento. Ej. "$129.000" cuando price="$89.900".'),
18868
- productContext: z73.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."),
18869
- productLabel: z73.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.'),
18870
- buyerPersona: z73.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.'),
18871
- isCombo: z73.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
18872
- competitorScanEnabled: z73.boolean().optional().describe("Opcional. Debe enviarse true solo despu\xE9s de que el usuario acepte estudiar la competencia (o si solicita la narrativa an\xE1lisis de mercado). En video, true analiza anuncios de la competencia y desbloquea la narrativa an\xE1lisis de mercado; si el estudio no encuentra datos suficientes, degrada al planner cl\xE1sico. Omitir no autoriza el estudio profundo."),
18873
- brandId: z73.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.")
19917
+ price: z77.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.'),
19918
+ offer: z77.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.'),
19919
+ compareAtPrice: z77.string().min(1).max(120).optional().describe('Opcional. Precio anterior o de referencia para mostrar descuento. Ej. "$129.000" cuando price="$89.900".'),
19920
+ productContext: z77.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."),
19921
+ productLabel: z77.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.'),
19922
+ buyerPersona: z77.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.'),
19923
+ isCombo: z77.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
19924
+ competitorScanEnabled: z77.boolean().optional().describe("Opcional. Debe enviarse true solo despu\xE9s de que el usuario acepte estudiar la competencia (o si solicita la narrativa an\xE1lisis de mercado). En video, true analiza anuncios de la competencia y desbloquea la narrativa an\xE1lisis de mercado; si el estudio no encuentra datos suficientes, degrada al planner cl\xE1sico. Omitir no autoriza el estudio profundo."),
19925
+ brandId: z77.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.")
18874
19926
  };
18875
- var VideoPresenter = z73.discriminatedUnion("mode", [
18876
- z73.object({
18877
- mode: z73.literal("none")
19927
+ var VideoPresenter = z77.discriminatedUnion("mode", [
19928
+ z77.object({
19929
+ mode: z77.literal("none")
18878
19930
  }).strict(),
18879
- z73.object({
18880
- mode: z73.literal("saved_actor"),
18881
- actorId: z73.string().trim().min(1).max(120).describe("ID devuelto por list_actors para un actor guardado del usuario.")
19931
+ z77.object({
19932
+ mode: z77.literal("saved_actor"),
19933
+ actorId: z77.string().trim().min(1).max(120).describe("ID devuelto por list_actors para un actor guardado del usuario.")
18882
19934
  }).strict(),
18883
- z73.object({
18884
- mode: z73.literal("described"),
18885
- description: z73.string().trim().min(1).max(500).describe("Descripci\xF3n concreta de la persona que presenta el producto.")
19935
+ z77.object({
19936
+ mode: z77.literal("described"),
19937
+ description: z77.string().trim().min(1).max(500).describe("Descripci\xF3n concreta de la persona que presenta el producto.")
18886
19938
  }).strict()
18887
19939
  ]);
18888
- var VideoCreativeSetup = z73.discriminatedUnion("mode", [
18889
- z73.object({
18890
- mode: z73.literal("automatic").describe('S\xF3lo cuando el usuario pidi\xF3 expl\xEDcitamente "decide todo".')
19940
+ var VideoCreativeSetup = z77.discriminatedUnion("mode", [
19941
+ z77.object({
19942
+ mode: z77.literal("automatic").describe('S\xF3lo cuando el usuario pidi\xF3 expl\xEDcitamente "decide todo".')
18891
19943
  }).strict(),
18892
- z73.object({
18893
- mode: z73.literal("guided"),
19944
+ z77.object({
19945
+ mode: z77.literal("guided"),
18894
19946
  videoPreset: VideoPresetEnum.default("auto").describe('Narrativa opcional. Con estudio de mercado no la preguntes: se usa "auto" y el estudio define la estructura. Sin estudio conserva la elecci\xF3n del usuario. "problem_solution" requiere 30s.'),
18895
- buyerPersona: z73.string().trim().min(1).max(360),
19947
+ buyerPersona: z77.string().trim().min(1).max(360),
18896
19948
  presenter: VideoPresenter,
18897
- creativeDirection: z73.string().trim().min(1).max(500).nullable().describe("Direcci\xF3n visual elegida. null significa que el usuario deleg\xF3 expl\xEDcitamente esta decisi\xF3n.")
19949
+ creativeDirection: z77.string().trim().min(1).max(500).nullable().describe("Direcci\xF3n visual elegida. null significa que el usuario deleg\xF3 expl\xEDcitamente esta decisi\xF3n.")
18898
19950
  }).strict()
18899
19951
  ]);
18900
- var VideoInput = z73.object({
19952
+ var VideoInput = z77.object({
18901
19953
  images: CreativeRefs.images.optional(),
18902
19954
  winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
18903
- title: z73.string().min(1).describe('T\xEDtulo corto del producto. Aparece en la primera vista del video. Ej. "Crema Anti-Edad Premium".'),
18904
- primaryAngle: z73.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".'),
19955
+ title: z77.string().min(1).describe('T\xEDtulo corto del producto. Aparece en la primera vista del video. Ej. "Crema Anti-Edad Premium".'),
19956
+ primaryAngle: z77.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".'),
18905
19957
  language: CreativeRefs.language,
18906
19958
  country: CreativeRefs.country,
18907
- aspectRatio: z73.enum([
19959
+ aspectRatio: z77.enum([
18908
19960
  "9:16",
18909
19961
  "16:9",
18910
19962
  "Auto"
18911
19963
  ]).describe('Relaci\xF3n de aspecto. "9:16" = vertical (TikTok/Reels \u2014 default recomendado para ads). "16:9" = horizontal (YouTube). "Auto" = decide la IA.'),
18912
- durationSeconds: z73.number().int().min(4).max(30).default(15).describe("Opcional. Duraci\xF3n objetivo: Un solo clip Econ\xF3mico o Medio usa 15s; Un solo clip Pro usa 15s o 30s; Multiescena usa 15s o 30s totales. La combinaci\xF3n de formato y calidad determina la ruta y el costo."),
18913
- videoAngleCount: z73.number().int().min(1).max(3).describe("Cantidad de videos a renderizar: 1, 2 o 3. Es una decisi\xF3n de costo obligatoria: preg\xFAntala incluso cuando creativeSetup.mode=automatic."),
19964
+ durationSeconds: z77.number().int().min(4).max(30).default(15).describe("Opcional. Duraci\xF3n objetivo: Un solo clip Econ\xF3mico o Medio usa 15s; Un solo clip Pro usa 15s o 30s; Multiescena usa 15s o 30s totales. La combinaci\xF3n de formato y calidad determina la ruta y el costo."),
19965
+ videoAngleCount: z77.number().int().min(1).max(3).describe("Cantidad de videos a renderizar: 1, 2 o 3. Es una decisi\xF3n de costo obligatoria: preg\xFAntala incluso cuando creativeSetup.mode=automatic."),
18914
19966
  angleNarratives: PublicNarrativeStyleEnum.array().max(3).optional().describe('Opcional. Narrativa POR \xC1NGULO (posicional). Valores actuales: "review", "ugc_how_to", "ugc_unboxing_chaos", "problem_product_cta" (problema concreto, producto como soluci\xF3n y CTA), "product_showcase", "wearable_showcase", "cinematic", "wild_card", "animated_problem_pixar", "sassy_product_pixar", "market" (narrativa an\xE1lisis de mercado: adapta el video a los patrones ganadores estudiados de la competencia; requiere competitorScanEnabled: true). angleNarratives[i] fuerza la narrativa del \xE1ngulo i; las posiciones restantes las elige la IA. La lista no puede superar videoAngleCount.'),
18915
19967
  creativeSetup: VideoCreativeSetup.describe("Configuraci\xF3n creativa obligatoria. En guided pregunta narrativa, p\xFAblico, presentador y direcci\xF3n antes de confirmar. Usa automatic s\xF3lo si el usuario deleg\xF3 todo expl\xEDcitamente."),
18916
19968
  videoStructure: VideoStructureEnum.default("single_take").describe('Opcional. Formato: "single_take" = Un solo clip (generado completo; puede incluir cambios de plano) o "multi_scene" = Multiescena (varias tomas concatenadas). Default: single_take.'),
18917
19969
  videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El sistema elige el proveedor internamente. Default: economy.'),
18918
19970
  videoResolution: VideoResolutionEnum.optional().describe('Opcional. Resoluci\xF3n del render: "480p" o "720p". Si se omite, se usa 480p; elige 720p expl\xEDcitamente si necesitas m\xE1s nitidez.'),
18919
- features: z73.array(z73.string()).optional().describe('Opcional. Lista de features/beneficios clave a mencionar. Ej. ["resistente al agua", "bater\xEDa 24h", "carga inal\xE1mbrica"].'),
19971
+ features: z77.array(z77.string()).optional().describe('Opcional. Lista de features/beneficios clave a mencionar. Ej. ["resistente al agua", "bater\xEDa 24h", "carga inal\xE1mbrica"].'),
18920
19972
  price: CreativeRefs.price,
18921
19973
  offer: CreativeRefs.offer,
18922
19974
  productContext: CreativeRefs.productContext,
18923
19975
  productLabel: CreativeRefs.productLabel,
18924
- cta: z73.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.'),
18925
- scriptNotes: z73.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: creativeSetup.mode=guided activa siempre la revisi\xF3n palabra-por-palabra con get_video_script / approve_video_script.'),
19976
+ cta: z77.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.'),
19977
+ scriptNotes: z77.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: creativeSetup.mode=guided activa siempre la revisi\xF3n palabra-por-palabra con get_video_script / approve_video_script.'),
18926
19978
  creativeGender: CreativeGenderEnum.optional().describe('Opcional. QUI\xC9N APARECE EN PANTALLA en el video: mujer ("female"), hombre ("male") o ambos ("mixed"). NO es la voz; cuando se fija, la voz se alinea sola con el personaje. M\xE1ndalo en cuanto el usuario diga qui\xE9n debe SALIR en el video, con cualquier redacci\xF3n ("quiero una mujer", "que salga un hombre", "protagonista femenina"): si no lo mandas, el g\xE9nero lo inventa la IA y el usuario puede recibir lo contrario de lo que pidi\xF3 sin ning\xFAn aviso. NO lo mandes cuando hable del P\xDABLICO COMPRADOR ("esto es para hombres", "mi p\xFAblico son mujeres", "va dirigido a mam\xE1s"): eso es a qui\xE9n se le vende, no qui\xE9n act\xFAa, y fijarlo ah\xED pinea un protagonista que nadie pidi\xF3. "mixed" solo cuando pida expl\xEDcitamente ambos, y ten en cuenta que no garantiza una mezcla: no fija ning\xFAn g\xE9nero y cada creativo elige su protagonista. No lo preguntes como paso obligatorio. Si creativeSetup.presenter.mode="saved_actor", el personaje guardado manda sobre este campo.'),
18927
19979
  competitorScanEnabled: CreativeRefs.competitorScanEnabled,
18928
- replicateWinnerWhenFound: z73.boolean().optional().describe("Conserva la preferencia de copiar un ganador si existe. false permite estudiar el mercado sin reemplazar un \xE1ngulo por el anuncio ganador. No a\xF1adas una pregunta obligatoria para este campo."),
19980
+ replicateWinnerWhenFound: z77.boolean().optional().describe("Conserva la preferencia de copiar un ganador si existe. false permite estudiar el mercado sin reemplazar un \xE1ngulo por el anuncio ganador. No a\xF1adas una pregunta obligatoria para este campo."),
18929
19981
  brandId: CreativeRefs.brandId
18930
19982
  }).strict();
18931
19983
  var FreeVideoQualityEnum = VideoQualityEnum;
@@ -18943,7 +19995,7 @@ var FREE_VIDEO_DURATIONS_BY_QUALITY = {
18943
19995
  length: 29
18944
19996
  }, (_, index) => index + 2)
18945
19997
  };
18946
- var FreeVideoModelIdEnum = z73.enum([
19998
+ var FreeVideoModelIdEnum = z77.enum([
18947
19999
  "grok-imagine-video-1-5-preview",
18948
20000
  "minimax-h3/reference-to-video",
18949
20001
  "wan/3-0-video",
@@ -18954,70 +20006,65 @@ var FREE_SELECTABLE_MODEL_IDS = [
18954
20006
  "minimax-h3/reference-to-video",
18955
20007
  "wan/3-0-video"
18956
20008
  ];
18957
- var FreeVideoInput = z73.object({
20009
+ var FreeVideoInput = z77.object({
18958
20010
  images: ImagesInput.optional().describe("Opcional. 1-5 fotos del producto. Om\xEDtelo para generar desde texto puro."),
18959
- rawPrompt: z73.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."),
20011
+ rawPrompt: z77.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."),
18960
20012
  language: CreativeRefs.language,
18961
20013
  quality: FreeVideoQualityEnum.describe('Calidad del video: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro (Wan).'),
18962
20014
  // Retired in MCP 7.0.0 (Task 11): kept in the schema, accepted, and
18963
20015
  // parsed ONLY so the handler can reject it with a message naming its
18964
20016
  // replacement — dropping it silently would turn a caller's explicit
18965
20017
  // Pro++ pick into a quiet fallback to Wan.
18966
- freeProModel: z73.enum([
20018
+ freeProModel: z77.enum([
18967
20019
  "wan",
18968
20020
  "seedance"
18969
20021
  ]).optional().describe("ELIMINADO en MCP 7.0.0 \u2014 usa `modelId`. Enviar este campo produce un error."),
18970
20022
  modelId: FreeVideoModelIdEnum.optional().describe('Opcional. Elige expl\xEDcitamente el modelo del registro en vez del default de la calidad: "grok-imagine-video-1-5-preview" (Grok 1.5), "minimax-h3/reference-to-video" (H3 Max) o "wan/3-0-video" (Wan 3.0). Om\xEDtelo para usar el modelo por defecto de la calidad elegida. El modelo determina la duraci\xF3n permitida (Grok 6/8/10/15s, H3 Max 4-15s, Wan 2-30s) y el precio. "bytedance/seedance-2-5" est\xE1 inactivo y se rechaza con un error claro, nunca un 500.'),
18971
- durationSeconds: z73.number().int().min(2).max(30).describe("Duraci\xF3n exacta. Econ\xF3mico: 6, 8, 10 o 15s; Medio: enteros de 4 a 15s; Pro Wan: 2 a 30s; Pro++ Seedance: 4 a 30s."),
20023
+ durationSeconds: z77.number().int().min(2).max(30).describe("Duraci\xF3n exacta. Econ\xF3mico: 6, 8, 10 o 15s; Medio: enteros de 4 a 15s; Pro Wan: 2 a 30s; Pro++ Seedance: 4 a 30s."),
18972
20024
  videoResolution: VideoResolutionEnum.describe("Resoluci\xF3n del video: 480p o 720p."),
18973
- generateAiMusic: z73.boolean().default(false).describe("Opcional. Agrega m\xFAsica instrumental IA debajo del audio nativo. Default false."),
18974
- addSubtitles: z73.boolean().default(false).describe("Opcional. Quema subt\xEDtulos usando el rawPrompt exacto. Default false."),
18975
- idempotencyKey: z73.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.")
20025
+ generateAiMusic: z77.boolean().default(false).describe("Opcional. Agrega m\xFAsica instrumental IA debajo del audio nativo. Default false."),
20026
+ addSubtitles: z77.boolean().default(false).describe("Opcional. Quema subt\xEDtulos usando el rawPrompt exacto. Default false."),
20027
+ idempotencyKey: z77.string().uuid().optional().describe("Opcional. Se acepta por compatibilidad pero se ignora: la identidad del reintento se deriva autom\xE1ticamente de los argumentos sem\xE1nticos, as\xED que un retry con los mismos argumentos ya es idempotente sin este campo.")
18976
20028
  }).strict();
18977
20029
  var imageStyleList = ImageCreativeStyleEnum.options.join(" | ");
18978
- var ImageCreativeSetup = z73.discriminatedUnion("mode", [
18979
- z73.object({
18980
- mode: z73.literal("automatic")
20030
+ var ImageCreativeSetup = z77.discriminatedUnion("mode", [
20031
+ z77.object({
20032
+ mode: z77.literal("automatic")
18981
20033
  }).strict(),
18982
- z73.object({
18983
- mode: z73.literal("guided"),
18984
- creativeArchetype: z73.union([
20034
+ z77.object({
20035
+ mode: z77.literal("guided"),
20036
+ creativeArchetype: z77.union([
18985
20037
  ImageCreativeArchetypeEnum,
18986
- z73.literal("ai_decides")
20038
+ z77.literal("ai_decides")
18987
20039
  ]),
18988
- visualTreatment: z73.union([
20040
+ visualTreatment: z77.union([
18989
20041
  ImageCreativeVisualTreatmentEnum,
18990
- z73.literal("ai_decides")
20042
+ z77.literal("ai_decides")
18991
20043
  ]),
18992
20044
  aspectRatio: ImageCreativeAspectRatioEnum,
18993
- nVariants: z73.union([
18994
- z73.literal(1),
18995
- z73.literal(2),
18996
- z73.literal(3),
18997
- z73.literal(4),
18998
- z73.literal(6)
18999
- ]),
19000
- imageModel: z73.enum([
19001
- "gpt-image-2-5:1k",
19002
- "gpt-image-2-5:2k",
19003
- "gpt-image-2:1k",
19004
- "gpt-image-2:2k"
20045
+ nVariants: z77.union([
20046
+ z77.literal(1),
20047
+ z77.literal(2),
20048
+ z77.literal(3),
20049
+ z77.literal(4),
20050
+ z77.literal(6)
19005
20051
  ]),
19006
- styleMix: z73.boolean().nullable().describe("Decisi\xF3n s\xF3lo para 2\u20134 variantes. Usa null para 1 o 6, cuando la UI no hace esta pregunta."),
19007
- generateAdCopy: z73.boolean(),
19008
- competitorScanEnabled: z73.boolean(),
19009
- productContext: z73.string().trim().min(1).max(3e3).nullable(),
19010
- creativeDirection: z73.string().trim().min(1).max(500).nullable(),
19011
- brandColors: z73.string().trim().min(1).max(240).nullable().describe('Colores en lenguaje natural, igual que en el chat web. Ej. "Azul y Dorado". null significa sin colores espec\xEDficos.'),
19012
- headline: z73.string().trim().min(1).max(120).nullable(),
19013
- subheadline: z73.string().trim().min(1).max(160).nullable()
20052
+ imageModel: z77.enum(PUBLIC_IMAGE_QUALITY_INPUTS).describe('Calidad: "image:1k" (Est\xE1ndar) o "image:2k" (Premium). Los tokens gpt-image-2-5:{1k,2k} y gpt-image-2:{1k,2k} se siguen aceptando.'),
20053
+ styleMix: z77.boolean().nullable().describe("Decisi\xF3n s\xF3lo para 2\u20134 variantes. Usa null para 1 o 6, cuando la UI no hace esta pregunta."),
20054
+ generateAdCopy: z77.boolean(),
20055
+ competitorScanEnabled: z77.boolean(),
20056
+ productContext: z77.string().trim().min(1).max(3e3).nullable(),
20057
+ creativeDirection: z77.string().trim().min(1).max(500).nullable(),
20058
+ brandColors: z77.string().trim().min(1).max(240).nullable().describe('Colores en lenguaje natural, igual que en el chat web. Ej. "Azul y Dorado". null significa sin colores espec\xEDficos.'),
20059
+ headline: z77.string().trim().min(1).max(120).nullable(),
20060
+ subheadline: z77.string().trim().min(1).max(160).nullable()
19014
20061
  }).strict()
19015
20062
  ]).superRefine((setup, context) => {
19016
20063
  if (setup.mode !== "guided") return;
19017
20064
  const needsStyleMix = setup.nVariants >= 2 && setup.nVariants <= 4;
19018
20065
  if (needsStyleMix && setup.styleMix === null) {
19019
20066
  context.addIssue({
19020
- code: z73.ZodIssueCode.custom,
20067
+ code: z77.ZodIssueCode.custom,
19021
20068
  path: [
19022
20069
  "styleMix"
19023
20070
  ],
@@ -19026,16 +20073,16 @@ var ImageCreativeSetup = z73.discriminatedUnion("mode", [
19026
20073
  }
19027
20074
  if (!needsStyleMix && setup.styleMix !== null) {
19028
20075
  context.addIssue({
19029
- code: z73.ZodIssueCode.custom,
20076
+ code: z77.ZodIssueCode.custom,
19030
20077
  path: [
19031
20078
  "styleMix"
19032
20079
  ],
19033
20080
  message: "styleMix debe ser null cuando nVariants es 1 o 6."
19034
20081
  });
19035
20082
  }
19036
- if ((setup.imageModel === "gpt-image-2:2k" || setup.imageModel === "gpt-image-2-5:2k") && setup.aspectRatio === "4:5") {
20083
+ if (imageResolutionOf(setup.imageModel) === "2k" && setup.aspectRatio === "4:5") {
19037
20084
  context.addIssue({
19038
- code: z73.ZodIssueCode.custom,
20085
+ code: z77.ZodIssueCode.custom,
19039
20086
  path: [
19040
20087
  "aspectRatio"
19041
20088
  ],
@@ -19043,53 +20090,53 @@ var ImageCreativeSetup = z73.discriminatedUnion("mode", [
19043
20090
  });
19044
20091
  }
19045
20092
  });
19046
- var LandingCreativeSetup = z73.discriminatedUnion("mode", [
19047
- z73.object({
19048
- mode: z73.literal("automatic")
20093
+ var LandingCreativeSetup = z77.discriminatedUnion("mode", [
20094
+ z77.object({
20095
+ mode: z77.literal("automatic")
19049
20096
  }).strict(),
19050
- z73.object({
19051
- mode: z73.literal("guided"),
20097
+ z77.object({
20098
+ mode: z77.literal("guided"),
19052
20099
  landingStrategy: LandingStrategyEnum,
19053
20100
  landingImageFormat: LandingImageFormatEnum,
19054
- competitorScanEnabled: z73.boolean(),
19055
- paymentMethods: z73.string().trim().min(1).max(240).nullable(),
19056
- productContext: z73.string().trim().min(1).max(3e3).nullable(),
19057
- creativeDirection: z73.string().trim().min(1).max(500).nullable(),
19058
- headline: z73.string().trim().min(1).max(120).nullable(),
19059
- subheadline: z73.string().trim().min(1).max(160).nullable()
20101
+ competitorScanEnabled: z77.boolean(),
20102
+ paymentMethods: z77.string().trim().min(1).max(240).nullable(),
20103
+ productContext: z77.string().trim().min(1).max(3e3).nullable(),
20104
+ creativeDirection: z77.string().trim().min(1).max(500).nullable(),
20105
+ headline: z77.string().trim().min(1).max(120).nullable(),
20106
+ subheadline: z77.string().trim().min(1).max(160).nullable()
19060
20107
  }).strict()
19061
20108
  ]);
19062
- var ImageInput = z73.object({
20109
+ var ImageInput = z77.object({
19063
20110
  images: CreativeRefs.images.optional(),
19064
20111
  winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
19065
- productTitle: z73.string().min(1).describe('Nombre del producto. Ej. "Crema Anti-Edad Premium".'),
19066
- primaryAngle: z73.string().min(1).optional().describe('Opcional. \xC1ngulo de venta principal. Si se omite, ProductMaker lo genera y creativeSetup.mode="guided" lo muestra para revisi\xF3n antes de crear las im\xE1genes.'),
20112
+ productTitle: z77.string().min(1).describe('Nombre del producto. Ej. "Crema Anti-Edad Premium".'),
20113
+ primaryAngle: z77.string().min(1).optional().describe('Opcional. \xC1ngulo de venta principal. Si se omite, ProductMaker lo genera y creativeSetup.mode="guided" lo muestra para revisi\xF3n antes de crear las im\xE1genes.'),
19067
20114
  buyerPersona: CreativeRefs.buyerPersona,
19068
20115
  language: CreativeRefs.language,
19069
20116
  country: CreativeRefs.country,
19070
20117
  creativeSetup: ImageCreativeSetup.describe("Configuraci\xF3n obligatoria equivalente al wizard web. Usa guided salvo que el usuario delegue expl\xEDcitamente todo; presenta las decisiones guiadas en un \xFAnico checklist compacto."),
19071
20118
  imageStyle: ImageCreativeStyleEnum.optional().describe(`Opcional. Estilo del shot del producto. Valores: ${imageStyleList}. Si se omite, la IA escoge seg\xFAn el archetype.`),
19072
- features: z73.array(z73.string()).optional().describe('Opcional. Features/beneficios a destacar visualmente. Ej. ["resistente al agua", "ultra liviano"].'),
20119
+ features: z77.array(z77.string()).optional().describe('Opcional. Features/beneficios a destacar visualmente. Ej. ["resistente al agua", "ultra liviano"].'),
19073
20120
  price: CreativeRefs.price,
19074
- omitPrice: z73.boolean().optional().describe("Pon true \xFAnicamente cuando el usuario pida expl\xEDcitamente una imagen sin precio. Evita que el backend reutilice un precio extra\xEDdo del producto. No lo combines con price ni compareAtPrice."),
20121
+ omitPrice: z77.boolean().optional().describe("Pon true \xFAnicamente cuando el usuario pida expl\xEDcitamente una imagen sin precio. Evita que el backend reutilice un precio extra\xEDdo del producto. No lo combines con price ni compareAtPrice."),
19075
20122
  compareAtPrice: CreativeRefs.compareAtPrice,
19076
20123
  offer: CreativeRefs.offer,
19077
20124
  productLabel: CreativeRefs.productLabel,
19078
20125
  isCombo: CreativeRefs.isCombo,
19079
20126
  brandId: CreativeRefs.brandId
19080
20127
  });
19081
- var EditImageCreativeInput = z73.object({
19082
- taskId: z73.string().uuid().describe("ID de la tarea de imagen ya finalizada."),
19083
- variantIndex: z73.number().int().min(0).max(10).describe("\xCDndice de la variante a corregir (0 = primera imagen)."),
19084
- instruction: z73.string().min(1).max(1e3).describe('Qu\xE9 cambiar en esa imagen. Ej. "quita el badge de env\xEDo gratis", "cambia el fondo a blanco".')
20128
+ var EditImageCreativeInput = z77.object({
20129
+ taskId: z77.string().uuid().describe("ID de la tarea de imagen ya finalizada."),
20130
+ variantIndex: z77.number().int().min(0).max(10).describe("\xCDndice de la variante a corregir (0 = primera imagen)."),
20131
+ instruction: z77.string().min(1).max(1e3).describe('Qu\xE9 cambiar en esa imagen. Ej. "quita el badge de env\xEDo gratis", "cambia el fondo a blanco".')
19085
20132
  });
19086
- var LandingInput = z73.object({
20133
+ var LandingInput = z77.object({
19087
20134
  images: CreativeRefs.images.optional(),
19088
20135
  winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
19089
20136
  language: CreativeRefs.language,
19090
20137
  country: CreativeRefs.country,
19091
20138
  creativeSetup: LandingCreativeSetup.describe("Configuraci\xF3n obligatoria de landing cl\xE1sica equivalente al wizard web. No expone landing editable/code mientras siga siendo admin-only."),
19092
- salesAngle: z73.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.'),
20139
+ salesAngle: z77.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.'),
19093
20140
  buyerPersona: CreativeRefs.buyerPersona,
19094
20141
  price: CreativeRefs.price,
19095
20142
  compareAtPrice: CreativeRefs.compareAtPrice,
@@ -19108,7 +20155,7 @@ function resolveImageSetup(setup) {
19108
20155
  },
19109
20156
  aspectRatio: setup.aspectRatio,
19110
20157
  nVariants: setup.nVariants,
19111
- imageModel: setup.imageModel,
20158
+ imageModel: canonicalImageModelInput(setup.imageModel),
19112
20159
  ...setup.nVariants === 6 ? {
19113
20160
  styleMix: true
19114
20161
  } : setup.styleMix === null ? {} : {
@@ -19199,7 +20246,16 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
19199
20246
  addSubtitles: input.addSubtitles
19200
20247
  },
19201
20248
  allowEmptyImages: true,
19202
- idempotencyKey: input.idempotencyKey ?? randomUUID(),
20249
+ // Always derived — never `input.idempotencyKey ??`: a host that
20250
+ // mints a fresh UUID per retry would defeat the dedupe entirely.
20251
+ // The semantic key is also a strictly better retry identity: a
20252
+ // reused caller key still lands on it (see request-key.ts).
20253
+ idempotencyKey: derivePaidRequestKey({
20254
+ fingerprint: api.fingerprint(),
20255
+ tool: "generate_free_video",
20256
+ input,
20257
+ nowMs: Date.now()
20258
+ }),
19203
20259
  transport
19204
20260
  });
19205
20261
  } catch (e) {
@@ -19264,6 +20320,12 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
19264
20320
  path: "/v1/creatives/videos",
19265
20321
  estimatedSeconds: 300,
19266
20322
  input: wirePayload,
20323
+ idempotencyKey: derivePaidRequestKey({
20324
+ fingerprint: api.fingerprint(),
20325
+ tool: "generate_video_creative",
20326
+ input,
20327
+ nowMs: Date.now()
20328
+ }),
19267
20329
  transport
19268
20330
  });
19269
20331
  } catch (e) {
@@ -19298,6 +20360,12 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
19298
20360
  pauseForAngleReview: true
19299
20361
  }
19300
20362
  },
20363
+ idempotencyKey: derivePaidRequestKey({
20364
+ fingerprint: api.fingerprint(),
20365
+ tool: "generate_image_creatives",
20366
+ input,
20367
+ nowMs: Date.now()
20368
+ }),
19301
20369
  transport
19302
20370
  });
19303
20371
  } catch (e) {
@@ -19341,6 +20409,12 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
19341
20409
  pauseForAngleReview: true
19342
20410
  }
19343
20411
  },
20412
+ idempotencyKey: derivePaidRequestKey({
20413
+ fingerprint: api.fingerprint(),
20414
+ tool: "generate_landing",
20415
+ input,
20416
+ nowMs: Date.now()
20417
+ }),
19344
20418
  transport
19345
20419
  });
19346
20420
  } catch (e) {
@@ -19351,10 +20425,10 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
19351
20425
  __name(registerCreativeTools, "registerCreativeTools");
19352
20426
 
19353
20427
  // src/tools/creative-variants.ts
19354
- import { z as z74 } from "zod";
20428
+ import { z as z78 } from "zod";
19355
20429
  var estimateCreativeVariantGuidance = getProductMakerCapabilityModelGuidance("estimate_creative_variant");
19356
20430
  var createCreativeVariantGuidance = getProductMakerCapabilityModelGuidance("create_creative_variant");
19357
- var LandingSectionKeyEnum = z74.enum([
20431
+ var LandingSectionKeyEnum = z78.enum([
19358
20432
  "problem_hook",
19359
20433
  "problem_cost",
19360
20434
  "solution_mechanism",
@@ -19394,24 +20468,24 @@ var LandingSectionKeyEnum = z74.enum([
19394
20468
  "lifestyle_fit",
19395
20469
  "join_lifestyle"
19396
20470
  ]);
19397
- var CreativeVariantKindEnum = z74.enum([
20471
+ var CreativeVariantKindEnum = z78.enum([
19398
20472
  "video",
19399
20473
  "image",
19400
20474
  "landing"
19401
20475
  ]);
19402
- var EstimateCreativeVariantInputBase = z74.object({
19403
- sourceTaskId: z74.string().uuid().describe("ID de la tarea original completada."),
20476
+ var EstimateCreativeVariantInputBase = z78.object({
20477
+ sourceTaskId: z78.string().uuid().describe("ID de la tarea original completada."),
19404
20478
  kind: CreativeVariantKindEnum,
19405
20479
  videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Solo cambia el preset p\xFAblico; el servidor conserva el modelo/tier del task origen."),
19406
20480
  videoResolution: VideoResolutionEnum.optional().describe("Opcional para video. Resoluci\xF3n del nuevo render."),
19407
- nVariants: z74.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
20481
+ nVariants: z78.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
19408
20482
  sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si ya conoces la secci\xF3n, evita cotizar la clasificaci\xF3n con IA."),
19409
- instructions: z74.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.")
20483
+ instructions: z78.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.")
19410
20484
  }).strict();
19411
20485
  var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine((input, ctx) => {
19412
20486
  if (input.kind === "video" && input.nVariants !== void 0) {
19413
20487
  ctx.addIssue({
19414
- code: z74.ZodIssueCode.custom,
20488
+ code: z78.ZodIssueCode.custom,
19415
20489
  path: [
19416
20490
  "nVariants"
19417
20491
  ],
@@ -19421,7 +20495,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
19421
20495
  if (input.kind !== "video") {
19422
20496
  if (input.videoPreset !== void 0) {
19423
20497
  ctx.addIssue({
19424
- code: z74.ZodIssueCode.custom,
20498
+ code: z78.ZodIssueCode.custom,
19425
20499
  path: [
19426
20500
  "videoPreset"
19427
20501
  ],
@@ -19430,7 +20504,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
19430
20504
  }
19431
20505
  if (input.videoResolution !== void 0) {
19432
20506
  ctx.addIssue({
19433
- code: z74.ZodIssueCode.custom,
20507
+ code: z78.ZodIssueCode.custom,
19434
20508
  path: [
19435
20509
  "videoResolution"
19436
20510
  ],
@@ -19440,7 +20514,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
19440
20514
  }
19441
20515
  if (input.kind !== "image" && input.nVariants !== void 0) {
19442
20516
  ctx.addIssue({
19443
- code: z74.ZodIssueCode.custom,
20517
+ code: z78.ZodIssueCode.custom,
19444
20518
  path: [
19445
20519
  "nVariants"
19446
20520
  ],
@@ -19450,7 +20524,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
19450
20524
  if (input.kind !== "landing") {
19451
20525
  if (input.sectionKey !== void 0) {
19452
20526
  ctx.addIssue({
19453
- code: z74.ZodIssueCode.custom,
20527
+ code: z78.ZodIssueCode.custom,
19454
20528
  path: [
19455
20529
  "sectionKey"
19456
20530
  ],
@@ -19459,7 +20533,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
19459
20533
  }
19460
20534
  if (input.instructions !== void 0) {
19461
20535
  ctx.addIssue({
19462
- code: z74.ZodIssueCode.custom,
20536
+ code: z78.ZodIssueCode.custom,
19463
20537
  path: [
19464
20538
  "instructions"
19465
20539
  ],
@@ -19468,31 +20542,31 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
19468
20542
  }
19469
20543
  }
19470
20544
  });
19471
- var CreateCreativeVariantInputBase = z74.object({
19472
- sourceTaskId: z74.string().uuid().describe("ID de la tarea original completada."),
20545
+ var CreateCreativeVariantInputBase = z78.object({
20546
+ sourceTaskId: z78.string().uuid().describe("ID de la tarea original completada."),
19473
20547
  kind: CreativeVariantKindEnum,
19474
- confirm: z74.literal(true).describe("Confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrarle el estimado de cr\xE9ditos."),
19475
- clientRequestId: z74.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."),
19476
- angle: z74.string().trim().min(1).max(500).optional().describe("\xC1ngulo nuevo para video o imagen."),
19477
- buyerPersona: z74.string().trim().min(1).max(360).optional().describe("Buyer persona coherente con el \xE1ngulo."),
19478
- scriptNotes: z74.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n breve del guion para una variante de video."),
19479
- creativeDirection: z74.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n creativa para una variante de imagen."),
20548
+ confirm: z78.literal(true).describe("Confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrarle el estimado de cr\xE9ditos."),
20549
+ clientRequestId: z78.string().uuid().describe("UUID estable para esta intenci\xF3n (obligatorio por compatibilidad de contrato; el servidor deriva la identidad real del request desde los argumentos sem\xE1nticos, as\xED que un UUID nuevo en un retry NO crea una variante duplicada). Si la API devuelve status=error, una nueva generaci\xF3n exige otra confirmaci\xF3n."),
20550
+ angle: z78.string().trim().min(1).max(500).optional().describe("\xC1ngulo nuevo para video o imagen."),
20551
+ buyerPersona: z78.string().trim().min(1).max(360).optional().describe("Buyer persona coherente con el \xE1ngulo."),
20552
+ scriptNotes: z78.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n breve del guion para una variante de video."),
20553
+ creativeDirection: z78.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n creativa para una variante de imagen."),
19480
20554
  videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Preset p\xFAblico; el servidor resuelve modelo/tier desde el task origen."),
19481
20555
  videoResolution: VideoResolutionEnum.optional().describe("Opcional para video."),
19482
- generateAiMusic: z74.boolean().optional().describe("Opcional para video."),
19483
- nVariants: z74.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
20556
+ generateAiMusic: z78.boolean().optional().describe("Opcional para video."),
20557
+ nVariants: z78.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
19484
20558
  imageStyle: ImageCreativeStyleEnum.optional().describe("Opcional para imagen."),
19485
20559
  creativeArchetype: ImageCreativeArchetypeEnum.optional().describe("Opcional para imagen."),
19486
20560
  visualTreatment: ImageCreativeVisualTreatmentEnum.optional().describe("Opcional para imagen."),
19487
20561
  aspectRatio: ImageCreativeAspectRatioEnum.optional().describe("Opcional para imagen."),
19488
- styleMix: z74.boolean().optional().describe("Opcional para imagen."),
20562
+ styleMix: z78.boolean().optional().describe("Opcional para imagen."),
19489
20563
  sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si se omite, se clasifica con IA a partir de instructions."),
19490
- instructions: z74.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.")
20564
+ instructions: z78.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.")
19491
20565
  }).strict();
19492
20566
  var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((input, ctx) => {
19493
20567
  if ((input.kind === "video" || input.kind === "image") && !input.angle) {
19494
20568
  ctx.addIssue({
19495
- code: z74.ZodIssueCode.custom,
20569
+ code: z78.ZodIssueCode.custom,
19496
20570
  path: [
19497
20571
  "angle"
19498
20572
  ],
@@ -19501,7 +20575,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
19501
20575
  }
19502
20576
  if (input.kind === "landing" && !input.sectionKey && !input.instructions) {
19503
20577
  ctx.addIssue({
19504
- code: z74.ZodIssueCode.custom,
20578
+ code: z78.ZodIssueCode.custom,
19505
20579
  path: [
19506
20580
  "instructions"
19507
20581
  ],
@@ -19547,7 +20621,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
19547
20621
  for (const field of fieldsByKind[input.kind]) {
19548
20622
  if (input[field] !== void 0) {
19549
20623
  ctx.addIssue({
19550
- code: z74.ZodIssueCode.custom,
20624
+ code: z78.ZodIssueCode.custom,
19551
20625
  path: [
19552
20626
  field
19553
20627
  ],
@@ -19594,7 +20668,7 @@ function registerCreativeVariantTools(server, api) {
19594
20668
  throw translateError(error);
19595
20669
  }
19596
20670
  });
19597
- server.tool("create_creative_variant", createCreativeVariantGuidance.summary + " ES UNA ACCI\xD3N DE PAGO: primero llama estimate_creative_variant, muestra el rango de cr\xE9ditos al usuario y solo despu\xE9s invoca este tool con confirm=true. Las recomendaciones IA tambi\xE9n son acciones pagas a 2x del costo real. clientRequestId es obligatorio: genera un UUID por intenci\xF3n y reutiliza el mismo ante timeouts o transporte ambiguo. Si recibes status=error, pide confirmaci\xF3n para un intento nuevo y usa otro UUID. Reutiliza el producto y referencias del task origen; crea un child task independiente y devuelve variantTaskId. Para landing puedes enviar instructions en texto libre y omitir sectionKey: el backend lo clasifica con IA. No expone ni permite cambiar videoModel, videoTier, videoStyle o narrativeStyle; esos valores se heredan del task origen.", CreateCreativeVariantInputBase.shape, CREATES_PAID, async (input) => {
20671
+ server.tool("create_creative_variant", createCreativeVariantGuidance.summary + " ES UNA ACCI\xD3N DE PAGO: primero llama estimate_creative_variant, muestra el rango de cr\xE9ditos al usuario y solo despu\xE9s invoca este tool con confirm=true. Las recomendaciones IA tambi\xE9n son acciones pagas a 2x del costo real. clientRequestId es obligatorio por contrato: genera un UUID por intenci\xF3n. El servidor deriva la identidad real del request desde los argumentos sem\xE1nticos, as\xED que un UUID nuevo NO desbloquea un intento distinto \u2014 si recibes status=error, pide confirmaci\xF3n al usuario y reinvoca con los mismos argumentos. Reutiliza el producto y referencias del task origen; crea un child task independiente y devuelve variantTaskId. Para landing puedes enviar instructions en texto libre y omitir sectionKey: el backend lo clasifica con IA. No expone ni permite cambiar videoModel, videoTier, videoStyle o narrativeStyle; esos valores se heredan del task origen.", CreateCreativeVariantInputBase.shape, CREATES_PAID, async (input) => {
19598
20672
  try {
19599
20673
  const validatedInput = parseVariantInput(CreateCreativeVariantInput, input);
19600
20674
  const estimateInput = EstimateCreativeVariantInput.parse({
@@ -19621,8 +20695,16 @@ function registerCreativeVariantTools(server, api) {
19621
20695
  });
19622
20696
  sectionKey = LandingSectionKeyEnum.parse(recommendation.value);
19623
20697
  }
20698
+ const derivedClientRequestId = derivePaidRequestKey({
20699
+ fingerprint: api.fingerprint(),
20700
+ tool: "create_creative_variant",
20701
+ input: {
20702
+ ...validatedInput
20703
+ },
20704
+ nowMs: Date.now()
20705
+ });
19624
20706
  const payload = {
19625
- clientRequestId: validatedInput.clientRequestId,
20707
+ clientRequestId: derivedClientRequestId,
19626
20708
  kind: validatedInput.kind,
19627
20709
  ...validatedInput.kind !== "landing" ? {
19628
20710
  angle: validatedInput.angle,
@@ -19648,7 +20730,7 @@ function registerCreativeVariantTools(server, api) {
19648
20730
  };
19649
20731
  const created = await api.postJson(`/v1/tasks/${encodeURIComponent(validatedInput.sourceTaskId)}/creative-variants`, payload);
19650
20732
  if (created.status === "error") {
19651
- throw mcp(McpErrorCode.CREATIVE_VARIANT_FAILED, "La variante anterior termin\xF3 con error. Pide confirmaci\xF3n para un intento nuevo y usa otro clientRequestId.", {
20733
+ throw mcp(McpErrorCode.CREATIVE_VARIANT_FAILED, "La variante anterior termin\xF3 con error. Pide confirmaci\xF3n al usuario y reinvoca con los mismos argumentos para un intento nuevo.", {
19652
20734
  variantTaskId: created.variantTaskId
19653
20735
  });
19654
20736
  }
@@ -19664,32 +20746,32 @@ function registerCreativeVariantTools(server, api) {
19664
20746
  __name(registerCreativeVariantTools, "registerCreativeVariantTools");
19665
20747
 
19666
20748
  // src/tools/script-review.ts
19667
- import { z as z75 } from "zod";
20749
+ import { z as z79 } from "zod";
19668
20750
  var getVideoScriptGuidance = getProductMakerCapabilityModelGuidance("get_video_script");
19669
20751
  var regenerateVideoScriptGuidance = getProductMakerCapabilityModelGuidance("regenerate_video_script");
19670
20752
  var approveVideoScriptGuidance = getProductMakerCapabilityModelGuidance("approve_video_script");
19671
- var GetVideoScriptInput = z75.object({
19672
- taskId: z75.string().min(1).describe("ID de la tarea (la misma que devolvi\xF3 create_product_task / generate_video_creative)."),
19673
- angleKey: z75.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.')
20753
+ var GetVideoScriptInput = z79.object({
20754
+ taskId: z79.string().min(1).describe("ID de la tarea (la misma que devolvi\xF3 create_product_task / generate_video_creative)."),
20755
+ angleKey: z79.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.')
19674
20756
  }).strict();
19675
20757
  var VIDEO_SCRIPT_APPROVAL_MAX_SCENES2 = 50;
19676
20758
  var VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2 = 2e3;
19677
- var ApproveSceneEdit = z75.object({
19678
- index: z75.number().int().nonnegative().describe("\xCDndice de la escena (0-based, mismo que devuelve get_video_script)."),
19679
- dialogue: z75.string().min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2).refine((v) => v.trim().length > 0, {
20759
+ var ApproveSceneEdit = z79.object({
20760
+ index: z79.number().int().nonnegative().describe("\xCDndice de la escena (0-based, mismo que devuelve get_video_script)."),
20761
+ dialogue: z79.string().min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2).refine((v) => v.trim().length > 0, {
19680
20762
  message: "dialogue no puede ser s\xF3lo espacios"
19681
20763
  }).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.")
19682
20764
  }).strict();
19683
- var ApproveVideoScriptInput = z75.object({
19684
- taskId: z75.string().min(1).describe("ID de la tarea."),
19685
- angleKey: z75.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.'),
19686
- scenes: z75.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.")
20765
+ var ApproveVideoScriptInput = z79.object({
20766
+ taskId: z79.string().min(1).describe("ID de la tarea."),
20767
+ angleKey: z79.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.'),
20768
+ scenes: z79.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.")
19687
20769
  }).strict();
19688
- var RegenerateVideoScriptInput = z75.object({
19689
- taskId: z75.string().min(1).describe("ID de la tarea."),
19690
- angleKey: z75.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.'),
20770
+ var RegenerateVideoScriptInput = z79.object({
20771
+ taskId: z79.string().min(1).describe("ID de la tarea."),
20772
+ angleKey: z79.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.'),
19691
20773
  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).'),
19692
- narrativeSelection: z75.enum([
20774
+ narrativeSelection: z79.enum([
19693
20775
  "market",
19694
20776
  "guided"
19695
20777
  ]).optional().describe("Elecci\xF3n expl\xEDcita del usuario: market usa el estudio de mercado disponible; guided usa la narrativa elegida. Omitir conserva el comportamiento existente.")
@@ -19770,14 +20852,14 @@ function registerScriptReviewTools(server, api) {
19770
20852
  __name(registerScriptReviewTools, "registerScriptReviewTools");
19771
20853
 
19772
20854
  // src/tools/connections.ts
19773
- import { z as z76 } from "zod";
20855
+ import { z as z80 } from "zod";
19774
20856
  var listShopifyShopsGuidance = getProductMakerCapabilityModelGuidance("list_shopify_shops");
19775
20857
  var listShopifyProductsGuidance = getProductMakerCapabilityModelGuidance("list_shopify_products");
19776
20858
  var listMetaAdAccountsGuidance = getProductMakerCapabilityModelGuidance("list_meta_ad_accounts");
19777
20859
  var listMetaDestinationsGuidance = getProductMakerCapabilityModelGuidance("list_meta_destinations");
19778
20860
  var listTikTokAdvertisersGuidance = getProductMakerCapabilityModelGuidance("list_tiktok_advertisers");
19779
20861
  function registerConnectionTools(server, api) {
19780
- server.tool("list_shopify_shops", listShopifyShopsGuidance.summary + " Si la lista est\xE1 vac\xEDa, dile al usuario que conecte una tienda en productmaker.app/integrations antes de intentar publicar.", z76.object({}).shape, READS_INTERNAL, async () => {
20862
+ server.tool("list_shopify_shops", listShopifyShopsGuidance.summary + " Si la lista est\xE1 vac\xEDa, dile al usuario que conecte una tienda en productmaker.app/integrations antes de intentar publicar.", z80.object({}).shape, READS_INTERNAL, async () => {
19781
20863
  try {
19782
20864
  const r = await api.getJson("/v1/shopify/shops");
19783
20865
  return asText(r);
@@ -19785,10 +20867,10 @@ function registerConnectionTools(server, api) {
19785
20867
  throw translateError(e);
19786
20868
  }
19787
20869
  });
19788
- server.tool("list_shopify_products", listShopifyProductsGuidance.summary + ' 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.', z76.object({
19789
- shopDomain: z76.string().min(3).max(120),
19790
- query: z76.string().trim().max(120).optional(),
19791
- taskId: z76.string().uuid().optional()
20870
+ server.tool("list_shopify_products", listShopifyProductsGuidance.summary + ' 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.', z80.object({
20871
+ shopDomain: z80.string().min(3).max(120),
20872
+ query: z80.string().trim().max(120).optional(),
20873
+ taskId: z80.string().uuid().optional()
19792
20874
  }).shape, READS_EXTERNAL, async ({ shopDomain, query, taskId }) => {
19793
20875
  try {
19794
20876
  const params = new URLSearchParams({
@@ -19802,7 +20884,7 @@ function registerConnectionTools(server, api) {
19802
20884
  throw translateError(e);
19803
20885
  }
19804
20886
  });
19805
- server.tool("list_meta_ad_accounts", listMetaAdAccountsGuidance.summary + " Para publicar, prefiere `list_meta_destinations`, que tambi\xE9n devuelve el portfolio y las p\xE1ginas disponibles. Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar Meta en productmaker.app/integrations.", z76.object({}).shape, READS_EXTERNAL, async () => {
20887
+ server.tool("list_meta_ad_accounts", listMetaAdAccountsGuidance.summary + " Para publicar, prefiere `list_meta_destinations`, que tambi\xE9n devuelve el portfolio y las p\xE1ginas disponibles. Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar Meta en productmaker.app/integrations.", z80.object({}).shape, READS_EXTERNAL, async () => {
19806
20888
  try {
19807
20889
  const r = await api.getJson("/v1/facebook/ad-accounts");
19808
20890
  return asText(r);
@@ -19812,7 +20894,7 @@ function registerConnectionTools(server, api) {
19812
20894
  });
19813
20895
  server.registerTool("list_meta_destinations", {
19814
20896
  description: listMetaDestinationsGuidance.summary + " Es obligatorio llamar esta herramienta antes de publicar, mostrar las opciones y enviar al publicador el `portfolioId`, `adAccountId` y `pageId` elegidos; nunca elijas silenciosamente la primera p\xE1gina o cuenta. No modifica Meta.",
19815
- inputSchema: z76.object({}).shape,
20897
+ inputSchema: z80.object({}).shape,
19816
20898
  annotations: READS_EXTERNAL,
19817
20899
  _meta: {
19818
20900
  "openai/widgetAccessible": true
@@ -19825,7 +20907,7 @@ function registerConnectionTools(server, api) {
19825
20907
  throw translateError(e);
19826
20908
  }
19827
20909
  });
19828
- server.tool("list_tiktok_advertisers", listTikTokAdvertisersGuidance.summary + " Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar TikTok en productmaker.app/integrations.", z76.object({}).shape, READS_EXTERNAL, async () => {
20910
+ server.tool("list_tiktok_advertisers", listTikTokAdvertisersGuidance.summary + " Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar TikTok en productmaker.app/integrations.", z80.object({}).shape, READS_EXTERNAL, async () => {
19829
20911
  try {
19830
20912
  const r = await api.getJson("/v1/tiktok/advertisers");
19831
20913
  return asText(r);
@@ -19837,10 +20919,10 @@ function registerConnectionTools(server, api) {
19837
20919
  __name(registerConnectionTools, "registerConnectionTools");
19838
20920
 
19839
20921
  // src/tools/actors.ts
19840
- import { z as z77 } from "zod";
20922
+ import { z as z81 } from "zod";
19841
20923
  var listActorsGuidance = getProductMakerCapabilityModelGuidance("list_actors");
19842
20924
  function registerActorTools(server, api) {
19843
- server.tool("list_actors", listActorsGuidance.summary + " Devuelve id, nombre, descripci\xF3n, g\xE9nero y voz. Usa el `id` como `actorId` al crear o editar una tarea. Solo aparecen los personajes listos para usar; si la lista est\xE1 vac\xEDa, dile al usuario que cree uno en productmaker.app antes de intentar asignarlo. No modifica nada.", z77.object({}).shape, READS_INTERNAL, async () => {
20925
+ server.tool("list_actors", listActorsGuidance.summary + " Devuelve id, nombre, descripci\xF3n, g\xE9nero y voz. Usa el `id` como `actorId` al crear o editar una tarea. Solo aparecen los personajes listos para usar; si la lista est\xE1 vac\xEDa, dile al usuario que cree uno en productmaker.app antes de intentar asignarlo. No modifica nada.", z81.object({}).shape, READS_INTERNAL, async () => {
19844
20926
  try {
19845
20927
  const rows = await api.getJson("/v1/actors");
19846
20928
  const actors = (Array.isArray(rows) ? rows : []).filter((actor) => actor.status === "ready").map(({ id, name, description, gender, voiceId }) => ({
@@ -19861,40 +20943,7 @@ function registerActorTools(server, api) {
19861
20943
  __name(registerActorTools, "registerActorTools");
19862
20944
 
19863
20945
  // src/tools/publish.ts
19864
- import { z as z78 } from "zod";
19865
-
19866
- // src/tools/stable-uuid.ts
19867
- import { createHash as createHash2 } from "crypto";
19868
- function canonicalize(value) {
19869
- if (Array.isArray(value)) return value.map(canonicalize);
19870
- if (value === null || typeof value !== "object") return value;
19871
- const record = value;
19872
- return Object.fromEntries(Object.keys(record).sort().filter((key) => record[key] !== void 0).map((key) => [
19873
- key,
19874
- canonicalize(record[key])
19875
- ]));
19876
- }
19877
- __name(canonicalize, "canonicalize");
19878
- function canonicalJson(value) {
19879
- return JSON.stringify(canonicalize(value));
19880
- }
19881
- __name(canonicalJson, "canonicalJson");
19882
- function deriveUuidV5(name, namespace) {
19883
- const namespaceBytes = Buffer.from(namespace.replaceAll("-", ""), "hex");
19884
- const digest = createHash2("sha1").update(namespaceBytes).update(name, "utf8").digest();
19885
- const bytes = Buffer.from(digest.subarray(0, 16));
19886
- bytes[6] = (bytes[6] ?? 0) & 15 | 80;
19887
- bytes[8] = (bytes[8] ?? 0) & 63 | 128;
19888
- const hex = bytes.toString("hex");
19889
- return [
19890
- hex.slice(0, 8),
19891
- hex.slice(8, 12),
19892
- hex.slice(12, 16),
19893
- hex.slice(16, 20),
19894
- hex.slice(20)
19895
- ].join("-");
19896
- }
19897
- __name(deriveUuidV5, "deriveUuidV5");
20946
+ import { z as z82 } from "zod";
19898
20947
 
19899
20948
  // src/tools/meta-publish-attempt-id.ts
19900
20949
  var META_PUBLISH_NAMESPACE = "7b19c9ac-b4eb-4db1-970e-c2f24ce30b98";
@@ -19933,40 +20982,40 @@ var publishToShopifyGuidance = getProductMakerCapabilityModelGuidance("publish_t
19933
20982
  var publishToMetaGuidance = getProductMakerCapabilityModelGuidance("publish_to_meta");
19934
20983
  var publishCreativesToMetaGuidance = getProductMakerCapabilityModelGuidance("publish_creatives_to_meta");
19935
20984
  var publishToTikTokGuidance = getProductMakerCapabilityModelGuidance("publish_to_tiktok");
19936
- var ShopifyInput = z78.object({
19937
- taskId: z78.string().min(1),
19938
- shopDomain: z78.string().min(1),
20985
+ var ShopifyInput = z82.object({
20986
+ taskId: z82.string().min(1),
20987
+ shopDomain: z82.string().min(1),
19939
20988
  /** Override: number of landing images to include. Default = ALL images the
19940
20989
  * task generated (the MCP fetches /v1/results first to count). Bounded
19941
20990
  * generously here; the backend validates each index against
19942
20991
  * landing.images.length and rejects out-of-range. */
19943
- numLandingImages: z78.number().int().min(1).max(20).optional(),
19944
- variantIndex: z78.number().int().min(0).optional(),
20992
+ numLandingImages: z82.number().int().min(1).max(20).optional(),
20993
+ variantIndex: z82.number().int().min(0).optional(),
19945
20994
  /** ASK THE USER before setting: "¿Tu producto tiene variantes (talla, color, etc.)?".
19946
20995
  * When true, the landing renders variant-picker pills; when false, it ships
19947
20996
  * a single-option product page. Defaults to false. */
19948
- showVariantPicker: z78.boolean().optional(),
20997
+ showVariantPicker: z82.boolean().optional(),
19949
20998
  /** Default `auto` updates the product previously published for this task and
19950
20999
  * shop. Use `existing` with targetProductId to choose another product, or
19951
21000
  * `new` only when the user explicitly wants a duplicate/new listing. */
19952
- publicationMode: z78.enum([
21001
+ publicationMode: z82.enum([
19953
21002
  "auto",
19954
21003
  "existing",
19955
21004
  "new"
19956
21005
  ]).optional(),
19957
21006
  /** Shopify product GID, numeric id, or admin product URL. Required when
19958
21007
  * publicationMode is `existing`. */
19959
- targetProductId: z78.string().trim().min(1).max(300).optional(),
21008
+ targetProductId: z82.string().trim().min(1).max(300).optional(),
19960
21009
  /** Add an announcement bar text above add-to-cart (optional). */
19961
- announcementBarEnabled: z78.boolean().optional(),
19962
- announcementBarText: z78.string().trim().min(1).max(SHOPIFY_ANNOUNCEMENT_BAR_MAX_LENGTH).optional(),
21010
+ announcementBarEnabled: z82.boolean().optional(),
21011
+ announcementBarText: z82.string().trim().min(1).max(SHOPIFY_ANNOUNCEMENT_BAR_MAX_LENGTH).optional(),
19963
21012
  commerceCorrection: PublishCommerceCorrectionSchema.optional().describe("Complete seller-confirmed commerce values, with the revision and fingerprint returned in recovery. Never invent missing prices or convert currency."),
19964
- confirmed: z78.boolean().optional().describe("Required as true only when submitting commerceCorrection, after showing its values and obtaining explicit seller confirmation.")
21013
+ confirmed: z82.boolean().optional().describe("Required as true only when submitting commerceCorrection, after showing its values and obtaining explicit seller confirmation.")
19965
21014
  });
19966
21015
  var ShopifyInputWithAnnouncementBarValidation = ShopifyInput.superRefine((value, ctx) => {
19967
21016
  if (value.commerceCorrection && value.confirmed !== true) {
19968
21017
  ctx.addIssue({
19969
- code: z78.ZodIssueCode.custom,
21018
+ code: z82.ZodIssueCode.custom,
19970
21019
  path: [
19971
21020
  "confirmed"
19972
21021
  ],
@@ -19975,7 +21024,7 @@ var ShopifyInputWithAnnouncementBarValidation = ShopifyInput.superRefine((value,
19975
21024
  }
19976
21025
  if (value.announcementBarEnabled && !value.announcementBarText) {
19977
21026
  ctx.addIssue({
19978
- code: z78.ZodIssueCode.custom,
21027
+ code: z82.ZodIssueCode.custom,
19979
21028
  path: [
19980
21029
  "announcementBarText"
19981
21030
  ],
@@ -19984,7 +21033,7 @@ var ShopifyInputWithAnnouncementBarValidation = ShopifyInput.superRefine((value,
19984
21033
  }
19985
21034
  if (!value.announcementBarEnabled && value.announcementBarText) {
19986
21035
  ctx.addIssue({
19987
- code: z78.ZodIssueCode.custom,
21036
+ code: z82.ZodIssueCode.custom,
19988
21037
  path: [
19989
21038
  "announcementBarText"
19990
21039
  ],
@@ -19998,91 +21047,91 @@ function parseShopifyInput(rawInput) {
19998
21047
  return parsed.data;
19999
21048
  }
20000
21049
  __name(parseShopifyInput, "parseShopifyInput");
20001
- var McpCampaignConfig = z78.object({
21050
+ var McpCampaignConfig = z82.object({
20002
21051
  /** ALWAYS in US dollars, whatever the ad account's currency is. ProductMaker
20003
21052
  * converts to the account currency server-side. Do NOT pre-convert: on a COP
20004
21053
  * account the user's "60.000 al día" is `dailyBudgetUsd: 15`, not 60000. */
20005
- dailyBudgetUsd: z78.number().positive().optional(),
21054
+ dailyBudgetUsd: z82.number().positive().optional(),
20006
21055
  /** ISO-3166-1 alpha-2 country codes or resolvable names (CountryIso2Schema
20007
21056
  * normalizes at validation). Meta accepts max 1 (single-country ad set).
20008
21057
  * TikTok takes only the first entry. */
20009
- targetCountries: z78.array(CountryIso2Schema).max(1).optional(),
20010
- campaignName: z78.string().trim().min(1).max(100).optional()
21058
+ targetCountries: z82.array(CountryIso2Schema).max(1).optional(),
21059
+ campaignName: z82.string().trim().min(1).max(100).optional()
20011
21060
  });
20012
21061
  var McpAdAccountId = MetaAdAccountId.describe('Meta ad account id. Accepts "act_1234567890" or the bare numeric id "1234567890".');
20013
- var MetaInput = z78.object({
20014
- taskId: z78.string().min(1),
21062
+ var MetaInput = z82.object({
21063
+ taskId: z82.string().min(1),
20015
21064
  /** Exact Meta Business portfolio selected from `list_meta_destinations`. */
20016
- portfolioId: z78.string().trim().min(1),
21065
+ portfolioId: z82.string().trim().min(1),
20017
21066
  /** Exact Facebook Page selected from `list_meta_destinations`. */
20018
- pageId: z78.string().trim().min(1),
21067
+ pageId: z82.string().trim().min(1),
20019
21068
  adAccountId: McpAdAccountId,
20020
- pixelId: z78.string().min(1).optional(),
20021
- landingUrl: z78.string().url().optional(),
21069
+ pixelId: z82.string().min(1).optional(),
21070
+ landingUrl: z82.string().url().optional(),
20022
21071
  /** Select which angles to publish by 0-based index. Omit to publish all
20023
21072
  * successful angles. Example: a task with 3 angles → `[0,2]` publishes #0 + #2. */
20024
- angleIndexes: z78.array(z78.number().int().nonnegative()).optional(),
21073
+ angleIndexes: z82.array(z82.number().int().nonnegative()).optional(),
20025
21074
  campaignConfig: McpCampaignConfig.optional(),
20026
21075
  /** Override the ad's primary text (body copy). If omitted, ProductMaker
20027
21076
  * uses the auto-generated copy attached to the task. Capped at 500 chars
20028
21077
  * (Meta's primary-text limit). */
20029
- primaryText: z78.string().trim().min(1).max(500).optional(),
21078
+ primaryText: z82.string().trim().min(1).max(500).optional(),
20030
21079
  /** Optional WhatsApp number. When set, the ad button opens a WhatsApp chat
20031
21080
  * (wa.me) instead of a landing page. */
20032
- phoneNumber: z78.string().min(8).max(32).optional()
21081
+ phoneNumber: z82.string().min(8).max(32).optional()
20033
21082
  });
20034
- var GroupedTaskSource = z78.object({
20035
- type: z78.literal("task"),
20036
- taskId: z78.string().min(1),
20037
- angleIndex: z78.number().int().min(0).optional(),
20038
- videoVariant: z78.enum([
21083
+ var GroupedTaskSource = z82.object({
21084
+ type: z82.literal("task"),
21085
+ taskId: z82.string().min(1),
21086
+ angleIndex: z82.number().int().min(0).optional(),
21087
+ videoVariant: z82.enum([
20039
21088
  "with_subtitles",
20040
21089
  "without_subtitles"
20041
21090
  ]).optional(),
20042
- imageVariantIndex: z78.number().int().min(0).optional()
20043
- });
20044
- var GroupedMediaSource = z78.object({
20045
- type: z78.literal("media"),
20046
- url: z78.string().url().optional().describe("Public HTTPS media URL. For a file on the user laptop, use request_media_upload on the hosted MCP instead."),
20047
- path: z78.string().min(1).optional().describe('Local file path only when this MCP runs over stdio. On the hosted MCP, call request_media_upload and then use source.type="asset".'),
20048
- thumbnailUrl: z78.string().url().optional(),
20049
- thumbnailPath: z78.string().min(1).optional()
21091
+ imageVariantIndex: z82.number().int().min(0).optional()
21092
+ });
21093
+ var GroupedMediaSource = z82.object({
21094
+ type: z82.literal("media"),
21095
+ url: z82.string().url().optional().describe("Public HTTPS media URL. For a file on the user laptop, use request_media_upload on the hosted MCP instead."),
21096
+ path: z82.string().min(1).optional().describe('Local file path only when this MCP runs over stdio. On the hosted MCP, call request_media_upload and then use source.type="asset".'),
21097
+ thumbnailUrl: z82.string().url().optional(),
21098
+ thumbnailPath: z82.string().min(1).optional()
20050
21099
  }).refine((value) => Boolean(value.url) !== Boolean(value.path), {
20051
21100
  message: "media source needs exactly one of `url` or `path`"
20052
21101
  }).refine((value) => !(value.thumbnailUrl && value.thumbnailPath), {
20053
21102
  message: "media source needs at most one of `thumbnailUrl` or `thumbnailPath`"
20054
21103
  });
20055
- var GroupedUploadedAssetSource = z78.object({
20056
- type: z78.literal("asset"),
21104
+ var GroupedUploadedAssetSource = z82.object({
21105
+ type: z82.literal("asset"),
20057
21106
  /** Returned by check_media_upload. ProductMaker resolves ownership server-side. */
20058
- assetId: z78.string().uuid(),
20059
- thumbnailAssetId: z78.string().uuid().optional()
21107
+ assetId: z82.string().uuid(),
21108
+ thumbnailAssetId: z82.string().uuid().optional()
20060
21109
  });
20061
- var GroupedCreative = z78.object({
20062
- type: z78.enum([
21110
+ var GroupedCreative = z82.object({
21111
+ type: z82.enum([
20063
21112
  "image",
20064
21113
  "video"
20065
21114
  ]),
20066
- source: z78.union([
21115
+ source: z82.union([
20067
21116
  GroupedTaskSource,
20068
21117
  GroupedMediaSource,
20069
21118
  GroupedUploadedAssetSource
20070
21119
  ]),
20071
- title: z78.string().max(120).optional(),
20072
- description: z78.string().max(500).optional(),
21120
+ title: z82.string().max(120).optional(),
21121
+ description: z82.string().max(500).optional(),
20073
21122
  cta: MetaCtaType.optional(),
20074
- creativeId: z78.string().min(1).optional()
21123
+ creativeId: z82.string().min(1).optional()
20075
21124
  });
20076
- var GroupedCampaignConfigBase = z78.object({
21125
+ var GroupedCampaignConfigBase = z82.object({
20077
21126
  campaignType: MetaCampaignType.default("sales"),
20078
21127
  optimizationMode: MetaOptimizationMode.default("advantage_plus"),
20079
21128
  budgetMode: MetaBudgetMode.optional(),
20080
- campaignDailyBudget: z78.number().int().positive().optional(),
20081
- adSets: z78.array(z78.object({
20082
- tempId: z78.string().min(1).max(80),
20083
- name: z78.string().min(1).max(100),
20084
- dailyBudget: z78.number().int().positive().optional(),
20085
- creativeIds: z78.array(z78.string().min(1)).min(1)
21129
+ campaignDailyBudget: z82.number().int().positive().optional(),
21130
+ adSets: z82.array(z82.object({
21131
+ tempId: z82.string().min(1).max(80),
21132
+ name: z82.string().min(1).max(100),
21133
+ dailyBudget: z82.number().int().positive().optional(),
21134
+ creativeIds: z82.array(z82.string().min(1)).min(1)
20086
21135
  })).min(1).max(CAMPAIGN_LIMITS.MAX_AD_SETS),
20087
21136
  /**
20088
21137
  * Valid values are 'landing' and 'whatsapp' — there is no 'website'. This
@@ -20090,17 +21139,17 @@ var GroupedCampaignConfigBase = z78.object({
20090
21139
  * opaque `invalid_literal` and made the WhatsApp destination — supported by
20091
21140
  * the API since #849 — unreachable from this tool.
20092
21141
  */
20093
- destination: z78.discriminatedUnion("type", [
20094
- z78.object({
20095
- type: z78.literal("landing"),
20096
- landingUrl: z78.string().url()
21142
+ destination: z82.discriminatedUnion("type", [
21143
+ z82.object({
21144
+ type: z82.literal("landing"),
21145
+ landingUrl: z82.string().url()
20097
21146
  }),
20098
- z78.object({
20099
- type: z78.literal("whatsapp"),
20100
- phoneNumber: z78.string().min(8).max(32)
21147
+ z82.object({
21148
+ type: z82.literal("whatsapp"),
21149
+ phoneNumber: z82.string().min(8).max(32)
20101
21150
  })
20102
21151
  ]),
20103
- saveAsDefault: z78.boolean().default(false),
21152
+ saveAsDefault: z82.boolean().default(false),
20104
21153
  budgetPolicy: MetaBudgetPolicy.optional()
20105
21154
  });
20106
21155
  function resolveBudgetMode(value) {
@@ -20114,7 +21163,7 @@ var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) =
20114
21163
  const budgetMode = resolveBudgetMode(value);
20115
21164
  if (budgetMode === "campaign" && value.campaignDailyBudget === void 0) {
20116
21165
  ctx.addIssue({
20117
- code: z78.ZodIssueCode.custom,
21166
+ code: z82.ZodIssueCode.custom,
20118
21167
  path: [
20119
21168
  "campaignDailyBudget"
20120
21169
  ],
@@ -20125,7 +21174,7 @@ var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) =
20125
21174
  value.adSets.forEach((adSet, index) => {
20126
21175
  if (adSet.dailyBudget === void 0) {
20127
21176
  ctx.addIssue({
20128
- code: z78.ZodIssueCode.custom,
21177
+ code: z82.ZodIssueCode.custom,
20129
21178
  path: [
20130
21179
  "adSets",
20131
21180
  index,
@@ -20140,28 +21189,28 @@ var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) =
20140
21189
  ...value,
20141
21190
  budgetMode: resolveBudgetMode(value)
20142
21191
  }));
20143
- var PublishCreativesToMetaInputBase = z78.object({
20144
- creatives: z78.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
21192
+ var PublishCreativesToMetaInputBase = z82.object({
21193
+ creatives: z82.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
20145
21194
  /** Exact Meta Business portfolio selected from `list_meta_destinations`. */
20146
- portfolioId: z78.string().trim().min(1),
21195
+ portfolioId: z82.string().trim().min(1),
20147
21196
  /** Exact Facebook Page selected from `list_meta_destinations`. */
20148
- pageId: z78.string().trim().min(1),
21197
+ pageId: z82.string().trim().min(1),
20149
21198
  adAccountId: McpAdAccountId,
20150
- pixelId: z78.string().min(1).optional(),
21199
+ pixelId: z82.string().min(1).optional(),
20151
21200
  /** Required unless `campaignConfig.destination` is whatsapp — see the
20152
21201
  * superRefine below. Mirrors the API's `FacebookCreateCampaignFromTasksBodySchema`. */
20153
- landingUrl: z78.string().url().optional(),
21202
+ landingUrl: z82.string().url().optional(),
20154
21203
  /** ISO-2 code or resolvable country name — mirrors the API contract
20155
21204
  * (CountryIso2Schema), which normalizes names to alpha-2 at validation. */
20156
21205
  targetCountry: CountryIso2Schema,
20157
- campaignName: z78.string().trim().min(1).max(100),
21206
+ campaignName: z82.string().trim().min(1).max(100),
20158
21207
  campaignConfig: GroupedCampaignConfig.optional()
20159
21208
  });
20160
21209
  var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((value, ctx) => {
20161
21210
  const destination = value.campaignConfig?.destination;
20162
21211
  if (destination?.type !== "whatsapp" && destination?.type !== "landing" && !value.landingUrl) {
20163
21212
  ctx.addIssue({
20164
- code: z78.ZodIssueCode.custom,
21213
+ code: z82.ZodIssueCode.custom,
20165
21214
  path: [
20166
21215
  "landingUrl"
20167
21216
  ],
@@ -20170,7 +21219,7 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
20170
21219
  }
20171
21220
  if (destination?.type === "landing" && value.landingUrl && destination.landingUrl !== value.landingUrl) {
20172
21221
  ctx.addIssue({
20173
- code: z78.ZodIssueCode.custom,
21222
+ code: z82.ZodIssueCode.custom,
20174
21223
  path: [
20175
21224
  "campaignConfig",
20176
21225
  "destination",
@@ -20189,16 +21238,16 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
20189
21238
  }
20190
21239
  return value;
20191
21240
  });
20192
- var PresignUploadResponseSchema = z78.object({
20193
- uploadUrl: z78.string().url().refine((value) => {
21241
+ var PresignUploadResponseSchema = z82.object({
21242
+ uploadUrl: z82.string().url().refine((value) => {
20194
21243
  const url = new URL(value);
20195
21244
  return !url.username && !url.password && url.protocol === "https:";
20196
21245
  }, "presigned upload URL must be HTTPS without embedded credentials"),
20197
- publicUrl: z78.string().url().refine((value) => {
21246
+ publicUrl: z82.string().url().refine((value) => {
20198
21247
  const url = new URL(value);
20199
21248
  return !url.username && !url.password;
20200
21249
  }, "public media URL must not contain embedded credentials"),
20201
- stagingAssetId: z78.string().uuid()
21250
+ stagingAssetId: z82.string().uuid()
20202
21251
  });
20203
21252
  async function uploadResolvedMedia(api, media, assetRole) {
20204
21253
  if (!media.buffer) throw new ResolveMediaError("INVALID_INPUT", "resolved media has no bytes");
@@ -20223,16 +21272,16 @@ async function resolveAndUploadMedia(api, ref, expectedKind, assetRole, transpor
20223
21272
  return uploadResolvedMedia(api, media, assetRole);
20224
21273
  }
20225
21274
  __name(resolveAndUploadMedia, "resolveAndUploadMedia");
20226
- var TikTokInput = z78.object({
20227
- taskId: z78.string().min(1),
20228
- advertiserId: z78.string().min(1),
20229
- pixelId: z78.string().min(1).optional(),
20230
- landingUrl: z78.string().url().optional(),
20231
- angleIndexes: z78.array(z78.number().int().nonnegative()).optional(),
21275
+ var TikTokInput = z82.object({
21276
+ taskId: z82.string().min(1),
21277
+ advertiserId: z82.string().min(1),
21278
+ pixelId: z82.string().min(1).optional(),
21279
+ landingUrl: z82.string().url().optional(),
21280
+ angleIndexes: z82.array(z82.number().int().nonnegative()).optional(),
20232
21281
  campaignConfig: McpCampaignConfig.optional(),
20233
21282
  /** Override the ad's description (TikTok's body copy). If omitted,
20234
21283
  * ProductMaker uses the auto-generated copy attached to the task. */
20235
- primaryText: z78.string().trim().min(1).max(500).optional()
21284
+ primaryText: z82.string().trim().min(1).max(500).optional()
20236
21285
  });
20237
21286
  function registerPublishTools(server, api, opts = {}) {
20238
21287
  const transport = opts.transport ?? "http";
@@ -20560,39 +21609,39 @@ __name(toTikTokCampaignConfig, "toTikTokCampaignConfig");
20560
21609
 
20561
21610
  // src/tools/meta-upload.ts
20562
21611
  import { registerAppTool } from "@modelcontextprotocol/ext-apps/server";
20563
- import { z as z79 } from "zod";
21612
+ import { z as z83 } from "zod";
20564
21613
  var requestMediaUploadGuidance = getProductMakerCapabilityModelGuidance("request_media_upload");
20565
- var UploadSessionInputBase = z79.object({
20566
- mediaKind: z79.enum([
21614
+ var UploadSessionInputBase = z83.object({
21615
+ mediaKind: z83.enum([
20567
21616
  "image",
20568
21617
  "video",
20569
21618
  "mixed"
20570
21619
  ]).default("mixed").describe("The kind of file the user will select. Use image for product references and landing pages, video for a single video, and mixed only when the consumer supports both."),
20571
- assetRole: z79.enum([
21620
+ assetRole: z83.enum([
20572
21621
  "media",
20573
21622
  "thumbnail"
20574
21623
  ]).default("media").describe("Use media for the creative itself; thumbnail only for an image thumbnail."),
20575
- uploadPurpose: z79.enum([
21624
+ uploadPurpose: z83.enum([
20576
21625
  "product_reference",
20577
21626
  "creative",
20578
21627
  "generic"
20579
21628
  ]).default("generic").describe("The user's purpose for these files. Use product_reference for a product photo used by tasks or landings, creative for media that will become an ad or creative, and generic only when neither applies."),
20580
- maxFiles: z79.number().int().min(1).max(10).optional().describe("Optional batch limit: image accepts 1-5, video exactly 1, and mixed 1-10.")
21629
+ maxFiles: z83.number().int().min(1).max(10).optional().describe("Optional batch limit: image accepts 1-5, video exactly 1, and mixed 1-10.")
20581
21630
  });
20582
- var OpenAiFileReference = z79.object({
20583
- download_url: z79.string().url().describe("Required by ChatGPT's openai/fileParams schema. The App prefers getFileDownloadUrl(file_id) for a fresh URL, and the server never fetches this value."),
20584
- file_id: z79.string().min(1),
20585
- mime_type: z79.string().min(1).optional(),
20586
- file_name: z79.string().min(1).optional()
21631
+ var OpenAiFileReference = z83.object({
21632
+ download_url: z83.string().url().describe("Required by ChatGPT's openai/fileParams schema. The App prefers getFileDownloadUrl(file_id) for a fresh URL, and the server never fetches this value."),
21633
+ file_id: z83.string().min(1),
21634
+ mime_type: z83.string().min(1).optional(),
21635
+ file_name: z83.string().min(1).optional()
20587
21636
  });
20588
21637
  var UploadSessionToolInputShape = {
20589
21638
  ...UploadSessionInputBase.shape,
20590
- files: z79.array(OpenAiFileReference).max(10).optional().describe("Optional OpenAI-hosted file references provided by ChatGPT. They are only metadata for the UI bridge and are never forwarded to ProductMaker.")
21639
+ files: z83.array(OpenAiFileReference).max(10).optional().describe("Optional OpenAI-hosted file references provided by ChatGPT. They are only metadata for the UI bridge and are never forwarded to ProductMaker.")
20591
21640
  };
20592
- var UploadSessionToolInput = z79.object(UploadSessionToolInputShape).superRefine((value, ctx) => {
21641
+ var UploadSessionToolInput = z83.object(UploadSessionToolInputShape).superRefine((value, ctx) => {
20593
21642
  if (value.assetRole === "thumbnail" && value.mediaKind !== "image") {
20594
21643
  ctx.addIssue({
20595
- code: z79.ZodIssueCode.custom,
21644
+ code: z83.ZodIssueCode.custom,
20596
21645
  path: [
20597
21646
  "mediaKind"
20598
21647
  ],
@@ -20601,7 +21650,7 @@ var UploadSessionToolInput = z79.object(UploadSessionToolInputShape).superRefine
20601
21650
  }
20602
21651
  if (value.mediaKind === "video" && value.maxFiles !== void 0 && value.maxFiles !== 1) {
20603
21652
  ctx.addIssue({
20604
- code: z79.ZodIssueCode.custom,
21653
+ code: z83.ZodIssueCode.custom,
20605
21654
  path: [
20606
21655
  "maxFiles"
20607
21656
  ],
@@ -20610,7 +21659,7 @@ var UploadSessionToolInput = z79.object(UploadSessionToolInputShape).superRefine
20610
21659
  }
20611
21660
  if (value.mediaKind === "image" && (value.maxFiles ?? 1) > 5) {
20612
21661
  ctx.addIssue({
20613
- code: z79.ZodIssueCode.custom,
21662
+ code: z83.ZodIssueCode.custom,
20614
21663
  path: [
20615
21664
  "maxFiles"
20616
21665
  ],
@@ -20618,38 +21667,38 @@ var UploadSessionToolInput = z79.object(UploadSessionToolInputShape).superRefine
20618
21667
  });
20619
21668
  }
20620
21669
  });
20621
- var UploadSessionResponse = z79.object({
20622
- sessionId: z79.string().uuid(),
20623
- uploadPageUrl: z79.string().url(),
20624
- mediaKind: z79.enum([
21670
+ var UploadSessionResponse = z83.object({
21671
+ sessionId: z83.string().uuid(),
21672
+ uploadPageUrl: z83.string().url(),
21673
+ mediaKind: z83.enum([
20625
21674
  "image",
20626
21675
  "video",
20627
21676
  "mixed"
20628
21677
  ]),
20629
- assetRole: z79.enum([
21678
+ assetRole: z83.enum([
20630
21679
  "media",
20631
21680
  "thumbnail"
20632
21681
  ]),
20633
- maxFiles: z79.number().int().positive(),
20634
- expiresAt: z79.string().datetime()
21682
+ maxFiles: z83.number().int().positive(),
21683
+ expiresAt: z83.string().datetime()
20635
21684
  });
20636
- var UploadStatusInput = z79.object({
20637
- sessionId: z79.string().uuid().describe("The sessionId returned by request_media_upload.")
21685
+ var UploadStatusInput = z83.object({
21686
+ sessionId: z83.string().uuid().describe("The sessionId returned by request_media_upload.")
20638
21687
  });
20639
- var UniversalUploadStatusResponse = z79.discriminatedUnion("status", [
20640
- z79.object({
20641
- status: z79.literal("pending"),
20642
- assets: z79.array(z79.never()).length(0)
21688
+ var UniversalUploadStatusResponse = z83.discriminatedUnion("status", [
21689
+ z83.object({
21690
+ status: z83.literal("pending"),
21691
+ assets: z83.array(z83.never()).length(0)
20643
21692
  }),
20644
- z79.object({
20645
- status: z79.literal("uploaded"),
20646
- assets: z79.array(z79.object({
20647
- assetId: z79.string().uuid(),
20648
- mediaKind: z79.enum([
21693
+ z83.object({
21694
+ status: z83.literal("uploaded"),
21695
+ assets: z83.array(z83.object({
21696
+ assetId: z83.string().uuid(),
21697
+ mediaKind: z83.enum([
20649
21698
  "image",
20650
21699
  "video"
20651
21700
  ]),
20652
- assetRole: z79.enum([
21701
+ assetRole: z83.enum([
20653
21702
  "media",
20654
21703
  "thumbnail"
20655
21704
  ])
@@ -20657,13 +21706,13 @@ var UniversalUploadStatusResponse = z79.discriminatedUnion("status", [
20657
21706
  })
20658
21707
  ]);
20659
21708
  var UniversalUploadStructuredResponse = UploadSessionResponse.extend({
20660
- uploadPurpose: z79.enum([
21709
+ uploadPurpose: z83.enum([
20661
21710
  "product_reference",
20662
21711
  "creative",
20663
21712
  "generic"
20664
21713
  ]),
20665
- checkToolName: z79.literal("check_media_upload"),
20666
- resolveEndpoint: z79.string().url()
21714
+ checkToolName: z83.literal("check_media_upload"),
21715
+ resolveEndpoint: z83.string().url()
20667
21716
  });
20668
21717
  function contextualizeUploadPageUrl(uploadPageUrl, uploadPurpose) {
20669
21718
  const url = new URL(uploadPageUrl);
@@ -20754,15 +21803,15 @@ function registerMediaUploadTools(server, api) {
20754
21803
  __name(registerMediaUploadTools, "registerMediaUploadTools");
20755
21804
 
20756
21805
  // src/tools/campaigns.ts
20757
- import { z as z80 } from "zod";
21806
+ import { z as z84 } from "zod";
20758
21807
  var getMetaCampaignStatusGuidance = getProductMakerCapabilityModelGuidance("get_meta_campaign_status");
20759
21808
  var getTikTokCampaignStatusGuidance = getProductMakerCapabilityModelGuidance("get_tiktok_campaign_status");
20760
- var MetaStatusInput = z80.object({
20761
- campaignId: z80.string().min(1).describe("Meta campaign ID returned by publish_to_meta.")
21809
+ var MetaStatusInput = z84.object({
21810
+ campaignId: z84.string().min(1).describe("Meta campaign ID returned by publish_to_meta.")
20762
21811
  });
20763
- var TikTokStatusInput = z80.object({
20764
- campaignId: z80.string().min(1).describe("TikTok campaign ID returned by publish_to_tiktok."),
20765
- advertiserId: z80.string().min(1).describe("The same advertiserId used to publish the campaign.")
21812
+ var TikTokStatusInput = z84.object({
21813
+ campaignId: z84.string().min(1).describe("TikTok campaign ID returned by publish_to_tiktok."),
21814
+ advertiserId: z84.string().min(1).describe("The same advertiserId used to publish the campaign.")
20766
21815
  });
20767
21816
  function registerCampaignTools(server, api) {
20768
21817
  server.tool("get_meta_campaign_status", getMetaCampaignStatusGuidance.summary + " 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, READS_EXTERNAL, async (input) => {
@@ -20859,10 +21908,10 @@ function renderTikTokStatusMarkdown(r) {
20859
21908
  __name(renderTikTokStatusMarkdown, "renderTikTokStatusMarkdown");
20860
21909
 
20861
21910
  // ../../packages/product-economics/src/product-costing.ts
20862
- import { z as z82 } from "zod";
21911
+ import { z as z86 } from "zod";
20863
21912
 
20864
21913
  // ../../packages/product-economics/src/product-costing-countries.ts
20865
- import { z as z81 } from "zod";
21914
+ import { z as z85 } from "zod";
20866
21915
 
20867
21916
  // ../../packages/product-economics/src/product-costing-shipping-references.json
20868
21917
  var product_costing_shipping_references_default = {
@@ -20926,21 +21975,21 @@ var product_costing_shipping_references_default = {
20926
21975
  };
20927
21976
 
20928
21977
  // ../../packages/product-economics/src/product-costing-countries.ts
20929
- var ShippingReferenceSchema = z81.object({
20930
- reference: z81.number().positive(),
20931
- min: z81.number().nonnegative(),
20932
- max: z81.number().positive(),
20933
- updatedAt: z81.string().date(),
20934
- confidence: z81.enum([
21978
+ var ShippingReferenceSchema = z85.object({
21979
+ reference: z85.number().positive(),
21980
+ min: z85.number().nonnegative(),
21981
+ max: z85.number().positive(),
21982
+ updatedAt: z85.string().date(),
21983
+ confidence: z85.enum([
20935
21984
  "high",
20936
21985
  "medium",
20937
21986
  "medium-low"
20938
21987
  ]),
20939
- sources: z81.array(z81.string().url().startsWith("https://")).min(1).max(3)
21988
+ sources: z85.array(z85.string().url().startsWith("https://")).min(1).max(3)
20940
21989
  }).superRefine(({ reference, min, max }, context) => {
20941
21990
  if (min > reference || reference > max) {
20942
21991
  context.addIssue({
20943
- code: z81.ZodIssueCode.custom,
21992
+ code: z85.ZodIssueCode.custom,
20944
21993
  message: "shipping_reference_outside_range",
20945
21994
  path: [
20946
21995
  "reference"
@@ -20948,7 +21997,7 @@ var ShippingReferenceSchema = z81.object({
20948
21997
  });
20949
21998
  }
20950
21999
  });
20951
- var SHIPPING_REFERENCES = z81.record(ShippingReferenceSchema).parse(product_costing_shipping_references_default);
22000
+ var SHIPPING_REFERENCES = z85.record(ShippingReferenceSchema).parse(product_costing_shipping_references_default);
20952
22001
  var PRODUCT_COSTING_COUNTRY_CODES = Object.freeze(Object.keys(SHIPPING_REFERENCES));
20953
22002
  var SPANISH_LOCALE_COUNTRIES = /* @__PURE__ */ new Set([
20954
22003
  "MX",
@@ -21130,18 +22179,18 @@ function getProductCostingCountryName(country) {
21130
22179
  __name(getProductCostingCountryName, "getProductCostingCountryName");
21131
22180
 
21132
22181
  // ../../packages/product-economics/src/product-costing.ts
21133
- var ProductCostingInputSchema = z82.object({
21134
- country: z82.string().refine(isProductCostingCountryCode, "unsupported_country"),
21135
- supplierCost: z82.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").positive("supplier_cost_positive"),
21136
- shippingCost: z82.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("shipping_cost_nonnegative"),
21137
- returnRate: z82.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
21138
- cancellationRate: z82.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
21139
- adsManagerCpa: z82.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("cpa_nonnegative"),
21140
- desiredMargin: z82.number().finite().min(0, "margin_nonnegative").lt(1, "margin_below_one")
22182
+ var ProductCostingInputSchema = z86.object({
22183
+ country: z86.string().refine(isProductCostingCountryCode, "unsupported_country"),
22184
+ supplierCost: z86.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").positive("supplier_cost_positive"),
22185
+ shippingCost: z86.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("shipping_cost_nonnegative"),
22186
+ returnRate: z86.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
22187
+ cancellationRate: z86.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
22188
+ adsManagerCpa: z86.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("cpa_nonnegative"),
22189
+ desiredMargin: z86.number().finite().min(0, "margin_nonnegative").lt(1, "margin_below_one")
21141
22190
  }).superRefine(({ returnRate, cancellationRate }, context) => {
21142
22191
  if (returnRate + cancellationRate >= 1) {
21143
22192
  context.addIssue({
21144
- code: z82.ZodIssueCode.custom,
22193
+ code: z86.ZodIssueCode.custom,
21145
22194
  message: "delivery_rate_positive",
21146
22195
  path: [
21147
22196
  "cancellationRate"
@@ -21238,15 +22287,15 @@ function calculateProductCosting(rawInput) {
21238
22287
  __name(calculateProductCosting, "calculateProductCosting");
21239
22288
 
21240
22289
  // src/tools/product-costing.ts
21241
- import { z as z83 } from "zod";
22290
+ import { z as z87 } from "zod";
21242
22291
  var costProductGuidance = getProductMakerCapabilityModelGuidance("cost_product");
21243
- var optionalAmount = /* @__PURE__ */ __name((description) => z83.number().finite().max(Number.MAX_SAFE_INTEGER).nonnegative().optional().describe(description), "optionalAmount");
21244
- var optionalPercent = /* @__PURE__ */ __name((description) => z83.number().finite().min(0).lt(100).optional().describe(description), "optionalPercent");
21245
- var ProductCostingToolInput = z83.object({
21246
- country: z83.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
22292
+ var optionalAmount = /* @__PURE__ */ __name((description) => z87.number().finite().max(Number.MAX_SAFE_INTEGER).nonnegative().optional().describe(description), "optionalAmount");
22293
+ var optionalPercent = /* @__PURE__ */ __name((description) => z87.number().finite().min(0).lt(100).optional().describe(description), "optionalPercent");
22294
+ var ProductCostingToolInput = z87.object({
22295
+ country: z87.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
21247
22296
  message: "Pa\xEDs no soportado por ProductMaker."
21248
22297
  }).optional().describe("C\xF3digo ISO-2 de cualquiera de los pa\xEDses disponibles en ProductMaker. Omite para Colombia."),
21249
- supplierCost: z83.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."),
22298
+ supplierCost: z87.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."),
21250
22299
  supplierCostCop: optionalAmount("Alias anterior para costo del proveedor en Colombia. Prefiere supplierCost."),
21251
22300
  shippingCost: optionalAmount("Costo de un env\xEDo en la moneda del pa\xEDs. Omite para usar el valor gu\xEDa vigente."),
21252
22301
  shippingCostCop: optionalAmount("Alias anterior para flete en Colombia. Prefiere shippingCost."),
@@ -21405,7 +22454,7 @@ function formatPercent(value) {
21405
22454
  __name(formatPercent, "formatPercent");
21406
22455
 
21407
22456
  // src/tools/product-research.ts
21408
- import { z as z84 } from "zod";
22457
+ import { z as z88 } from "zod";
21409
22458
 
21410
22459
  // src/tools/product-research-idempotency.ts
21411
22460
  var RESEARCH_IDEMPOTENCY_WINDOW_MS = 2 * 60 * 60 * 1e3;
@@ -21776,35 +22825,35 @@ var TERMINAL2 = /* @__PURE__ */ new Set([
21776
22825
  ]);
21777
22826
  var POLL_INTERVAL_MS = 5e3;
21778
22827
  var MAX_WAIT_SECONDS = 120;
21779
- var FindInput = z84.object({
22828
+ var FindInput = z88.object({
21780
22829
  targetCountry: ResearchTargetCountryEnum.describe("Pa\xEDs objetivo. S\xF3lo CO, MX, PE, EC y CL tienen cobertura de investigaci\xF3n."),
21781
22830
  categoryKey: ResearchCategoryEnum.default("auto").describe('Categor\xEDa a investigar. Usa "auto" para que la IA elija seg\xFAn el mercado.'),
21782
- confirmed: z84.boolean().describe(`Ponlo en true SOLO despu\xE9s de que el usuario haya aceptado el cargo de ${PRICE_CREDITS} cr\xE9ditos. No lo asumas.`),
21783
- waitSeconds: z84.number().int().min(0).max(MAX_WAIT_SECONDS).optional().describe("Segundos a esperar el reporte antes de devolver el runId. M\xE1ximo 120.")
22831
+ confirmed: z88.boolean().describe(`Ponlo en true SOLO despu\xE9s de que el usuario haya aceptado el cargo de ${PRICE_CREDITS} cr\xE9ditos. No lo asumas.`),
22832
+ waitSeconds: z88.number().int().min(0).max(MAX_WAIT_SECONDS).optional().describe("Segundos a esperar el reporte antes de devolver el runId. M\xE1ximo 120.")
21784
22833
  });
21785
- var GetInput = z84.object({
21786
- runId: z84.string().uuid().describe("ID del run devuelto por find_winning_products.")
22834
+ var GetInput = z88.object({
22835
+ runId: z88.string().uuid().describe("ID del run devuelto por find_winning_products.")
21787
22836
  });
21788
- var ListInput = z84.object({
21789
- limit: z84.number().int().min(1).max(20).default(10)
22837
+ var ListInput = z88.object({
22838
+ limit: z88.number().int().min(1).max(20).default(10)
21790
22839
  });
21791
- function textResult(text) {
22840
+ function textResult(text2) {
21792
22841
  return {
21793
22842
  content: [
21794
22843
  {
21795
22844
  type: "text",
21796
- text
22845
+ text: text2
21797
22846
  }
21798
22847
  ]
21799
22848
  };
21800
22849
  }
21801
22850
  __name(textResult, "textResult");
21802
- function researchResult(run, text) {
22851
+ function researchResult(run, text2) {
21803
22852
  return {
21804
22853
  content: [
21805
22854
  {
21806
22855
  type: "text",
21807
- text
22856
+ text: text2
21808
22857
  }
21809
22858
  ],
21810
22859
  structuredContent: run
@@ -21909,17 +22958,17 @@ _Esta llamada no cobr\xF3 cr\xE9ditos: se reutiliz\xF3 una investigaci\xF3n reci
21909
22958
  __name(registerProductResearchTools, "registerProductResearchTools");
21910
22959
 
21911
22960
  // src/tools/dropshipping-projects.ts
21912
- import { z as z85 } from "zod";
22961
+ import { z as z89 } from "zod";
21913
22962
  var startDropshippingProjectGuidance = getProductMakerCapabilityModelGuidance("start_dropshipping_project");
21914
22963
  var costDropshippingProjectGuidance = getProductMakerCapabilityModelGuidance("cost_dropshipping_project");
21915
22964
  var guideDropshippingLaunchGuidance = getProductMakerCapabilityModelGuidance("guide_dropshipping_launch");
21916
- var ProjectId = z85.string().uuid();
21917
- var DropshippingProjectStatus = z85.enum([
22965
+ var ProjectId = z89.string().uuid();
22966
+ var DropshippingProjectStatus = z89.enum([
21918
22967
  "active",
21919
22968
  "completed",
21920
22969
  "archived"
21921
22970
  ]);
21922
- var DropshippingProjectPhase = z85.enum([
22971
+ var DropshippingProjectPhase = z89.enum([
21923
22972
  "research",
21924
22973
  "costing",
21925
22974
  "media",
@@ -21928,21 +22977,21 @@ var DropshippingProjectPhase = z85.enum([
21928
22977
  "advertising",
21929
22978
  "complete"
21930
22979
  ]);
21931
- var DropshippingRewardMilestone = z85.enum([
22980
+ var DropshippingRewardMilestone = z89.enum([
21932
22981
  "product_selected",
21933
22982
  "costing_saved",
21934
22983
  "product_media_ready",
21935
22984
  "creatives_completed",
21936
22985
  "campaign_created"
21937
22986
  ]);
21938
- var TargetCountry = z85.enum([
22987
+ var TargetCountry = z89.enum([
21939
22988
  "CO",
21940
22989
  "MX",
21941
22990
  "PE",
21942
22991
  "EC",
21943
22992
  "CL"
21944
22993
  ]);
21945
- var ResearchCategory = z85.enum([
22994
+ var ResearchCategory = z89.enum([
21946
22995
  "auto",
21947
22996
  "automotive",
21948
22997
  "kitchen",
@@ -21957,7 +23006,7 @@ var ResearchCategory = z85.enum([
21957
23006
  "garden",
21958
23007
  "cleaning"
21959
23008
  ]);
21960
- var HttpUrl3 = z85.string().url().max(2e3).refine((value) => {
23009
+ var HttpUrl3 = z89.string().url().max(2e3).refine((value) => {
21961
23010
  try {
21962
23011
  return [
21963
23012
  "http:",
@@ -21967,45 +23016,45 @@ var HttpUrl3 = z85.string().url().max(2e3).refine((value) => {
21967
23016
  return false;
21968
23017
  }
21969
23018
  }, "URL must use HTTP(S)");
21970
- var ResearchCandidateOrigin = z85.object({
21971
- type: z85.literal("research_candidate"),
23019
+ var ResearchCandidateOrigin = z89.object({
23020
+ type: z89.literal("research_candidate"),
21972
23021
  runId: ProjectId,
21973
- candidateSourceId: z85.string().min(1).max(160)
23022
+ candidateSourceId: z89.string().min(1).max(160)
21974
23023
  });
21975
- var TaskOrigin = z85.object({
21976
- type: z85.literal("task"),
23024
+ var TaskOrigin = z89.object({
23025
+ type: z89.literal("task"),
21977
23026
  taskId: ProjectId
21978
23027
  });
21979
- var ManualOrigin = z85.object({
21980
- type: z85.literal("manual"),
23028
+ var ManualOrigin = z89.object({
23029
+ type: z89.literal("manual"),
21981
23030
  clientRequestId: ProjectId,
21982
- country: z85.string().min(2).max(3).optional()
23031
+ country: z89.string().min(2).max(3).optional()
21983
23032
  });
21984
- var StartProjectInput = z85.object({
21985
- origin: z85.discriminatedUnion("type", [
23033
+ var StartProjectInput = z89.object({
23034
+ origin: z89.discriminatedUnion("type", [
21986
23035
  ResearchCandidateOrigin,
21987
23036
  TaskOrigin,
21988
23037
  ManualOrigin
21989
23038
  ]),
21990
- productName: z85.string().trim().min(1).max(240).optional(),
23039
+ productName: z89.string().trim().min(1).max(240).optional(),
21991
23040
  productUrl: HttpUrl3.optional(),
21992
- country: z85.string().min(2).max(3).optional(),
21993
- languageCode: z85.string().min(2).max(16).optional()
23041
+ country: z89.string().min(2).max(3).optional(),
23042
+ languageCode: z89.string().min(2).max(16).optional()
21994
23043
  }).strict();
21995
- var CostProjectInput = z85.object({
23044
+ var CostProjectInput = z89.object({
21996
23045
  projectId: ProjectId,
21997
- version: z85.number().int().positive().describe("Version le\xEDda del proyecto. Copia la \xFAltima que viste; no se la pidas al usuario."),
21998
- country: z85.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
23046
+ version: z89.number().int().positive().describe("Version le\xEDda del proyecto. Copia la \xFAltima que viste; no se la pidas al usuario."),
23047
+ country: z89.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
21999
23048
  message: "Pa\xEDs no soportado por ProductMaker."
22000
23049
  }).describe("C\xF3digo ISO-2 del pa\xEDs del proyecto. Usa el `targetCountry` que devuelve el proyecto."),
22001
- supplierCost: z85.number().finite().positive().describe("Costo real de una unidad con el proveedor, en la moneda del pa\xEDs. \xDANICO dato que debes pedirle al usuario: nunca lo inventes."),
22002
- shippingCost: z85.number().finite().nonnegative().optional().describe("Costo de un env\xEDo. Omite para usar el valor gu\xEDa del pa\xEDs en vez de inventarlo."),
22003
- returnRate: z85.number().finite().min(0).lt(1).optional().describe("Fracci\xF3n de pedidos devueltos (0.25 = 25%). Omite para usar el valor gu\xEDa del pa\xEDs."),
22004
- cancellationRate: z85.number().finite().min(0).lt(1).optional().describe("Fracci\xF3n de pedidos cancelados (0.15 = 15%). Omite para usar el valor gu\xEDa del pa\xEDs."),
22005
- adsManagerCpa: z85.number().finite().nonnegative().optional().describe("Costo por compra de Ads Manager. Omite para usar el valor gu\xEDa del pa\xEDs."),
22006
- desiredMargin: z85.number().finite().min(0).lt(1).optional().describe("Margen deseado (0.1 = 10%). Omite para usar el valor gu\xEDa del pa\xEDs.")
22007
- });
22008
- var GuideInput = z85.object({
23050
+ supplierCost: z89.number().finite().positive().describe("Costo real de una unidad con el proveedor, en la moneda del pa\xEDs. \xDANICO dato que debes pedirle al usuario: nunca lo inventes."),
23051
+ shippingCost: z89.number().finite().nonnegative().optional().describe("Costo de un env\xEDo. Omite para usar el valor gu\xEDa del pa\xEDs en vez de inventarlo."),
23052
+ returnRate: z89.number().finite().min(0).lt(1).optional().describe("Fracci\xF3n de pedidos devueltos (0.25 = 25%). Omite para usar el valor gu\xEDa del pa\xEDs."),
23053
+ cancellationRate: z89.number().finite().min(0).lt(1).optional().describe("Fracci\xF3n de pedidos cancelados (0.15 = 15%). Omite para usar el valor gu\xEDa del pa\xEDs."),
23054
+ adsManagerCpa: z89.number().finite().nonnegative().optional().describe("Costo por compra de Ads Manager. Omite para usar el valor gu\xEDa del pa\xEDs."),
23055
+ desiredMargin: z89.number().finite().min(0).lt(1).optional().describe("Margen deseado (0.1 = 10%). Omite para usar el valor gu\xEDa del pa\xEDs.")
23056
+ });
23057
+ var GuideInput = z89.object({
22009
23058
  projectId: ProjectId.optional()
22010
23059
  }).strict();
22011
23060
  var NEXT_ACTION_KINDS = [
@@ -22023,29 +23072,29 @@ var NEXT_ACTION_KINDS = [
22023
23072
  "repair_meta_campaign",
22024
23073
  "review_paused_campaign"
22025
23074
  ];
22026
- var DropshippingProjectNextAction = z85.discriminatedUnion("kind", NEXT_ACTION_KINDS.map((kind) => z85.object({
22027
- kind: z85.literal(kind),
22028
- href: z85.string().min(1)
23075
+ var DropshippingProjectNextAction = z89.discriminatedUnion("kind", NEXT_ACTION_KINDS.map((kind) => z89.object({
23076
+ kind: z89.literal(kind),
23077
+ href: z89.string().min(1)
22029
23078
  })));
22030
- var CostingInputSchema = z85.object({
22031
- country: z85.string().min(2).max(3),
22032
- supplierCost: z85.number().finite().positive(),
22033
- shippingCost: z85.number().finite().nonnegative(),
22034
- returnRate: z85.number().finite().min(0).lt(1),
22035
- cancellationRate: z85.number().finite().min(0).lt(1),
22036
- adsManagerCpa: z85.number().finite().nonnegative(),
22037
- desiredMargin: z85.number().finite().min(0).lt(1)
23079
+ var CostingInputSchema = z89.object({
23080
+ country: z89.string().min(2).max(3),
23081
+ supplierCost: z89.number().finite().positive(),
23082
+ shippingCost: z89.number().finite().nonnegative(),
23083
+ returnRate: z89.number().finite().min(0).lt(1),
23084
+ cancellationRate: z89.number().finite().min(0).lt(1),
23085
+ adsManagerCpa: z89.number().finite().nonnegative(),
23086
+ desiredMargin: z89.number().finite().min(0).lt(1)
22038
23087
  }).superRefine(({ returnRate, cancellationRate }, context) => {
22039
23088
  if (returnRate + cancellationRate >= 1) context.addIssue({
22040
- code: z85.ZodIssueCode.custom,
23089
+ code: z89.ZodIssueCode.custom,
22041
23090
  path: [
22042
23091
  "cancellationRate"
22043
23092
  ]
22044
23093
  });
22045
23094
  });
22046
- var FiniteCostingNumber = z85.number().finite();
22047
- var CostingResultSchema = z85.object({
22048
- country: z85.string().min(2).max(3),
23095
+ var FiniteCostingNumber = z89.number().finite();
23096
+ var CostingResultSchema = z89.object({
23097
+ country: z89.string().min(2).max(3),
22049
23098
  deliveryRate: FiniteCostingNumber,
22050
23099
  adjustedShippingCost: FiniteCostingNumber,
22051
23100
  adjustedCpa: FiniteCostingNumber,
@@ -22058,45 +23107,45 @@ var CostingResultSchema = z85.object({
22058
23107
  estimatedProfit: FiniteCostingNumber,
22059
23108
  actualMargin: FiniteCostingNumber,
22060
23109
  breakEvenAdsManagerCpa: FiniteCostingNumber,
22061
- wasPriceAutoAdjusted: z85.boolean(),
22062
- profitabilityStatus: z85.literal("healthy")
22063
- });
22064
- var RewardProgressSchema = z85.object({
22065
- campaignKey: z85.literal("dropshipping-launch-v1"),
22066
- totalCredits: z85.number().int().positive(),
22067
- earnedCredits: z85.number().int().nonnegative(),
22068
- milestones: z85.array(z85.object({
23110
+ wasPriceAutoAdjusted: z89.boolean(),
23111
+ profitabilityStatus: z89.literal("healthy")
23112
+ });
23113
+ var RewardProgressSchema = z89.object({
23114
+ campaignKey: z89.literal("dropshipping-launch-v1"),
23115
+ totalCredits: z89.number().int().positive(),
23116
+ earnedCredits: z89.number().int().nonnegative(),
23117
+ milestones: z89.array(z89.object({
22069
23118
  milestone: DropshippingRewardMilestone,
22070
- credits: z85.number().int().positive(),
22071
- available: z85.boolean().optional(),
22072
- earned: z85.boolean(),
23119
+ credits: z89.number().int().positive(),
23120
+ available: z89.boolean().optional(),
23121
+ earned: z89.boolean(),
22073
23122
  grantId: ProjectId.nullable(),
22074
- grantedAt: z85.string().datetime().nullable()
23123
+ grantedAt: z89.string().datetime().nullable()
22075
23124
  })).length(DropshippingRewardMilestone.options.length)
22076
23125
  });
22077
- var ProjectResponseSchema = z85.object({
23126
+ var ProjectResponseSchema = z89.object({
22078
23127
  id: ProjectId,
22079
23128
  taskId: ProjectId.nullable(),
22080
23129
  status: DropshippingProjectStatus,
22081
- originKey: z85.string().min(1),
23130
+ originKey: z89.string().min(1),
22082
23131
  researchRunId: ProjectId.nullable(),
22083
- candidateSourceId: z85.string().nullable(),
22084
- productName: z85.string().nullable(),
23132
+ candidateSourceId: z89.string().nullable(),
23133
+ productName: z89.string().nullable(),
22085
23134
  productUrl: HttpUrl3.nullable(),
22086
23135
  referenceImageUrl: HttpUrl3.nullable(),
22087
- targetCountry: z85.string().nullable(),
22088
- languageCode: z85.string().nullable(),
22089
- hasReferenceImage: z85.boolean(),
22090
- hasWinnerReferenceImage: z85.boolean(),
23136
+ targetCountry: z89.string().nullable(),
23137
+ languageCode: z89.string().nullable(),
23138
+ hasReferenceImage: z89.boolean(),
23139
+ hasWinnerReferenceImage: z89.boolean(),
22091
23140
  costingInput: CostingInputSchema.nullable(),
22092
23141
  costingResult: CostingResultSchema.nullable(),
22093
- costingVersion: z85.number().int().positive().nullable(),
22094
- version: z85.number().int().positive(),
23142
+ costingVersion: z89.number().int().positive().nullable(),
23143
+ version: z89.number().int().positive(),
22095
23144
  phase: DropshippingProjectPhase,
22096
23145
  nextAction: DropshippingProjectNextAction.nullable(),
22097
23146
  rewards: RewardProgressSchema,
22098
- createdAt: z85.string().datetime(),
22099
- updatedAt: z85.string().datetime()
23147
+ createdAt: z89.string().datetime(),
23148
+ updatedAt: z89.string().datetime()
22100
23149
  }).passthrough();
22101
23150
  var NEXT_ACTION_GUIDANCE = {
22102
23151
  cost_product: "Llama `cost_dropshipping_project` con el costo real del proveedor. Nunca lo inventes: p\xEDdeselo al usuario. Es el \xDANICO dato que debes pedirle: omite env\xEDo, devoluciones, cancelaciones, CPA y margen para que ProductMaker use los valores gu\xEDa del pa\xEDs, y luego dile al usuario cu\xE1les us\xF3 por si quiere corregirlos.",
@@ -22214,58 +23263,58 @@ function registerDropshippingProjectTools(server, api) {
22214
23263
  __name(registerDropshippingProjectTools, "registerDropshippingProjectTools");
22215
23264
 
22216
23265
  // src/tools/daily-control.ts
22217
- import { z as z86 } from "zod";
23266
+ import { z as z90 } from "zod";
22218
23267
  var getDailyControlGuidance = getProductMakerCapabilityModelGuidance("get_daily_control");
22219
23268
  var linkCampaignToProjectGuidance = getProductMakerCapabilityModelGuidance("link_campaign_to_project");
22220
23269
  var reportDailyReturnsGuidance = getProductMakerCapabilityModelGuidance("report_daily_returns");
22221
- var ProviderCampaignIdentifier = z86.string().min(1).max(64).regex(/^[A-Za-z0-9_-]+$/);
22222
- var DateOnly = z86.string().regex(/^\d{4}-\d{2}-\d{2}$/);
22223
- var DailyControlInput = z86.object({
23270
+ var ProviderCampaignIdentifier = z90.string().min(1).max(64).regex(/^[A-Za-z0-9_-]+$/);
23271
+ var DateOnly = z90.string().regex(/^\d{4}-\d{2}-\d{2}$/);
23272
+ var DailyControlInput = z90.object({
22224
23273
  date: DateOnly.optional().describe("D\xEDa final de la ventana (YYYY-MM-DD). Si se omite, la ventana termina AYER en UTC \u2014 nunca hoy, porque el d\xEDa en curso est\xE1 incompleto \u2014 y el servidor puede correrla un d\xEDa m\xE1s atr\xE1s si en la zona horaria de la tienda ese d\xEDa todav\xEDa no ha cerrado."),
22225
- days: z86.number().int().min(1).max(14).default(3).describe("Tama\xF1o de la ventana, 1 a 14 d\xEDas."),
22226
- projectId: z86.string().uuid().optional().describe("Opcional: limita el reporte a un solo proyecto en vez de todos los productos activos.")
23274
+ days: z90.number().int().min(1).max(14).default(3).describe("Tama\xF1o de la ventana, 1 a 14 d\xEDas."),
23275
+ projectId: z90.string().uuid().optional().describe("Opcional: limita el reporte a un solo proyecto en vez de todos los productos activos.")
22227
23276
  }).strict();
22228
- var LinkCampaignInput = z86.object({
22229
- projectId: z86.string().uuid(),
22230
- provider: z86.enum([
23277
+ var LinkCampaignInput = z90.object({
23278
+ projectId: z90.string().uuid(),
23279
+ provider: z90.enum([
22231
23280
  "meta",
22232
23281
  "tiktok"
22233
23282
  ]),
22234
23283
  accountId: ProviderCampaignIdentifier.describe("Cuenta publicitaria del proveedor. Para Meta el servidor la IGNORA y deriva la cuenta real de la campa\xF1a \u2014 se sigue pidiendo aqu\xED solo porque TikTok s\xED la necesita para resolver la campa\xF1a."),
22235
23284
  campaignId: ProviderCampaignIdentifier
22236
23285
  }).strict();
22237
- var ReportReturnsInput = z86.object({
22238
- projectId: z86.string().uuid(),
23286
+ var ReportReturnsInput = z90.object({
23287
+ projectId: z90.string().uuid(),
22239
23288
  date: DateOnly,
22240
- returnedOrders: z86.number().int().min(0).max(1e5),
22241
- note: z86.string().trim().max(280).optional()
23289
+ returnedOrders: z90.number().int().min(0).max(1e5),
23290
+ note: z90.string().trim().max(280).optional()
22242
23291
  }).strict();
22243
- var DailyControlStatusMirror = DailyControlStatusEnum.or(z86.string());
22244
- var DailyControlLightMirror = DailyControlLightEnum.or(z86.string());
22245
- var DailyControlSourceMirror = DailyControlSourceEnum.or(z86.string());
22246
- var DailyControlVerdictMirror = DailyControlVerdictEnum.or(z86.string());
22247
- var DailyControlDayMirror = z86.object({
22248
- date: z86.string(),
22249
- returnedOrders: z86.number(),
23292
+ var DailyControlStatusMirror = DailyControlStatusEnum.or(z90.string());
23293
+ var DailyControlLightMirror = DailyControlLightEnum.or(z90.string());
23294
+ var DailyControlSourceMirror = DailyControlSourceEnum.or(z90.string());
23295
+ var DailyControlVerdictMirror = DailyControlVerdictEnum.or(z90.string());
23296
+ var DailyControlDayMirror = z90.object({
23297
+ date: z90.string(),
23298
+ returnedOrders: z90.number(),
22250
23299
  returnedSource: DailyControlSourceMirror,
22251
23300
  cancelledSource: DailyControlSourceMirror,
22252
- hasEstimates: z86.boolean(),
23301
+ hasEstimates: z90.boolean(),
22253
23302
  light: DailyControlLightMirror
22254
23303
  }).passthrough();
22255
- var DailyControlProductMirror = z86.object({
22256
- projectId: z86.string().uuid(),
22257
- productName: z86.string().nullable(),
23304
+ var DailyControlProductMirror = z90.object({
23305
+ projectId: z90.string().uuid(),
23306
+ productName: z90.string().nullable(),
22258
23307
  status: DailyControlStatusMirror,
22259
- statusMessage: z86.string().nullable(),
22260
- days: z86.array(DailyControlDayMirror),
23308
+ statusMessage: z90.string().nullable(),
23309
+ days: z90.array(DailyControlDayMirror),
22261
23310
  verdict: DailyControlVerdictMirror.nullable(),
22262
- rationale: z86.string().nullable()
23311
+ rationale: z90.string().nullable()
22263
23312
  }).passthrough();
22264
- var DailyControlResponseMirror = z86.object({
22265
- since: z86.string(),
22266
- until: z86.string(),
22267
- products: z86.array(DailyControlProductMirror),
22268
- archivedProjectSpend: z86.array(z86.unknown())
23313
+ var DailyControlResponseMirror = z90.object({
23314
+ since: z90.string(),
23315
+ until: z90.string(),
23316
+ products: z90.array(DailyControlProductMirror),
23317
+ archivedProjectSpend: z90.array(z90.unknown())
22269
23318
  }).passthrough();
22270
23319
  var MCP_WARNINGS_KEY = "_mcpDailyControlWarnings";
22271
23320
  function withWarnings(response) {
@@ -22349,42 +23398,42 @@ function registerDailyControlTools(server, api) {
22349
23398
  __name(registerDailyControlTools, "registerDailyControlTools");
22350
23399
 
22351
23400
  // src/tools/product-control.ts
22352
- import { z as z87 } from "zod";
23401
+ import { z as z91 } from "zod";
22353
23402
  var getProductControlGuidance = getProductMakerCapabilityModelGuidance("get_product_control");
22354
23403
  var listImportableProductsGuidance = getProductMakerCapabilityModelGuidance("list_importable_products");
22355
23404
  var importProductControlsGuidance = getProductMakerCapabilityModelGuidance("import_product_controls");
22356
23405
  var setProductCostingGuidance = getProductMakerCapabilityModelGuidance("set_product_costing");
22357
23406
  var reportProductCountsGuidance = getProductMakerCapabilityModelGuidance("report_product_counts");
22358
- var DateOnly2 = z87.string().regex(/^\d{4}-\d{2}-\d{2}$/);
23407
+ var DateOnly2 = z91.string().regex(/^\d{4}-\d{2}-\d{2}$/);
22359
23408
  var IMPORT_BATCH_MAX2 = 50;
22360
- var ProductControlListInput = z87.object({
22361
- days: z87.number().int().min(1).max(14).default(3).describe("Tama\xF1o de la ventana, 1 a 14 d\xEDas."),
23409
+ var ProductControlListInput = z91.object({
23410
+ days: z91.number().int().min(1).max(14).default(3).describe("Tama\xF1o de la ventana, 1 a 14 d\xEDas."),
22362
23411
  date: DateOnly2.optional().describe("D\xEDa final de la ventana (YYYY-MM-DD). Si se omite, termina en el \xFAltimo d\xEDa cerrado \u2014 nunca hoy, que est\xE1 incompleto."),
22363
- shopDomain: z87.string().min(1).optional().describe("Tienda, si tiene m\xE1s de una conectada.")
23412
+ shopDomain: z91.string().min(1).optional().describe("Tienda, si tiene m\xE1s de una conectada.")
22364
23413
  }).strict();
22365
- var ImportableProductsInput = z87.object({
22366
- q: z87.string().trim().max(120).optional().describe("B\xFAsqueda por t\xEDtulo. Sin esto un cat\xE1logo grande es inrecorrible."),
22367
- after: z87.string().max(512).optional().describe("Cursor de la p\xE1gina anterior, tal como vino en la respuesta."),
22368
- shopDomain: z87.string().min(1).optional()
23414
+ var ImportableProductsInput = z91.object({
23415
+ q: z91.string().trim().max(120).optional().describe("B\xFAsqueda por t\xEDtulo. Sin esto un cat\xE1logo grande es inrecorrible."),
23416
+ after: z91.string().max(512).optional().describe("Cursor de la p\xE1gina anterior, tal como vino en la respuesta."),
23417
+ shopDomain: z91.string().min(1).optional()
22369
23418
  }).strict();
22370
- var ImportProductControlsInput = z87.object({
22371
- shopifyProductIds: z87.array(z87.string().min(1).max(64)).min(1).max(IMPORT_BATCH_MAX2).describe("Identificadores tal como vinieron de list_importable_products. NO los inventes ni los deduzcas del t\xEDtulo."),
23419
+ var ImportProductControlsInput = z91.object({
23420
+ shopifyProductIds: z91.array(z91.string().min(1).max(64)).min(1).max(IMPORT_BATCH_MAX2).describe("Identificadores tal como vinieron de list_importable_products. NO los inventes ni los deduzcas del t\xEDtulo."),
22372
23421
  // Obligatorio, como en el endpoint: con dos tiendas conectadas, importar
22373
23422
  // sin decir a cuál archivaría el producto bajo la tienda equivocada.
22374
- shopDomain: z87.string().min(1)
23423
+ shopDomain: z91.string().min(1)
22375
23424
  }).strict();
22376
- var SetProductCostingInput = z87.object({
22377
- shopDomain: z87.string().min(1),
22378
- shopifyProductId: z87.string().min(1).max(64),
22379
- targetCountry: z87.string().length(2).describe("Pa\xEDs de venta en ISO-2, que fija moneda y referencia de flete."),
22380
- supplierCost: z87.number().finite().positive().describe("Lo que le cuesta el producto al proveedor. Nunca cero."),
22381
- shippingCost: z87.number().finite().nonnegative(),
22382
- returnRate: z87.number().finite().min(0).lt(1).describe("Proporci\xF3n 0\u20131, no porcentaje: 0.15 es quince por ciento."),
22383
- cancellationRate: z87.number().finite().min(0).lt(1)
23425
+ var SetProductCostingInput = z91.object({
23426
+ shopDomain: z91.string().min(1),
23427
+ shopifyProductId: z91.string().min(1).max(64),
23428
+ targetCountry: z91.string().length(2).describe("Pa\xEDs de venta en ISO-2, que fija moneda y referencia de flete."),
23429
+ supplierCost: z91.number().finite().positive().describe("Lo que le cuesta el producto al proveedor. Nunca cero."),
23430
+ shippingCost: z91.number().finite().nonnegative(),
23431
+ returnRate: z91.number().finite().min(0).lt(1).describe("Proporci\xF3n 0\u20131, no porcentaje: 0.15 es quince por ciento."),
23432
+ cancellationRate: z91.number().finite().min(0).lt(1)
22384
23433
  }).strict();
22385
- var Count = z87.number().int().min(0).max(1e5).nullable().optional();
22386
- var ReportProductCountsInput = z87.object({
22387
- productControlId: z87.string().uuid().describe("El identificador que devuelve get_product_control."),
23434
+ var Count = z91.number().int().min(0).max(1e5).nullable().optional();
23435
+ var ReportProductCountsInput = z91.object({
23436
+ productControlId: z91.string().uuid().describe("El identificador que devuelve get_product_control."),
22388
23437
  date: DateOnly2,
22389
23438
  orders: Count.describe("Pedidos que entraron ese d\xEDa."),
22390
23439
  cancelledOrders: Count,